mirror of
https://github.com/librespot-org/librespot.git
synced 2024-12-18 17:11:53 +00:00
Add basic support for playlists
This commit is contained in:
parent
abd6ad6b25
commit
9faaaae6d2
3 changed files with 74 additions and 65 deletions
|
@ -1,10 +1,13 @@
|
|||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate env_logger;
|
||||
|
||||
extern crate librespot;
|
||||
extern crate tokio_core;
|
||||
//extern crate tokio_fs;
|
||||
extern crate tokio_io;
|
||||
extern crate futures;
|
||||
//extern crate futures_cpupool;
|
||||
|
||||
use std::env;
|
||||
use tokio_core::reactor::Core;
|
||||
|
@ -13,7 +16,39 @@ use librespot::core::authentication::Credentials;
|
|||
use librespot::core::config::SessionConfig;
|
||||
use librespot::core::session::Session;
|
||||
use librespot::core::spotify_id::SpotifyId;
|
||||
use librespot::metadata::{Metadata, Track, Playlist};
|
||||
use librespot::playback::config::PlayerConfig;
|
||||
use librespot::playback::config::Bitrate;
|
||||
use librespot::metadata::{FileFormat, Metadata, PlaylistMeta, Track, Album, Artist, Playlist};
|
||||
|
||||
|
||||
/*
|
||||
fn make_list_playlist(core: &mut Core, session: &Session, uri: &str) -> TrackList {
|
||||
let mut tracks = Vec::new();
|
||||
let mut fnames = Vec::new();
|
||||
|
||||
let plist_uri = SpotifyId::from_base62(&uri).unwrap();
|
||||
let plist = core.run(Playlist::get(&session, plist_uri)).unwrap();
|
||||
println!("album name: {}",plist.name);
|
||||
let plist_name = &plist.name;
|
||||
|
||||
|
||||
|
||||
for (i, track_id) in plist.tracks.iter().enumerate() {
|
||||
let plist_track = core.run(Track::get(&session, *track_id)).unwrap();
|
||||
//println!("album track: {} - {}",i+1, alb_track.name);
|
||||
let artist = core.run(Artist::get(&session, plist_track.artists[0])).unwrap();
|
||||
println!("track artist: {}",artist.name);
|
||||
tracks.push(plist_track.id);
|
||||
let filename = format!("{} - {}.ogg",&artist.name, alb_track.name);
|
||||
fnames.push(filename);
|
||||
}
|
||||
let ntr = plist.tracks.len();
|
||||
|
||||
let folder = format!("{}",plist_name);
|
||||
let mut tlist = TrackList::new(ntr, folder, tracks, fnames);
|
||||
tlist
|
||||
}
|
||||
*/
|
||||
|
||||
fn main() {
|
||||
env_logger::init();
|
||||
|
@ -21,16 +56,20 @@ fn main() {
|
|||
let handle = core.handle();
|
||||
|
||||
let session_config = SessionConfig::default();
|
||||
let mut player_config = PlayerConfig::default();
|
||||
player_config.bitrate = Bitrate::Bitrate320;
|
||||
|
||||
let args: Vec<_> = env::args().collect();
|
||||
if args.len() != 4 {
|
||||
println!("Usage: {} USERNAME PASSWORD PLAYLIST", args[0]);
|
||||
if args.len() != 5 {
|
||||
println!("Usage: {} USERNAME PASSWORD PLAYLIST PLISTUSER", args[0]);
|
||||
}
|
||||
let username = args[1].to_owned();
|
||||
let password = args[2].to_owned();
|
||||
let credentials = Credentials::with_password(username, password);
|
||||
|
||||
let uri_split = args[3].split(":");
|
||||
let plist_owner = args[4].to_string();
|
||||
|
||||
let mut uri_split = args[3].split(":");
|
||||
let uri_parts: Vec<&str> = uri_split.collect();
|
||||
println!("{}, {}, {}",uri_parts[0], uri_parts[1], uri_parts[2]);
|
||||
|
||||
|
@ -40,7 +79,7 @@ fn main() {
|
|||
.run(Session::connect(session_config, credentials, None, handle))
|
||||
.unwrap();
|
||||
|
||||
let plist = core.run(Playlist::get(&session, plist_uri)).unwrap();
|
||||
let plist = core.run(Playlist::get(&session, plist_uri, plist_owner, 0, 100)).unwrap();
|
||||
println!("{:?}",plist);
|
||||
for track_id in plist.tracks {
|
||||
let plist_track = core.run(Track::get(&session, track_id)).unwrap();
|
||||
|
|
|
@ -146,6 +146,29 @@ pub trait Metadata: Send + Sized + 'static {
|
|||
}
|
||||
}
|
||||
|
||||
pub trait PlaylistMeta: Send + Sized + 'static {
|
||||
type Message: protobuf::Message;
|
||||
|
||||
fn base_url() -> &'static str;
|
||||
fn parse(msg: &Self::Message, session: &Session) -> Self;
|
||||
|
||||
fn get(session: &Session, id: SpotifyId, user: String, start: i32, len: i32) -> Box<Future<Item = Self, Error = MercuryError>> {
|
||||
//let uri = format!("hm://playlist/{}?from={}&length={}",id.to_base62(), 0, 100);
|
||||
let uri = format!("hm://playlist/user/{}/playlist/{}?from={}&length={}", user, id.to_base62(), start, len);
|
||||
println!("request uri: {}", uri);
|
||||
let request = session.mercury().get(uri);
|
||||
println!("a");
|
||||
let session = session.clone();
|
||||
Box::new(request.and_then(move |response| {
|
||||
println!("{:?}", response);
|
||||
let data = response.payload.first().expect("Empty payload");
|
||||
let msg: Self::Message = protobuf::parse_from_bytes(data).unwrap();
|
||||
println!("{:?}", msg);
|
||||
Ok(Self::parse(&msg, &session))
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Track {
|
||||
pub id: SpotifyId,
|
||||
|
@ -192,7 +215,7 @@ pub struct Show {
|
|||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Playlist {
|
||||
pub user: String,
|
||||
//pub id: SpotifyId,
|
||||
pub length: i32,
|
||||
pub name: String,
|
||||
pub tracks: Vec<SpotifyId>,
|
||||
|
@ -295,13 +318,14 @@ impl Metadata for Album {
|
|||
}
|
||||
}
|
||||
|
||||
impl Metadata for Playlist {
|
||||
impl PlaylistMeta for Playlist {
|
||||
type Message = protocol::playlist4changes::SelectedListContent;
|
||||
|
||||
fn request_url(id: SpotifyId) -> String {
|
||||
format!("hm://playlist/v2/playlist/{}", id.to_base62())
|
||||
fn base_url() -> &'static str {
|
||||
"hm://playlist/'?from=' + from + '&length=' + length"
|
||||
}
|
||||
|
||||
|
||||
fn parse(msg: &Self::Message, _: &Session) -> Self {
|
||||
|
||||
let tracks = msg
|
||||
|
@ -321,7 +345,6 @@ impl Metadata for Playlist {
|
|||
name: msg.get_attributes().get_name().to_owned(),
|
||||
length: msg.get_length(),
|
||||
tracks: tracks,
|
||||
user: msg.get_owner_username().to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2759,7 +2759,6 @@ pub struct SelectedListContent {
|
|||
resolveAction: ::protobuf::RepeatedField<super::playlist4issues::ClientResolveAction>,
|
||||
issues: ::protobuf::RepeatedField<super::playlist4issues::ClientIssue>,
|
||||
nonces: ::std::vec::Vec<i32>,
|
||||
owner_username: ::protobuf::SingularField<::std::string::String>,
|
||||
// special fields
|
||||
pub unknown_fields: ::protobuf::UnknownFields,
|
||||
pub cached_size: ::protobuf::CachedSize,
|
||||
|
@ -3133,42 +3132,6 @@ impl SelectedListContent {
|
|||
pub fn take_nonces(&mut self) -> ::std::vec::Vec<i32> {
|
||||
::std::mem::replace(&mut self.nonces, ::std::vec::Vec::new())
|
||||
}
|
||||
|
||||
// optional string owner_username = 16;
|
||||
|
||||
|
||||
pub fn get_owner_username(&self) -> &str {
|
||||
match self.owner_username.as_ref() {
|
||||
Some(v) => &v,
|
||||
None => "",
|
||||
}
|
||||
}
|
||||
pub fn clear_owner_username(&mut self) {
|
||||
self.owner_username.clear();
|
||||
}
|
||||
|
||||
pub fn has_owner_username(&self) -> bool {
|
||||
self.owner_username.is_some()
|
||||
}
|
||||
|
||||
// Param is passed by value, moved
|
||||
pub fn set_owner_username(&mut self, v: ::std::string::String) {
|
||||
self.owner_username = ::protobuf::SingularField::some(v);
|
||||
}
|
||||
|
||||
// Mutable pointer to the field.
|
||||
// If field is not initialized, it is initialized with default value first.
|
||||
pub fn mut_owner_username(&mut self) -> &mut ::std::string::String {
|
||||
if self.owner_username.is_none() {
|
||||
self.owner_username.set_default();
|
||||
}
|
||||
self.owner_username.as_mut().unwrap()
|
||||
}
|
||||
|
||||
// Take field
|
||||
pub fn take_owner_username(&mut self) -> ::std::string::String {
|
||||
self.owner_username.take().unwrap_or_else(|| ::std::string::String::new())
|
||||
}
|
||||
}
|
||||
|
||||
impl ::protobuf::Message for SelectedListContent {
|
||||
|
@ -3266,9 +3229,6 @@ impl ::protobuf::Message for SelectedListContent {
|
|||
14 => {
|
||||
::protobuf::rt::read_repeated_int32_into(wire_type, is, &mut self.nonces)?;
|
||||
},
|
||||
16 => {
|
||||
::protobuf::rt::read_singular_string_into(wire_type, is, &mut self.owner_username)?;
|
||||
},
|
||||
_ => {
|
||||
::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
|
||||
},
|
||||
|
@ -3327,9 +3287,6 @@ impl ::protobuf::Message for SelectedListContent {
|
|||
for value in &self.nonces {
|
||||
my_size += ::protobuf::rt::value_size(14, *value, ::protobuf::wire_format::WireTypeVarint);
|
||||
};
|
||||
if let Some(ref v) = self.owner_username.as_ref() {
|
||||
my_size += ::protobuf::rt::string_size(16, &v);
|
||||
}
|
||||
my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
|
||||
self.cached_size.set(my_size);
|
||||
my_size
|
||||
|
@ -3389,9 +3346,6 @@ impl ::protobuf::Message for SelectedListContent {
|
|||
for v in &self.nonces {
|
||||
os.write_int32(14, *v)?;
|
||||
};
|
||||
if let Some(ref v) = self.owner_username.as_ref() {
|
||||
os.write_string(16, &v)?;
|
||||
}
|
||||
os.write_unknown_fields(self.get_unknown_fields())?;
|
||||
::std::result::Result::Ok(())
|
||||
}
|
||||
|
@ -3499,11 +3453,6 @@ impl ::protobuf::Message for SelectedListContent {
|
|||
|m: &SelectedListContent| { &m.nonces },
|
||||
|m: &mut SelectedListContent| { &mut m.nonces },
|
||||
));
|
||||
fields.push(::protobuf::reflect::accessor::make_singular_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
|
||||
"owner_username",
|
||||
|m: &SelectedListContent| { &m.owner_username },
|
||||
|m: &mut SelectedListContent| { &mut m.owner_username },
|
||||
));
|
||||
::protobuf::reflect::MessageDescriptor::new::<SelectedListContent>(
|
||||
"SelectedListContent",
|
||||
fields,
|
||||
|
@ -3539,7 +3488,6 @@ impl ::protobuf::Clear for SelectedListContent {
|
|||
self.resolveAction.clear();
|
||||
self.issues.clear();
|
||||
self.nonces.clear();
|
||||
self.owner_username.clear();
|
||||
self.unknown_fields.clear();
|
||||
}
|
||||
}
|
||||
|
@ -3586,7 +3534,7 @@ static file_descriptor_proto_data: &'static [u8] = b"\
|
|||
eltaB\0\x12\x20\n\x16wantResultingRevisions\x18\x03\x20\x01(\x08B\0\x12\
|
||||
\x18\n\x0ewantSyncResult\x18\x04\x20\x01(\x08B\0\x12\x19\n\x04dump\x18\
|
||||
\x05\x20\x01(\x0b2\t.ListDumpB\0\x12\x10\n\x06nonces\x18\x06\x20\x03(\
|
||||
\x05B\0:\0\"\xa1\x03\n\x13SelectedListContent\x12\x12\n\x08revision\x18\
|
||||
\x05B\0:\0\"\x87\x03\n\x13SelectedListContent\x12\x12\n\x08revision\x18\
|
||||
\x01\x20\x01(\x0cB\0\x12\x10\n\x06length\x18\x02\x20\x01(\x05B\0\x12%\n\
|
||||
\nattributes\x18\x03\x20\x01(\x0b2\x0f.ListAttributesB\0\x12!\n\x08check\
|
||||
sum\x18\x04\x20\x01(\x0b2\r.ListChecksumB\0\x12\x1e\n\x08contents\x18\
|
||||
|
@ -3596,8 +3544,7 @@ static file_descriptor_proto_data: &'static [u8] = b"\
|
|||
ipleHeads\x18\t\x20\x01(\x08B\0\x12\x12\n\x08upToDate\x18\n\x20\x01(\x08\
|
||||
B\0\x12-\n\rresolveAction\x18\x0c\x20\x03(\x0b2\x14.ClientResolveActionB\
|
||||
\0\x12\x1e\n\x06issues\x18\r\x20\x03(\x0b2\x0c.ClientIssueB\0\x12\x10\n\
|
||||
\x06nonces\x18\x0e\x20\x03(\x05B\0\x12\x18\n\x0eowner_username\x18\x10\
|
||||
\x20\x01(\tB\0:\0B\0b\x06proto2\
|
||||
\x06nonces\x18\x0e\x20\x03(\x05B\0:\0B\0b\x06proto2\
|
||||
";
|
||||
|
||||
static mut file_descriptor_proto_lazy: ::protobuf::lazy::Lazy<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::lazy::Lazy {
|
||||
|
|
Loading…
Reference in a new issue