Add initial Spirc support.

This commit is contained in:
Paul Lietar 2015-07-01 20:47:51 +02:00
parent addc1fce22
commit 7897070bb7
2 changed files with 47 additions and 12 deletions

View file

@ -1,10 +1,12 @@
#![crate_name = "librespot"] #![crate_name = "librespot"]
#![feature(plugin,zero_one,iter_arith,slice_position_elem,slice_bytes,bitset,mpsc_select,arc_weak,append)] #![feature(plugin,zero_one,iter_arith,slice_position_elem,slice_bytes,bitset,mpsc_select,arc_weak,append)]
#![allow(unused_imports,dead_code)]
#![plugin(protobuf_macros)] #![plugin(protobuf_macros)]
#[macro_use] extern crate lazy_static; #[macro_use] extern crate lazy_static;
extern crate byteorder; extern crate byteorder;
extern crate crypto; extern crate crypto;
extern crate gmp; extern crate gmp;
@ -35,19 +37,20 @@ use std::clone::Clone;
use std::fs::File; use std::fs::File;
use std::io::{Read, Write}; use std::io::{Read, Write};
use std::path::Path; use std::path::Path;
use std::sync::mpsc;
use metadata::{MetadataCache, AlbumRef, ArtistRef, TrackRef}; use metadata::{MetadataCache, AlbumRef, ArtistRef, TrackRef};
use session::{Config, Session}; use session::{Config, Session};
use util::SpotifyId; use util::SpotifyId;
use player::Player; use player::Player;
use mercury::{MercuryRequest, MercuryMethod};
use librespot_protocol as protocol;
fn main() { fn main() {
let mut args = std::env::args().skip(1); let mut args = std::env::args().skip(1);
let mut appkey_file = File::open(Path::new(&args.next().unwrap())).unwrap(); let mut appkey_file = File::open(Path::new(&args.next().unwrap())).unwrap();
let username = args.next().unwrap(); let username = args.next().unwrap();
let password = args.next().unwrap(); let password = args.next().unwrap();
let track_uri = args.next().unwrap();
let track_id = SpotifyId::from_base62(track_uri.split(':').nth(2).unwrap());
let mut appkey = Vec::new(); let mut appkey = Vec::new();
appkey_file.read_to_end(&mut appkey).unwrap(); appkey_file.read_to_end(&mut appkey).unwrap();
@ -63,8 +66,28 @@ fn main() {
let mut cache = MetadataCache::new(session.metadata.clone()); let mut cache = MetadataCache::new(session.metadata.clone());
let (tx, rx) = mpsc::channel();
print_track(&mut cache, track_id); session.mercury.send(MercuryRequest{
method: MercuryMethod::SUB,
uri: "hm://remote/user/lietar/v23".to_string(),
content_type: None,
callback: Some(tx)
}).unwrap();
for pkt in rx.iter() {
let frame : protocol::spirc::Frame =
protobuf::parse_from_bytes(pkt.payload.front().unwrap()).unwrap();
if frame.get_device_state().get_is_active() &&
frame.has_state() {
let index = frame.get_state().get_playing_track_index();
let ref track = frame.get_state().get_track()[index as usize];
println!("{}", frame.get_device_state().get_name());
print_track(&mut cache, SpotifyId::from_raw(track.get_gid()));
println!("");
}
}
loop { loop {
session.poll(); session.poll();

View file

@ -23,7 +23,7 @@ pub struct MercuryRequest {
pub method: MercuryMethod, pub method: MercuryMethod,
pub uri: String, pub uri: String,
pub content_type: Option<String>, pub content_type: Option<String>,
pub callback: MercuryCallback pub callback: Option<MercuryCallback>
} }
#[derive(Debug)] #[derive(Debug)]
@ -32,17 +32,18 @@ pub struct MercuryResponse {
pub payload: LinkedList<Vec<u8>> pub payload: LinkedList<Vec<u8>>
} }
pub type MercuryCallback = Option<mpsc::Sender<MercuryResponse>>; pub type MercuryCallback = mpsc::Sender<MercuryResponse>;
pub struct MercuryPending { pub struct MercuryPending {
parts: LinkedList<Vec<u8>>, parts: LinkedList<Vec<u8>>,
partial: Option<Vec<u8>>, partial: Option<Vec<u8>>,
callback: MercuryCallback, callback: Option<MercuryCallback>
} }
pub struct MercuryManager { pub struct MercuryManager {
next_seq: u32, next_seq: u32,
pending: HashMap<Vec<u8>, MercuryPending>, pending: HashMap<Vec<u8>, MercuryPending>,
subscriptions: HashMap<String, MercuryCallback>,
requests: mpsc::Receiver<MercuryRequest>, requests: mpsc::Receiver<MercuryRequest>,
packet_tx: mpsc::Sender<Packet>, packet_tx: mpsc::Sender<Packet>,
@ -69,6 +70,7 @@ impl MercuryManager {
(MercuryManager { (MercuryManager {
next_seq: 0, next_seq: 0,
pending: HashMap::new(), pending: HashMap::new(),
subscriptions: HashMap::new(),
requests: req_rx, requests: req_rx,
packet_rx: pkt_rx, packet_rx: pkt_rx,
@ -93,11 +95,15 @@ impl MercuryManager {
data: data data: data
}).unwrap(); }).unwrap();
self.pending.insert(seq.to_vec(), MercuryPending{ if req.method != MercuryMethod::SUB {
parts: LinkedList::new(), self.pending.insert(seq.to_vec(), MercuryPending{
partial: None, parts: LinkedList::new(),
callback: req.callback, partial: None,
}); callback: req.callback,
});
} else if let Some(cb) = req.callback {
self.subscriptions.insert(req.uri, cb);
}
} }
fn parse_part(mut s: &mut Read) -> Vec<u8> { fn parse_part(mut s: &mut Read) -> Vec<u8> {
@ -117,7 +123,13 @@ impl MercuryManager {
let header : protocol::mercury::Header = let header : protocol::mercury::Header =
protobuf::parse_from_bytes(&header_data).unwrap(); protobuf::parse_from_bytes(&header_data).unwrap();
if let Some(ref ch) = pending.callback { let callback = if cmd == 0xb5 {
self.subscriptions.get(header.get_uri())
} else {
pending.callback.as_ref()
};
if let Some(ref ch) = callback {
ch.send(MercuryResponse{ ch.send(MercuryResponse{
uri: header.get_uri().to_string(), uri: header.get_uri().to_string(),
payload: pending.parts payload: pending.parts