mirror of
https://github.com/librespot-org/librespot.git
synced 2024-12-18 17:11:53 +00:00
Migrate and expand playlist protos
This commit is contained in:
parent
a73e05837e
commit
f037a42908
7 changed files with 134 additions and 338 deletions
|
@ -201,6 +201,21 @@ pub struct Show {
|
|||
pub covers: Vec<FileId>,
|
||||
}
|
||||
|
||||
#[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<TranscodedPicture>,
|
||||
pub abuse_reporting: bool,
|
||||
pub taken_down: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Playlist {
|
||||
pub revision: Vec<u8>,
|
||||
|
@ -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::<Vec<_>>();
|
||||
|
||||
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::<Vec<_>>();
|
||||
|
||||
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<Self, MetadataError> {
|
||||
let response = Self::request_for_user(session, username, playlist_id).await?;
|
||||
let msg = <Self as Metadata>::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<Self, MetadataError> {
|
||||
let response = Self::request_for_user(session, username, playlist_id).await?;
|
||||
let msg = <Self as Metadata>::Message::parse_from_bytes(&response)?;
|
||||
Ok(Self::parse(&msg, session))
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
async fn get_root_for_user(session: &Session, username: String) -> Result<Self, MetadataError> {
|
||||
let response = Self::request_root_for_user(session, username).await?;
|
||||
let msg = <Self as Metadata>::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::<Vec<_>>();
|
||||
|
||||
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::<Vec<_>>();
|
||||
|
||||
Show {
|
||||
Self {
|
||||
id: SpotifyId::from_raw(msg.get_gid()).unwrap(),
|
||||
name: msg.get_name().to_owned(),
|
||||
publisher: msg.get_publisher().to_owned(),
|
||||
|
|
|
@ -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"),
|
||||
];
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in a new issue