From f037a42908cb4fa65f91cc5934aa8fe17591fa93 Mon Sep 17 00:00:00 2001 From: Roderick van Domburg Date: Sat, 27 Nov 2021 11:59:22 +0100 Subject: [PATCH] Migrate and expand playlist protos --- metadata/src/lib.rs | 143 ++++++++++++++++++++++++-- protocol/build.rs | 7 +- protocol/proto/playlist4changes.proto | 87 ---------------- protocol/proto/playlist4content.proto | 37 ------- protocol/proto/playlist4issues.proto | 43 -------- protocol/proto/playlist4meta.proto | 52 ---------- protocol/proto/playlist4ops.proto | 103 ------------------- 7 files changed, 134 insertions(+), 338 deletions(-) delete mode 100644 protocol/proto/playlist4changes.proto delete mode 100644 protocol/proto/playlist4content.proto delete mode 100644 protocol/proto/playlist4issues.proto delete mode 100644 protocol/proto/playlist4meta.proto delete mode 100644 protocol/proto/playlist4ops.proto diff --git a/metadata/src/lib.rs b/metadata/src/lib.rs index 039bea83..05ab028d 100644 --- a/metadata/src/lib.rs +++ b/metadata/src/lib.rs @@ -201,6 +201,21 @@ pub struct Show { pub covers: Vec, } +#[derive(Debug, Clone)] +pub struct TranscodedPicture { + pub target_name: String, + pub uri: String, +} + +#[derive(Debug, Clone)] +pub struct PlaylistAnnotation { + pub description: String, + pub picture: String, + pub transcoded_pictures: Vec, + pub abuse_reporting: bool, + pub taken_down: bool, +} + #[derive(Debug, Clone)] pub struct Playlist { pub revision: Vec, @@ -250,7 +265,7 @@ impl Metadata for Track { }) .collect(); - Track { + Self { id: SpotifyId::from_raw(msg.get_gid()).unwrap(), name: msg.get_name().to_owned(), duration: msg.get_duration(), @@ -307,7 +322,7 @@ impl Metadata for Album { }) .collect::>(); - Album { + Self { id: SpotifyId::from_raw(msg.get_gid()).unwrap(), name: msg.get_name().to_owned(), artists, @@ -318,12 +333,73 @@ impl Metadata for Album { } #[async_trait] -impl Metadata for Playlist { - type Message = protocol::playlist4changes::SelectedListContent; +impl Metadata for PlaylistAnnotation { + type Message = protocol::playlist_annotate3::PlaylistAnnotation; + + async fn request(session: &Session, playlist_id: SpotifyId) -> MetadataResult { + let current_user = session.username(); + Self::request_for_user(session, current_user, playlist_id).await + } + + fn parse(msg: &Self::Message, _: &Session) -> Self { + let transcoded_pictures = msg + .get_transcoded_picture() + .iter() + .map(|picture| TranscodedPicture { + target_name: picture.get_target_name().to_string(), + uri: picture.get_uri().to_string(), + }) + .collect::>(); + + let taken_down = !matches!( + msg.get_abuse_report_state(), + protocol::playlist_annotate3::AbuseReportState::OK + ); + + Self { + description: msg.get_description().to_string(), + picture: msg.get_picture().to_string(), + transcoded_pictures, + abuse_reporting: msg.get_is_abuse_reporting_enabled(), + taken_down, + } + } +} + +impl PlaylistAnnotation { + async fn request_for_user( + session: &Session, + username: String, + playlist_id: SpotifyId, + ) -> MetadataResult { + let uri = format!( + "hm://playlist-annotate/v1/annotation/user/{}/playlist/{}", + username, + playlist_id.to_base62() + ); + let response = session.mercury().get(uri).await?; + match response.payload.first() { + Some(data) => Ok(data.to_vec().into()), + None => Err(MetadataError::Empty), + } + } + + #[allow(dead_code)] + async fn get_for_user( + session: &Session, + username: String, + playlist_id: SpotifyId, + ) -> Result { + let response = Self::request_for_user(session, username, playlist_id).await?; + let msg = ::Message::parse_from_bytes(&response)?; + Ok(Self::parse(&msg, session)) + } +} + +#[async_trait] +impl Metadata for Playlist { + type Message = protocol::playlist4_external::SelectedListContent; - // TODO: - // * Add PlaylistAnnotate3 annotations. - // * Find spclient endpoint and upgrade to that. async fn request(session: &Session, playlist_id: SpotifyId) -> MetadataResult { let uri = format!("hm://playlist/v2/playlist/{}", playlist_id.to_base62()); let response = session.mercury().get(uri).await?; @@ -353,7 +429,7 @@ impl Metadata for Playlist { ); } - Playlist { + Self { revision: msg.get_revision().to_vec(), name: msg.get_attributes().get_name().to_owned(), tracks, @@ -362,6 +438,51 @@ impl Metadata for Playlist { } } +impl Playlist { + async fn request_for_user( + session: &Session, + username: String, + playlist_id: SpotifyId, + ) -> MetadataResult { + let uri = format!( + "hm://playlist/user/{}/playlist/{}", + username, + playlist_id.to_base62() + ); + let response = session.mercury().get(uri).await?; + match response.payload.first() { + Some(data) => Ok(data.to_vec().into()), + None => Err(MetadataError::Empty), + } + } + + async fn request_root_for_user(session: &Session, username: String) -> MetadataResult { + let uri = format!("hm://playlist/user/{}/rootlist", username); + let response = session.mercury().get(uri).await?; + match response.payload.first() { + Some(data) => Ok(data.to_vec().into()), + None => Err(MetadataError::Empty), + } + } + #[allow(dead_code)] + async fn get_for_user( + session: &Session, + username: String, + playlist_id: SpotifyId, + ) -> Result { + let response = Self::request_for_user(session, username, playlist_id).await?; + let msg = ::Message::parse_from_bytes(&response)?; + Ok(Self::parse(&msg, session)) + } + + #[allow(dead_code)] + async fn get_root_for_user(session: &Session, username: String) -> Result { + let response = Self::request_root_for_user(session, username).await?; + let msg = ::Message::parse_from_bytes(&response)?; + Ok(Self::parse(&msg, session)) + } +} + #[async_trait] impl Metadata for Artist { type Message = protocol::metadata::Artist; @@ -391,7 +512,7 @@ impl Metadata for Artist { None => Vec::new(), }; - Artist { + Self { id: SpotifyId::from_raw(msg.get_gid()).unwrap(), name: msg.get_name().to_owned(), top_tracks, @@ -438,7 +559,7 @@ impl Metadata for Episode { }) .collect::>(); - Episode { + Self { id: SpotifyId::from_raw(msg.get_gid()).unwrap(), name: msg.get_name().to_owned(), external_url: msg.get_external_url().to_owned(), @@ -485,7 +606,7 @@ impl Metadata for Show { }) .collect::>(); - Show { + Self { id: SpotifyId::from_raw(msg.get_gid()).unwrap(), name: msg.get_name().to_owned(), publisher: msg.get_publisher().to_owned(), diff --git a/protocol/build.rs b/protocol/build.rs index 37be7000..560bbfea 100644 --- a/protocol/build.rs +++ b/protocol/build.rs @@ -23,17 +23,14 @@ fn compile() { proto_dir.join("extension_kind.proto"), proto_dir.join("metadata.proto"), proto_dir.join("player.proto"), + proto_dir.join("playlist_annotate3.proto"), + proto_dir.join("playlist4_external.proto"), // TODO: remove these legacy protobufs when we are on the new API completely proto_dir.join("authentication.proto"), proto_dir.join("canvaz.proto"), proto_dir.join("canvaz-meta.proto"), proto_dir.join("keyexchange.proto"), proto_dir.join("mercury.proto"), - proto_dir.join("playlist4changes.proto"), - proto_dir.join("playlist4content.proto"), - proto_dir.join("playlist4issues.proto"), - proto_dir.join("playlist4meta.proto"), - proto_dir.join("playlist4ops.proto"), proto_dir.join("pubsub.proto"), proto_dir.join("spirc.proto"), ]; diff --git a/protocol/proto/playlist4changes.proto b/protocol/proto/playlist4changes.proto deleted file mode 100644 index 6b424b71..00000000 --- a/protocol/proto/playlist4changes.proto +++ /dev/null @@ -1,87 +0,0 @@ -syntax = "proto2"; - -import "playlist4ops.proto"; -import "playlist4meta.proto"; -import "playlist4content.proto"; -import "playlist4issues.proto"; - -message ChangeInfo { - optional string user = 0x1; - optional int32 timestamp = 0x2; - optional bool admin = 0x3; - optional bool undo = 0x4; - optional bool redo = 0x5; - optional bool merge = 0x6; - optional bool compressed = 0x7; - optional bool migration = 0x8; -} - -message Delta { - optional bytes base_version = 0x1; - repeated Op ops = 0x2; - optional ChangeInfo info = 0x4; -} - -message Merge { - optional bytes base_version = 0x1; - optional bytes merge_version = 0x2; - optional ChangeInfo info = 0x4; -} - -message ChangeSet { - optional Kind kind = 0x1; - enum Kind { - KIND_UNKNOWN = 0x0; - DELTA = 0x2; - MERGE = 0x3; - } - optional Delta delta = 0x2; - optional Merge merge = 0x3; -} - -message RevisionTaggedChangeSet { - optional bytes revision = 0x1; - optional ChangeSet change_set = 0x2; -} - -message Diff { - optional bytes from_revision = 0x1; - repeated Op ops = 0x2; - optional bytes to_revision = 0x3; -} - -message ListDump { - optional bytes latestRevision = 0x1; - optional int32 length = 0x2; - optional ListAttributes attributes = 0x3; - optional ListChecksum checksum = 0x4; - optional ListItems contents = 0x5; - repeated Delta pendingDeltas = 0x7; -} - -message ListChanges { - optional bytes baseRevision = 0x1; - repeated Delta deltas = 0x2; - optional bool wantResultingRevisions = 0x3; - optional bool wantSyncResult = 0x4; - optional ListDump dump = 0x5; - repeated int32 nonces = 0x6; -} - -message SelectedListContent { - optional bytes revision = 0x1; - optional int32 length = 0x2; - optional ListAttributes attributes = 0x3; - optional ListChecksum checksum = 0x4; - optional ListItems contents = 0x5; - optional Diff diff = 0x6; - optional Diff syncResult = 0x7; - repeated bytes resultingRevisions = 0x8; - optional bool multipleHeads = 0x9; - optional bool upToDate = 0xa; - repeated ClientResolveAction resolveAction = 0xc; - repeated ClientIssue issues = 0xd; - repeated int32 nonces = 0xe; - optional string owner_username =0x10; -} - diff --git a/protocol/proto/playlist4content.proto b/protocol/proto/playlist4content.proto deleted file mode 100644 index 50d197fa..00000000 --- a/protocol/proto/playlist4content.proto +++ /dev/null @@ -1,37 +0,0 @@ -syntax = "proto2"; - -import "playlist4meta.proto"; -import "playlist4issues.proto"; - -message Item { - optional string uri = 0x1; - optional ItemAttributes attributes = 0x2; -} - -message ListItems { - optional int32 pos = 0x1; - optional bool truncated = 0x2; - repeated Item items = 0x3; -} - -message ContentRange { - optional int32 pos = 0x1; - optional int32 length = 0x2; -} - -message ListContentSelection { - optional bool wantRevision = 0x1; - optional bool wantLength = 0x2; - optional bool wantAttributes = 0x3; - optional bool wantChecksum = 0x4; - optional bool wantContent = 0x5; - optional ContentRange contentRange = 0x6; - optional bool wantDiff = 0x7; - optional bytes baseRevision = 0x8; - optional bytes hintRevision = 0x9; - optional bool wantNothingIfUpToDate = 0xa; - optional bool wantResolveAction = 0xc; - repeated ClientIssue issues = 0xd; - repeated ClientResolveAction resolveAction = 0xe; -} - diff --git a/protocol/proto/playlist4issues.proto b/protocol/proto/playlist4issues.proto deleted file mode 100644 index 3808d532..00000000 --- a/protocol/proto/playlist4issues.proto +++ /dev/null @@ -1,43 +0,0 @@ -syntax = "proto2"; - -message ClientIssue { - optional Level level = 0x1; - enum Level { - LEVEL_UNKNOWN = 0x0; - LEVEL_DEBUG = 0x1; - LEVEL_INFO = 0x2; - LEVEL_NOTICE = 0x3; - LEVEL_WARNING = 0x4; - LEVEL_ERROR = 0x5; - } - optional Code code = 0x2; - enum Code { - CODE_UNKNOWN = 0x0; - CODE_INDEX_OUT_OF_BOUNDS = 0x1; - CODE_VERSION_MISMATCH = 0x2; - CODE_CACHED_CHANGE = 0x3; - CODE_OFFLINE_CHANGE = 0x4; - CODE_CONCURRENT_CHANGE = 0x5; - } - optional int32 repeatCount = 0x3; -} - -message ClientResolveAction { - optional Code code = 0x1; - enum Code { - CODE_UNKNOWN = 0x0; - CODE_NO_ACTION = 0x1; - CODE_RETRY = 0x2; - CODE_RELOAD = 0x3; - CODE_DISCARD_LOCAL_CHANGES = 0x4; - CODE_SEND_DUMP = 0x5; - CODE_DISPLAY_ERROR_MESSAGE = 0x6; - } - optional Initiator initiator = 0x2; - enum Initiator { - INITIATOR_UNKNOWN = 0x0; - INITIATOR_SERVER = 0x1; - INITIATOR_CLIENT = 0x2; - } -} - diff --git a/protocol/proto/playlist4meta.proto b/protocol/proto/playlist4meta.proto deleted file mode 100644 index 4c22a9f0..00000000 --- a/protocol/proto/playlist4meta.proto +++ /dev/null @@ -1,52 +0,0 @@ -syntax = "proto2"; - -message ListChecksum { - optional int32 version = 0x1; - optional bytes sha1 = 0x4; -} - -message DownloadFormat { - optional Codec codec = 0x1; - enum Codec { - CODEC_UNKNOWN = 0x0; - OGG_VORBIS = 0x1; - FLAC = 0x2; - MPEG_1_LAYER_3 = 0x3; - } -} - -message ListAttributes { - optional string name = 0x1; - optional string description = 0x2; - optional bytes picture = 0x3; - optional bool collaborative = 0x4; - optional string pl3_version = 0x5; - optional bool deleted_by_owner = 0x6; - optional bool restricted_collaborative = 0x7; - optional int64 deprecated_client_id = 0x8; - optional bool public_starred = 0x9; - optional string client_id = 0xa; -} - -message ItemAttributes { - optional string added_by = 0x1; - optional int64 timestamp = 0x2; - optional string message = 0x3; - optional bool seen = 0x4; - optional int64 download_count = 0x5; - optional DownloadFormat download_format = 0x6; - optional string sevendigital_id = 0x7; - optional int64 sevendigital_left = 0x8; - optional int64 seen_at = 0x9; - optional bool public = 0xa; -} - -message StringAttribute { - optional string key = 0x1; - optional string value = 0x2; -} - -message StringAttributes { - repeated StringAttribute attribute = 0x1; -} - diff --git a/protocol/proto/playlist4ops.proto b/protocol/proto/playlist4ops.proto deleted file mode 100644 index dbbfcaa9..00000000 --- a/protocol/proto/playlist4ops.proto +++ /dev/null @@ -1,103 +0,0 @@ -syntax = "proto2"; - -import "playlist4meta.proto"; -import "playlist4content.proto"; - -message Add { - optional int32 fromIndex = 0x1; - repeated Item items = 0x2; - optional ListChecksum list_checksum = 0x3; - optional bool addLast = 0x4; - optional bool addFirst = 0x5; -} - -message Rem { - optional int32 fromIndex = 0x1; - optional int32 length = 0x2; - repeated Item items = 0x3; - optional ListChecksum list_checksum = 0x4; - optional ListChecksum items_checksum = 0x5; - optional ListChecksum uris_checksum = 0x6; - optional bool itemsAsKey = 0x7; -} - -message Mov { - optional int32 fromIndex = 0x1; - optional int32 length = 0x2; - optional int32 toIndex = 0x3; - optional ListChecksum list_checksum = 0x4; - optional ListChecksum items_checksum = 0x5; - optional ListChecksum uris_checksum = 0x6; -} - -message ItemAttributesPartialState { - optional ItemAttributes values = 0x1; - repeated ItemAttributeKind no_value = 0x2; - - enum ItemAttributeKind { - ITEM_UNKNOWN = 0x0; - ITEM_ADDED_BY = 0x1; - ITEM_TIMESTAMP = 0x2; - ITEM_MESSAGE = 0x3; - ITEM_SEEN = 0x4; - ITEM_DOWNLOAD_COUNT = 0x5; - ITEM_DOWNLOAD_FORMAT = 0x6; - ITEM_SEVENDIGITAL_ID = 0x7; - ITEM_SEVENDIGITAL_LEFT = 0x8; - ITEM_SEEN_AT = 0x9; - ITEM_PUBLIC = 0xa; - } -} - -message ListAttributesPartialState { - optional ListAttributes values = 0x1; - repeated ListAttributeKind no_value = 0x2; - - enum ListAttributeKind { - LIST_UNKNOWN = 0x0; - LIST_NAME = 0x1; - LIST_DESCRIPTION = 0x2; - LIST_PICTURE = 0x3; - LIST_COLLABORATIVE = 0x4; - LIST_PL3_VERSION = 0x5; - LIST_DELETED_BY_OWNER = 0x6; - LIST_RESTRICTED_COLLABORATIVE = 0x7; - } -} - -message UpdateItemAttributes { - optional int32 index = 0x1; - optional ItemAttributesPartialState new_attributes = 0x2; - optional ItemAttributesPartialState old_attributes = 0x3; - optional ListChecksum list_checksum = 0x4; - optional ListChecksum old_attributes_checksum = 0x5; -} - -message UpdateListAttributes { - optional ListAttributesPartialState new_attributes = 0x1; - optional ListAttributesPartialState old_attributes = 0x2; - optional ListChecksum list_checksum = 0x3; - optional ListChecksum old_attributes_checksum = 0x4; -} - -message Op { - optional Kind kind = 0x1; - enum Kind { - KIND_UNKNOWN = 0x0; - ADD = 0x2; - REM = 0x3; - MOV = 0x4; - UPDATE_ITEM_ATTRIBUTES = 0x5; - UPDATE_LIST_ATTRIBUTES = 0x6; - } - optional Add add = 0x2; - optional Rem rem = 0x3; - optional Mov mov = 0x4; - optional UpdateItemAttributes update_item_attributes = 0x5; - optional UpdateListAttributes update_list_attributes = 0x6; -} - -message OpList { - repeated Op ops = 0x1; -} -