mirror of
https://github.com/librespot-org/librespot.git
synced 2024-12-18 17:11:53 +00:00
Merge branch 'master' into error-source-fix
This commit is contained in:
commit
1ccf00cfbb
29 changed files with 1549 additions and 875 deletions
1046
Cargo.lock
generated
1046
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
12
Cargo.toml
12
Cargo.toml
|
@ -43,35 +43,31 @@ num-bigint = "0.1.35"
|
||||||
protobuf = "1.1"
|
protobuf = "1.1"
|
||||||
rand = "0.6"
|
rand = "0.6"
|
||||||
rpassword = "0.3.0"
|
rpassword = "0.3.0"
|
||||||
rust-crypto = "0.2.36"
|
|
||||||
serde = "0.9.6"
|
|
||||||
serde_derive = "0.9.6"
|
|
||||||
serde_json = "0.9.5"
|
|
||||||
tokio-core = "0.1.2"
|
tokio-core = "0.1.2"
|
||||||
tokio-io = "0.1"
|
tokio-io = "0.1"
|
||||||
tokio-process = "0.2.2"
|
tokio-process = "0.2.2"
|
||||||
tokio-signal = "0.1.2"
|
tokio-signal = "0.1.2"
|
||||||
url = "1.7.0"
|
url = "1.7.0"
|
||||||
|
sha-1 = "0.8.0"
|
||||||
|
hex = "0.3.2"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
rand = "0.6"
|
rand = "0.6"
|
||||||
vergen = "0.1.0"
|
vergen = "0.1.0"
|
||||||
|
|
||||||
[replace]
|
|
||||||
"rust-crypto:0.2.36" = { git = "https://github.com/awmath/rust-crypto.git", branch = "avx2" }
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
alsa-backend = ["librespot-playback/alsa-backend"]
|
alsa-backend = ["librespot-playback/alsa-backend"]
|
||||||
portaudio-backend = ["librespot-playback/portaudio-backend"]
|
portaudio-backend = ["librespot-playback/portaudio-backend"]
|
||||||
pulseaudio-backend = ["librespot-playback/pulseaudio-backend"]
|
pulseaudio-backend = ["librespot-playback/pulseaudio-backend"]
|
||||||
jackaudio-backend = ["librespot-playback/jackaudio-backend"]
|
jackaudio-backend = ["librespot-playback/jackaudio-backend"]
|
||||||
|
rodio-backend = ["librespot-playback/rodio-backend"]
|
||||||
|
|
||||||
with-tremor = ["librespot-audio/with-tremor"]
|
with-tremor = ["librespot-audio/with-tremor"]
|
||||||
with-vorbis = ["librespot-audio/with-vorbis"]
|
with-vorbis = ["librespot-audio/with-vorbis"]
|
||||||
|
|
||||||
with-dns-sd = ["librespot-connect/with-dns-sd"]
|
with-dns-sd = ["librespot-connect/with-dns-sd"]
|
||||||
|
|
||||||
default = ["librespot-playback/portaudio-backend"]
|
default = ["librespot-playback/rodio-backend"]
|
||||||
|
|
||||||
[package.metadata.deb]
|
[package.metadata.deb]
|
||||||
maintainer = "librespot-org"
|
maintainer = "librespot-org"
|
||||||
|
|
|
@ -14,8 +14,8 @@ lewton = "0.9.3"
|
||||||
log = "0.3.5"
|
log = "0.3.5"
|
||||||
num-bigint = "0.1.35"
|
num-bigint = "0.1.35"
|
||||||
num-traits = "0.1.36"
|
num-traits = "0.1.36"
|
||||||
rust-crypto = "0.2.36"
|
|
||||||
tempfile = "2.1"
|
tempfile = "2.1"
|
||||||
|
aes-ctr = "0.3.0"
|
||||||
|
|
||||||
tremor = { git = "https://github.com/plietar/rust-tremor", optional = true }
|
tremor = { git = "https://github.com/plietar/rust-tremor", optional = true }
|
||||||
vorbis = { version ="0.1.0", optional = true }
|
vorbis = { version ="0.1.0", optional = true }
|
||||||
|
|
|
@ -1,39 +1,38 @@
|
||||||
use crypto::aes;
|
|
||||||
use crypto::symmetriccipher::SynchronousStreamCipher;
|
|
||||||
use num_bigint::BigUint;
|
|
||||||
use num_traits::FromPrimitive;
|
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::ops::Add;
|
|
||||||
|
use aes_ctr::Aes128Ctr;
|
||||||
|
use aes_ctr::stream_cipher::{
|
||||||
|
NewStreamCipher, SyncStreamCipher, SyncStreamCipherSeek
|
||||||
|
};
|
||||||
|
use aes_ctr::stream_cipher::generic_array::GenericArray;
|
||||||
|
|
||||||
use core::audio_key::AudioKey;
|
use core::audio_key::AudioKey;
|
||||||
|
|
||||||
const AUDIO_AESIV: &'static [u8] = &[
|
const AUDIO_AESIV: [u8; 16] = [
|
||||||
0x72, 0xe0, 0x67, 0xfb, 0xdd, 0xcb, 0xcf, 0x77, 0xeb, 0xe8, 0xbc, 0x64, 0x3f, 0x63, 0x0d, 0x93,
|
0x72, 0xe0, 0x67, 0xfb, 0xdd, 0xcb, 0xcf, 0x77,
|
||||||
|
0xeb, 0xe8, 0xbc, 0x64, 0x3f, 0x63, 0x0d, 0x93,
|
||||||
];
|
];
|
||||||
|
|
||||||
pub struct AudioDecrypt<T: io::Read> {
|
pub struct AudioDecrypt<T: io::Read> {
|
||||||
cipher: Box<SynchronousStreamCipher + 'static>,
|
cipher: Aes128Ctr,
|
||||||
key: AudioKey,
|
|
||||||
reader: T,
|
reader: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: io::Read> AudioDecrypt<T> {
|
impl<T: io::Read> AudioDecrypt<T> {
|
||||||
pub fn new(key: AudioKey, reader: T) -> AudioDecrypt<T> {
|
pub fn new(key: AudioKey, reader: T) -> AudioDecrypt<T> {
|
||||||
let cipher = aes::ctr(aes::KeySize::KeySize128, &key.0, AUDIO_AESIV);
|
let cipher = Aes128Ctr::new(
|
||||||
AudioDecrypt {
|
&GenericArray::from_slice(&key.0),
|
||||||
cipher: cipher,
|
&GenericArray::from_slice(&AUDIO_AESIV),
|
||||||
key: key,
|
);
|
||||||
reader: reader,
|
AudioDecrypt { cipher, reader }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: io::Read> io::Read for AudioDecrypt<T> {
|
impl<T: io::Read> io::Read for AudioDecrypt<T> {
|
||||||
fn read(&mut self, output: &mut [u8]) -> io::Result<usize> {
|
fn read(&mut self, output: &mut [u8]) -> io::Result<usize> {
|
||||||
let mut buffer = vec![0u8; output.len()];
|
let len = try!(self.reader.read(output));
|
||||||
let len = try!(self.reader.read(&mut buffer));
|
|
||||||
|
|
||||||
self.cipher.process(&buffer[..len], &mut output[..len]);
|
self.cipher.apply_keystream(&mut output[..len]);
|
||||||
|
|
||||||
Ok(len)
|
Ok(len)
|
||||||
}
|
}
|
||||||
|
@ -42,17 +41,9 @@ impl<T: io::Read> io::Read for AudioDecrypt<T> {
|
||||||
impl<T: io::Read + io::Seek> io::Seek for AudioDecrypt<T> {
|
impl<T: io::Read + io::Seek> io::Seek for AudioDecrypt<T> {
|
||||||
fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
|
fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
|
||||||
let newpos = try!(self.reader.seek(pos));
|
let newpos = try!(self.reader.seek(pos));
|
||||||
let skip = newpos % 16;
|
|
||||||
|
|
||||||
let iv = BigUint::from_bytes_be(AUDIO_AESIV)
|
self.cipher.seek(newpos);
|
||||||
.add(BigUint::from_u64(newpos / 16).unwrap())
|
|
||||||
.to_bytes_be();
|
|
||||||
self.cipher = aes::ctr(aes::KeySize::KeySize128, &self.key.0, &iv);
|
|
||||||
|
|
||||||
let buf = vec![0u8; skip as usize];
|
Ok(newpos)
|
||||||
let mut buf2 = vec![0u8; skip as usize];
|
|
||||||
self.cipher.process(&buf, &mut buf2);
|
|
||||||
|
|
||||||
Ok(newpos as u64)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,10 +5,10 @@ extern crate log;
|
||||||
|
|
||||||
extern crate bit_set;
|
extern crate bit_set;
|
||||||
extern crate byteorder;
|
extern crate byteorder;
|
||||||
extern crate crypto;
|
|
||||||
extern crate num_bigint;
|
extern crate num_bigint;
|
||||||
extern crate num_traits;
|
extern crate num_traits;
|
||||||
extern crate tempfile;
|
extern crate tempfile;
|
||||||
|
extern crate aes_ctr;
|
||||||
|
|
||||||
extern crate librespot_core as core;
|
extern crate librespot_core as core;
|
||||||
|
|
||||||
|
|
|
@ -18,12 +18,15 @@ log = "0.3.5"
|
||||||
num-bigint = "0.1.35"
|
num-bigint = "0.1.35"
|
||||||
protobuf = "2.0.5"
|
protobuf = "2.0.5"
|
||||||
rand = "0.6"
|
rand = "0.6"
|
||||||
rust-crypto = "0.2.36"
|
serde = "1.0"
|
||||||
serde = "0.9.6"
|
serde_derive = "1.0"
|
||||||
serde_derive = "0.9.6"
|
serde_json = "1.0"
|
||||||
serde_json = "0.9.5"
|
|
||||||
tokio-core = "0.1.2"
|
tokio-core = "0.1.2"
|
||||||
url = "1.3"
|
url = "1.3"
|
||||||
|
sha-1 = "0.8.0"
|
||||||
|
hmac = "0.7.0"
|
||||||
|
aes-ctr = "0.3.0"
|
||||||
|
block-modes = "0.2.0"
|
||||||
|
|
||||||
dns-sd = { version = "0.1.3", optional = true }
|
dns-sd = { version = "0.1.3", optional = true }
|
||||||
mdns = { git = "https://github.com/plietar/rust-mdns", optional = true }
|
mdns = { git = "https://github.com/plietar/rust-mdns", optional = true }
|
||||||
|
|
86
connect/src/context.rs
Normal file
86
connect/src/context.rs
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
use core::spotify_id::SpotifyId;
|
||||||
|
use protocol::spirc::TrackRef;
|
||||||
|
|
||||||
|
use serde;
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
pub struct StationContext {
|
||||||
|
pub uri: Option<String>,
|
||||||
|
pub next_page_url: String,
|
||||||
|
#[serde(deserialize_with = "deserialize_protobuf_TrackRef")]
|
||||||
|
pub tracks: Vec<TrackRef>,
|
||||||
|
// Not required for core functionality
|
||||||
|
// pub seeds: Vec<String>,
|
||||||
|
// #[serde(rename = "imageUri")]
|
||||||
|
// pub image_uri: String,
|
||||||
|
// pub subtitle: Option<String>,
|
||||||
|
// pub subtitles: Vec<String>,
|
||||||
|
// #[serde(rename = "subtitleUri")]
|
||||||
|
// pub subtitle_uri: Option<String>,
|
||||||
|
// pub title: String,
|
||||||
|
// #[serde(rename = "titleUri")]
|
||||||
|
// pub title_uri: String,
|
||||||
|
// pub related_artists: Vec<ArtistContext>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
pub struct PageContext {
|
||||||
|
pub uri: String,
|
||||||
|
pub next_page_url: String,
|
||||||
|
#[serde(deserialize_with = "deserialize_protobuf_TrackRef")]
|
||||||
|
pub tracks: Vec<TrackRef>,
|
||||||
|
// Not required for core functionality
|
||||||
|
// pub url: String,
|
||||||
|
// // pub restrictions:
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
pub struct TrackContext {
|
||||||
|
#[serde(rename = "original_gid")]
|
||||||
|
pub gid: String,
|
||||||
|
pub uri: String,
|
||||||
|
pub uid: String,
|
||||||
|
// Not required for core functionality
|
||||||
|
// pub album_uri: String,
|
||||||
|
// pub artist_uri: String,
|
||||||
|
// pub metadata: MetadataContext,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct ArtistContext {
|
||||||
|
artist_name: String,
|
||||||
|
artist_uri: String,
|
||||||
|
image_uri: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
pub struct MetadataContext {
|
||||||
|
album_title: String,
|
||||||
|
artist_name: String,
|
||||||
|
artist_uri: String,
|
||||||
|
image_url: String,
|
||||||
|
title: String,
|
||||||
|
uid: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn deserialize_protobuf_TrackRef<'d, D>(de: D) -> Result<Vec<TrackRef>, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'d>,
|
||||||
|
{
|
||||||
|
let v: Vec<TrackContext> = try!(serde::Deserialize::deserialize(de));
|
||||||
|
let track_vec = v
|
||||||
|
.iter()
|
||||||
|
.map(|v| {
|
||||||
|
let mut t = TrackRef::new();
|
||||||
|
// This has got to be the most round about way of doing this.
|
||||||
|
t.set_gid(SpotifyId::from_base62(&v.gid).unwrap().to_raw().to_vec());
|
||||||
|
t.set_uri(v.uri.to_owned());
|
||||||
|
|
||||||
|
t
|
||||||
|
})
|
||||||
|
.collect::<Vec<TrackRef>>();
|
||||||
|
|
||||||
|
Ok(track_vec)
|
||||||
|
}
|
|
@ -1,7 +1,9 @@
|
||||||
use base64;
|
use base64;
|
||||||
use crypto;
|
use sha1::{Sha1, Digest};
|
||||||
use crypto::digest::Digest;
|
use hmac::{Hmac, Mac};
|
||||||
use crypto::mac::Mac;
|
use aes_ctr::Aes128Ctr;
|
||||||
|
use aes_ctr::stream_cipher::{NewStreamCipher, SyncStreamCipher};
|
||||||
|
use aes_ctr::stream_cipher::generic_array::GenericArray;
|
||||||
use futures::sync::mpsc;
|
use futures::sync::mpsc;
|
||||||
use futures::{Future, Poll, Stream};
|
use futures::{Future, Poll, Stream};
|
||||||
use hyper::server::{Http, Request, Response, Service};
|
use hyper::server::{Http, Request, Response, Service};
|
||||||
|
@ -26,6 +28,8 @@ use core::config::ConnectConfig;
|
||||||
use core::diffie_hellman::{DH_GENERATOR, DH_PRIME};
|
use core::diffie_hellman::{DH_GENERATOR, DH_PRIME};
|
||||||
use core::util;
|
use core::util;
|
||||||
|
|
||||||
|
type HmacSha1 = Hmac<Sha1>;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct Discovery(Arc<DiscoveryInner>);
|
struct Discovery(Arc<DiscoveryInner>);
|
||||||
struct DiscoveryInner {
|
struct DiscoveryInner {
|
||||||
|
@ -106,39 +110,45 @@ impl Discovery {
|
||||||
let encrypted = &encrypted_blob[16..encrypted_blob.len() - 20];
|
let encrypted = &encrypted_blob[16..encrypted_blob.len() - 20];
|
||||||
let cksum = &encrypted_blob[encrypted_blob.len() - 20..encrypted_blob.len()];
|
let cksum = &encrypted_blob[encrypted_blob.len() - 20..encrypted_blob.len()];
|
||||||
|
|
||||||
let base_key = {
|
let base_key = Sha1::digest(&shared_key.to_bytes_be());
|
||||||
let mut data = [0u8; 20];
|
let base_key = &base_key[..16];
|
||||||
let mut h = crypto::sha1::Sha1::new();
|
|
||||||
h.input(&shared_key.to_bytes_be());
|
|
||||||
h.result(&mut data);
|
|
||||||
data[..16].to_owned()
|
|
||||||
};
|
|
||||||
|
|
||||||
let checksum_key = {
|
let checksum_key = {
|
||||||
let mut h = crypto::hmac::Hmac::new(crypto::sha1::Sha1::new(), &base_key);
|
let mut h = HmacSha1::new_varkey(base_key)
|
||||||
|
.expect("HMAC can take key of any size");
|
||||||
h.input(b"checksum");
|
h.input(b"checksum");
|
||||||
h.result().code().to_owned()
|
h.result().code()
|
||||||
};
|
};
|
||||||
|
|
||||||
let encryption_key = {
|
let encryption_key = {
|
||||||
let mut h = crypto::hmac::Hmac::new(crypto::sha1::Sha1::new(), &base_key);
|
let mut h = HmacSha1::new_varkey(&base_key)
|
||||||
|
.expect("HMAC can take key of any size");
|
||||||
h.input(b"encryption");
|
h.input(b"encryption");
|
||||||
h.result().code().to_owned()
|
h.result().code()
|
||||||
};
|
};
|
||||||
|
|
||||||
let mac = {
|
let mut h = HmacSha1::new_varkey(&checksum_key)
|
||||||
let mut h = crypto::hmac::Hmac::new(crypto::sha1::Sha1::new(), &checksum_key);
|
.expect("HMAC can take key of any size");
|
||||||
h.input(encrypted);
|
h.input(encrypted);
|
||||||
h.result().code().to_owned()
|
if let Err(_) = h.verify(cksum) {
|
||||||
};
|
warn!("Login error for user {:?}: MAC mismatch", username);
|
||||||
|
let result = json!({
|
||||||
|
"status": 102,
|
||||||
|
"spotifyError": 1,
|
||||||
|
"statusString": "ERROR-MAC"
|
||||||
|
});
|
||||||
|
|
||||||
assert_eq!(&mac[..], cksum);
|
let body = result.to_string();
|
||||||
|
return ::futures::finished(Response::new().with_body(body))
|
||||||
|
}
|
||||||
|
|
||||||
let decrypted = {
|
let decrypted = {
|
||||||
let mut data = vec![0u8; encrypted.len()];
|
let mut data = encrypted.to_vec();
|
||||||
let mut cipher =
|
let mut cipher = Aes128Ctr::new(
|
||||||
crypto::aes::ctr(crypto::aes::KeySize::KeySize128, &encryption_key[0..16], iv);
|
&GenericArray::from_slice(&encryption_key[0..16]),
|
||||||
cipher.process(encrypted, &mut data);
|
&GenericArray::from_slice(iv),
|
||||||
|
);
|
||||||
|
cipher.apply_keystream(&mut data);
|
||||||
String::from_utf8(data).unwrap()
|
String::from_utf8(data).unwrap()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -221,7 +231,6 @@ pub fn discovery(
|
||||||
|
|
||||||
let serve = {
|
let serve = {
|
||||||
let http = Http::new();
|
let http = Http::new();
|
||||||
debug!("Zeroconf server listening on 0.0.0.0:{}", port);
|
|
||||||
http.serve_addr_handle(
|
http.serve_addr_handle(
|
||||||
&format!("0.0.0.0:{}", port).parse().unwrap(),
|
&format!("0.0.0.0:{}", port).parse().unwrap(),
|
||||||
&handle,
|
&handle,
|
||||||
|
@ -230,6 +239,7 @@ pub fn discovery(
|
||||||
};
|
};
|
||||||
|
|
||||||
let s_port = serve.incoming_ref().local_addr().port();
|
let s_port = serve.incoming_ref().local_addr().port();
|
||||||
|
debug!("Zeroconf server listening on 0.0.0.0:{}", s_port);
|
||||||
|
|
||||||
let server_future = {
|
let server_future = {
|
||||||
let handle = handle.clone();
|
let handle = handle.clone();
|
||||||
|
|
|
@ -2,9 +2,11 @@
|
||||||
extern crate log;
|
extern crate log;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate serde_json;
|
extern crate serde_json;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate serde_derive;
|
||||||
|
extern crate serde;
|
||||||
|
|
||||||
extern crate base64;
|
extern crate base64;
|
||||||
extern crate crypto;
|
|
||||||
extern crate futures;
|
extern crate futures;
|
||||||
extern crate hyper;
|
extern crate hyper;
|
||||||
extern crate num_bigint;
|
extern crate num_bigint;
|
||||||
|
@ -13,6 +15,11 @@ extern crate rand;
|
||||||
extern crate tokio_core;
|
extern crate tokio_core;
|
||||||
extern crate url;
|
extern crate url;
|
||||||
|
|
||||||
|
extern crate sha1;
|
||||||
|
extern crate hmac;
|
||||||
|
extern crate aes_ctr;
|
||||||
|
extern crate block_modes;
|
||||||
|
|
||||||
#[cfg(feature = "with-dns-sd")]
|
#[cfg(feature = "with-dns-sd")]
|
||||||
extern crate dns_sd;
|
extern crate dns_sd;
|
||||||
|
|
||||||
|
@ -23,5 +30,6 @@ extern crate librespot_core as core;
|
||||||
extern crate librespot_playback as playback;
|
extern crate librespot_playback as playback;
|
||||||
extern crate librespot_protocol as protocol;
|
extern crate librespot_protocol as protocol;
|
||||||
|
|
||||||
|
pub mod context;
|
||||||
pub mod discovery;
|
pub mod discovery;
|
||||||
pub mod spirc;
|
pub mod spirc;
|
||||||
|
|
|
@ -16,7 +16,9 @@ use protocol::spirc::{DeviceState, Frame, MessageType, PlayStatus, State};
|
||||||
|
|
||||||
use playback::mixer::Mixer;
|
use playback::mixer::Mixer;
|
||||||
use playback::player::Player;
|
use playback::player::Player;
|
||||||
|
use serde_json;
|
||||||
|
|
||||||
|
use context::StationContext;
|
||||||
use rand;
|
use rand;
|
||||||
use rand::seq::SliceRandom;
|
use rand::seq::SliceRandom;
|
||||||
use std;
|
use std;
|
||||||
|
@ -40,6 +42,8 @@ pub struct SpircTask {
|
||||||
|
|
||||||
shutdown: bool,
|
shutdown: bool,
|
||||||
session: Session,
|
session: Session,
|
||||||
|
context_fut: Box<Future<Item = serde_json::Value, Error = MercuryError>>,
|
||||||
|
context: Option<StationContext>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum SpircCommand {
|
pub enum SpircCommand {
|
||||||
|
@ -53,6 +57,9 @@ pub enum SpircCommand {
|
||||||
Shutdown,
|
Shutdown,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const CONTEXT_TRACKS_HISTORY: usize = 10;
|
||||||
|
const CONTEXT_FETCH_THRESHOLD: u32 = 5;
|
||||||
|
|
||||||
pub struct Spirc {
|
pub struct Spirc {
|
||||||
commands: mpsc::UnboundedSender<SpircCommand>,
|
commands: mpsc::UnboundedSender<SpircCommand>,
|
||||||
}
|
}
|
||||||
|
@ -139,6 +146,15 @@ fn initial_device_state(config: ConnectConfig) -> DeviceState {
|
||||||
};
|
};
|
||||||
msg
|
msg
|
||||||
};
|
};
|
||||||
|
{
|
||||||
|
let msg = repeated.push_default();
|
||||||
|
msg.set_typ(protocol::spirc::CapabilityType::kSupportsPlaylistV2);
|
||||||
|
{
|
||||||
|
let repeated = msg.mut_intValue();
|
||||||
|
repeated.push(64)
|
||||||
|
};
|
||||||
|
msg
|
||||||
|
};
|
||||||
{
|
{
|
||||||
let msg = repeated.push_default();
|
let msg = repeated.push_default();
|
||||||
msg.set_typ(protocol::spirc::CapabilityType::kSupportedContexts);
|
msg.set_typ(protocol::spirc::CapabilityType::kSupportedContexts);
|
||||||
|
@ -176,7 +192,7 @@ fn calc_logarithmic_volume(volume: u16) -> u16 {
|
||||||
// Volume conversion taken from https://www.dr-lex.be/info-stuff/volumecontrols.html#ideal2
|
// Volume conversion taken from https://www.dr-lex.be/info-stuff/volumecontrols.html#ideal2
|
||||||
// Convert the given volume [0..0xffff] to a dB gain
|
// Convert the given volume [0..0xffff] to a dB gain
|
||||||
// We assume a dB range of 60dB.
|
// We assume a dB range of 60dB.
|
||||||
// Use the equatation: a * exp(b * x)
|
// Use the equation: a * exp(b * x)
|
||||||
// in which a = IDEAL_FACTOR, b = 1/1000
|
// in which a = IDEAL_FACTOR, b = 1/1000
|
||||||
const IDEAL_FACTOR: f64 = 6.908;
|
const IDEAL_FACTOR: f64 = 6.908;
|
||||||
let normalized_volume = volume as f64 / std::u16::MAX as f64; // To get a value between 0 and 1
|
let normalized_volume = volume as f64 / std::u16::MAX as f64; // To get a value between 0 and 1
|
||||||
|
@ -259,6 +275,9 @@ impl Spirc {
|
||||||
|
|
||||||
shutdown: false,
|
shutdown: false,
|
||||||
session: session.clone(),
|
session: session.clone(),
|
||||||
|
|
||||||
|
context_fut: Box::new(future::empty()),
|
||||||
|
context: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
task.set_volume(volume);
|
task.set_volume(volume);
|
||||||
|
@ -335,6 +354,39 @@ impl Future for SpircTask {
|
||||||
Ok(Async::NotReady) => (),
|
Ok(Async::NotReady) => (),
|
||||||
Err(oneshot::Canceled) => self.end_of_track = Box::new(future::empty()),
|
Err(oneshot::Canceled) => self.end_of_track = Box::new(future::empty()),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
match self.context_fut.poll() {
|
||||||
|
Ok(Async::Ready(value)) => {
|
||||||
|
let r_context = serde_json::from_value::<StationContext>(value.clone());
|
||||||
|
self.context = match r_context {
|
||||||
|
Ok(context) => {
|
||||||
|
info!(
|
||||||
|
"Resolved {:?} tracks from <{:?}>",
|
||||||
|
context.tracks.len(),
|
||||||
|
self.state.get_context_uri(),
|
||||||
|
);
|
||||||
|
Some(context)
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("Unable to parse JSONContext {:?}\n{:?}", e, value);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// It needn't be so verbose - can be as simple as
|
||||||
|
// if let Some(ref context) = r_context {
|
||||||
|
// info!("Got {:?} tracks from <{}>", context.tracks.len(), context.uri);
|
||||||
|
// }
|
||||||
|
// self.context = r_context;
|
||||||
|
|
||||||
|
progress = true;
|
||||||
|
self.context_fut = Box::new(future::empty());
|
||||||
|
}
|
||||||
|
Ok(Async::NotReady) => (),
|
||||||
|
Err(err) => {
|
||||||
|
self.context_fut = Box::new(future::empty());
|
||||||
|
error!("ContextError: {:?}", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let poll_sender = self.sender.poll_complete().unwrap();
|
let poll_sender = self.sender.poll_complete().unwrap();
|
||||||
|
@ -455,6 +507,7 @@ impl SpircTask {
|
||||||
let play = frame.get_state().get_status() == PlayStatus::kPlayStatusPlay;
|
let play = frame.get_state().get_status() == PlayStatus::kPlayStatusPlay;
|
||||||
self.load_track(play);
|
self.load_track(play);
|
||||||
} else {
|
} else {
|
||||||
|
info!("No more tracks left in queue");
|
||||||
self.state.set_status(PlayStatus::kPlayStatusStop);
|
self.state.set_status(PlayStatus::kPlayStatusStop);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -600,6 +653,21 @@ impl SpircTask {
|
||||||
fn handle_next(&mut self) {
|
fn handle_next(&mut self) {
|
||||||
let mut new_index = self.consume_queued_track() as u32;
|
let mut new_index = self.consume_queued_track() as u32;
|
||||||
let mut continue_playing = true;
|
let mut continue_playing = true;
|
||||||
|
debug!(
|
||||||
|
"At track {:?} of {:?} <{:?}> update [{}]",
|
||||||
|
new_index,
|
||||||
|
self.state.get_track().len(),
|
||||||
|
self.state.get_context_uri(),
|
||||||
|
self.state.get_track().len() as u32 - new_index < CONTEXT_FETCH_THRESHOLD
|
||||||
|
);
|
||||||
|
let context_uri = self.state.get_context_uri().to_owned();
|
||||||
|
if (context_uri.starts_with("spotify:station:") || context_uri.starts_with("spotify:dailymix:"))
|
||||||
|
&& ((self.state.get_track().len() as u32) - new_index) < CONTEXT_FETCH_THRESHOLD
|
||||||
|
{
|
||||||
|
self.context_fut = self.resolve_station(&context_uri);
|
||||||
|
self.update_tracks_from_context();
|
||||||
|
}
|
||||||
|
|
||||||
if new_index >= self.state.get_track().len() as u32 {
|
if new_index >= self.state.get_track().len() as u32 {
|
||||||
new_index = 0; // Loop around back to start
|
new_index = 0; // Loop around back to start
|
||||||
continue_playing = self.state.get_repeat();
|
continue_playing = self.state.get_repeat();
|
||||||
|
@ -680,10 +748,59 @@ impl SpircTask {
|
||||||
self.state.get_position_ms() + diff as u32
|
self.state.get_position_ms() + diff as u32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn resolve_station(&self, uri: &str) -> Box<Future<Item = serde_json::Value, Error = MercuryError>> {
|
||||||
|
let radio_uri = format!("hm://radio-apollo/v3/stations/{}", uri);
|
||||||
|
|
||||||
|
self.resolve_uri(&radio_uri)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_uri(&self, uri: &str) -> Box<Future<Item = serde_json::Value, Error = MercuryError>> {
|
||||||
|
let request = self.session.mercury().get(uri);
|
||||||
|
|
||||||
|
Box::new(request.and_then(move |response| {
|
||||||
|
let data = response.payload.first().expect("Empty payload on context uri");
|
||||||
|
let response: serde_json::Value = serde_json::from_slice(&data).unwrap();
|
||||||
|
|
||||||
|
Ok(response)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_tracks_from_context(&mut self) {
|
||||||
|
if let Some(ref context) = self.context {
|
||||||
|
self.context_fut = self.resolve_uri(&context.next_page_url);
|
||||||
|
|
||||||
|
let new_tracks = &context.tracks;
|
||||||
|
debug!("Adding {:?} tracks from context to playlist", new_tracks.len());
|
||||||
|
let current_index = self.state.get_playing_track_index();
|
||||||
|
let mut new_index = 0;
|
||||||
|
{
|
||||||
|
let mut tracks = self.state.mut_track();
|
||||||
|
// Does this need to be optimised - we don't need to actually traverse the len of tracks
|
||||||
|
let tracks_len = tracks.len();
|
||||||
|
if tracks_len > CONTEXT_TRACKS_HISTORY {
|
||||||
|
tracks.rotate_right(tracks_len - CONTEXT_TRACKS_HISTORY);
|
||||||
|
tracks.truncate(CONTEXT_TRACKS_HISTORY);
|
||||||
|
}
|
||||||
|
// tracks.extend_from_slice(&mut new_tracks); // method doesn't exist for protobuf::RepeatedField
|
||||||
|
for t in new_tracks {
|
||||||
|
tracks.push(t.to_owned());
|
||||||
|
}
|
||||||
|
if current_index > CONTEXT_TRACKS_HISTORY as u32 {
|
||||||
|
new_index = current_index - CONTEXT_TRACKS_HISTORY as u32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.state.set_playing_track_index(new_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn update_tracks(&mut self, frame: &protocol::spirc::Frame) {
|
fn update_tracks(&mut self, frame: &protocol::spirc::Frame) {
|
||||||
let index = frame.get_state().get_playing_track_index();
|
let index = frame.get_state().get_playing_track_index();
|
||||||
let tracks = frame.get_state().get_track();
|
|
||||||
let context_uri = frame.get_state().get_context_uri().to_owned();
|
let context_uri = frame.get_state().get_context_uri().to_owned();
|
||||||
|
let tracks = frame.get_state().get_track();
|
||||||
|
debug!("Frame has {:?} tracks", tracks.len());
|
||||||
|
if context_uri.starts_with("spotify:station:") || context_uri.starts_with("spotify:dailymix:") {
|
||||||
|
self.context_fut = self.resolve_station(&context_uri);
|
||||||
|
}
|
||||||
|
|
||||||
self.state.set_playing_track_index(index);
|
self.state.set_playing_track_index(index);
|
||||||
self.state.set_track(tracks.into_iter().cloned().collect());
|
self.state.set_track(tracks.into_iter().cloned().collect());
|
||||||
|
@ -693,13 +810,25 @@ impl SpircTask {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_track(&mut self, play: bool) {
|
fn load_track(&mut self, play: bool) {
|
||||||
let index = self.state.get_playing_track_index();
|
|
||||||
let track = {
|
let track = {
|
||||||
let gid = self.state.get_track()[index as usize].get_gid();
|
let mut index = self.state.get_playing_track_index();
|
||||||
SpotifyId::from_raw(gid).unwrap()
|
// Check for malformed gid
|
||||||
|
let tracks_len = self.state.get_track().len() as u32;
|
||||||
|
let mut track_ref = &self.state.get_track()[index as usize];
|
||||||
|
while track_ref.get_gid().len() != 16 {
|
||||||
|
warn!(
|
||||||
|
"Skipping track {:?} at position [{}] of {}",
|
||||||
|
track_ref.get_uri(),
|
||||||
|
index,
|
||||||
|
tracks_len
|
||||||
|
);
|
||||||
|
index = if index + 1 < tracks_len { index + 1 } else { 0 };
|
||||||
|
track_ref = &self.state.get_track()[index as usize];
|
||||||
|
}
|
||||||
|
SpotifyId::from_raw(track_ref.get_gid()).unwrap()
|
||||||
};
|
};
|
||||||
let position = self.state.get_position_ms();
|
|
||||||
|
|
||||||
|
let position = self.state.get_position_ms();
|
||||||
let end_of_track = self.player.load(track, play, position);
|
let end_of_track = self.player.load(track, play, position);
|
||||||
|
|
||||||
if play {
|
if play {
|
||||||
|
|
|
@ -25,16 +25,19 @@ num-traits = "0.1.36"
|
||||||
protobuf = "2.0.5"
|
protobuf = "2.0.5"
|
||||||
rand = "0.6"
|
rand = "0.6"
|
||||||
rpassword = "0.3.0"
|
rpassword = "0.3.0"
|
||||||
rust-crypto = "0.2.36"
|
serde = "1.0"
|
||||||
serde = "0.9.6"
|
serde_derive = "1.0"
|
||||||
serde_derive = "0.9.6"
|
serde_json = "1.0"
|
||||||
serde_json = "0.9.5"
|
|
||||||
shannon = "0.2.0"
|
shannon = "0.2.0"
|
||||||
tokio-codec = "0.1.1"
|
tokio-codec = "0.1.1"
|
||||||
tokio-core = "0.1.2"
|
tokio-core = "0.1.2"
|
||||||
tokio-io = "0.1"
|
tokio-io = "0.1"
|
||||||
url = "1.7.0"
|
url = "1.7.0"
|
||||||
uuid = { version = "0.4", features = ["v4"] }
|
uuid = { version = "0.4", features = ["v4"] }
|
||||||
|
sha-1 = "0.8.0"
|
||||||
|
hmac = "0.7.0"
|
||||||
|
pbkdf2 = "0.3.0"
|
||||||
|
aes = "0.3.0"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
rand = "0.6"
|
rand = "0.6"
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
use base64;
|
use base64;
|
||||||
use byteorder::{BigEndian, ByteOrder};
|
use byteorder::{BigEndian, ByteOrder};
|
||||||
use crypto;
|
use aes::Aes192;
|
||||||
use crypto::aes;
|
use hmac::Hmac;
|
||||||
use crypto::digest::Digest;
|
use sha1::{Sha1, Digest};
|
||||||
use crypto::hmac::Hmac;
|
use pbkdf2::pbkdf2;
|
||||||
use crypto::pbkdf2::pbkdf2;
|
|
||||||
use crypto::sha1::Sha1;
|
|
||||||
use protobuf::ProtobufEnum;
|
use protobuf::ProtobufEnum;
|
||||||
use serde;
|
use serde;
|
||||||
use serde_json;
|
use serde_json;
|
||||||
|
@ -63,42 +61,34 @@ impl Credentials {
|
||||||
Ok(data)
|
Ok(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
let encrypted_blob = base64::decode(encrypted_blob).unwrap();
|
let secret = Sha1::digest(device_id.as_bytes());
|
||||||
|
|
||||||
let secret = {
|
|
||||||
let mut data = [0u8; 20];
|
|
||||||
let mut h = crypto::sha1::Sha1::new();
|
|
||||||
h.input(device_id.as_bytes());
|
|
||||||
h.result(&mut data);
|
|
||||||
data
|
|
||||||
};
|
|
||||||
|
|
||||||
let key = {
|
let key = {
|
||||||
let mut data = [0u8; 24];
|
let mut key = [0u8; 24];
|
||||||
let mut mac = Hmac::new(Sha1::new(), &secret);
|
pbkdf2::<Hmac<Sha1>>(&secret, username.as_bytes(), 0x100, &mut key[0..20]);
|
||||||
pbkdf2(&mut mac, username.as_bytes(), 0x100, &mut data[0..20]);
|
|
||||||
|
|
||||||
let mut hash = Sha1::new();
|
let hash = &Sha1::digest(&key[..20]);
|
||||||
hash.input(&data[0..20]);
|
key[..20].copy_from_slice(hash);
|
||||||
hash.result(&mut data[0..20]);
|
BigEndian::write_u32(&mut key[20..], 20);
|
||||||
BigEndian::write_u32(&mut data[20..], 20);
|
key
|
||||||
data
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// decrypt data using ECB mode without padding
|
||||||
let blob = {
|
let blob = {
|
||||||
// Anyone know what this block mode is ?
|
use aes::block_cipher_trait::BlockCipher;
|
||||||
let mut data = vec![0u8; encrypted_blob.len()];
|
use aes::block_cipher_trait::generic_array::GenericArray;
|
||||||
let mut cipher =
|
use aes::block_cipher_trait::generic_array::typenum::Unsigned;
|
||||||
aes::ecb_decryptor(aes::KeySize::KeySize192, &key, crypto::blockmodes::NoPadding);
|
|
||||||
cipher
|
|
||||||
.decrypt(
|
|
||||||
&mut crypto::buffer::RefReadBuffer::new(&encrypted_blob),
|
|
||||||
&mut crypto::buffer::RefWriteBuffer::new(&mut data),
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let l = encrypted_blob.len();
|
let mut data = base64::decode(encrypted_blob).unwrap();
|
||||||
|
let cipher = Aes192::new(GenericArray::from_slice(&key));
|
||||||
|
let block_size = <Aes192 as BlockCipher>::BlockSize::to_usize();
|
||||||
|
assert_eq!(data.len() % block_size, 0);
|
||||||
|
// replace to chunks_exact_mut with MSRV bump to 1.31
|
||||||
|
for chunk in data.chunks_mut(block_size) {
|
||||||
|
cipher.decrypt_block(GenericArray::from_mut_slice(chunk));
|
||||||
|
}
|
||||||
|
|
||||||
|
let l = data.len();
|
||||||
for i in 0..l - 0x10 {
|
for i in 0..l - 0x10 {
|
||||||
data[l - i - 1] ^= data[l - i - 0x11];
|
data[l - i - 1] ^= data[l - i - 0x11];
|
||||||
}
|
}
|
||||||
|
@ -152,10 +142,10 @@ where
|
||||||
serde::Serialize::serialize(&v.value(), ser)
|
serde::Serialize::serialize(&v.value(), ser)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_protobuf_enum<T, D>(de: D) -> Result<T, D::Error>
|
fn deserialize_protobuf_enum<'de, T, D>(de: D) -> Result<T, D::Error>
|
||||||
where
|
where
|
||||||
T: ProtobufEnum,
|
T: ProtobufEnum,
|
||||||
D: serde::Deserializer,
|
D: serde::Deserializer<'de>,
|
||||||
{
|
{
|
||||||
let v: i32 = try!(serde::Deserialize::deserialize(de));
|
let v: i32 = try!(serde::Deserialize::deserialize(de));
|
||||||
T::from_i32(v).ok_or_else(|| serde::de::Error::custom("Invalid enum value"))
|
T::from_i32(v).ok_or_else(|| serde::de::Error::custom("Invalid enum value"))
|
||||||
|
@ -169,9 +159,9 @@ where
|
||||||
serde::Serialize::serialize(&base64::encode(v.as_ref()), ser)
|
serde::Serialize::serialize(&base64::encode(v.as_ref()), ser)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_base64<D>(de: D) -> Result<Vec<u8>, D::Error>
|
fn deserialize_base64<'de, D>(de: D) -> Result<Vec<u8>, D::Error>
|
||||||
where
|
where
|
||||||
D: serde::Deserializer,
|
D: serde::Deserializer<'de>,
|
||||||
{
|
{
|
||||||
let v: String = try!(serde::Deserialize::deserialize(de));
|
let v: String = try!(serde::Deserialize::deserialize(de));
|
||||||
base64::decode(&v).map_err(|e| serde::de::Error::custom(e.to_string()))
|
base64::decode(&v).map_err(|e| serde::de::Error::custom(e.to_string()))
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use byteorder::{BigEndian, ByteOrder, WriteBytesExt};
|
use byteorder::{BigEndian, ByteOrder, WriteBytesExt};
|
||||||
use crypto::hmac::Hmac;
|
use hmac::{Hmac, Mac};
|
||||||
use crypto::mac::Mac;
|
use sha1::Sha1;
|
||||||
use crypto::sha1::Sha1;
|
|
||||||
use futures::{Async, Future, Poll};
|
use futures::{Async, Future, Poll};
|
||||||
use protobuf::{self, Message};
|
use protobuf::{self, Message};
|
||||||
use rand::thread_rng;
|
use rand::thread_rng;
|
||||||
|
@ -89,7 +88,7 @@ fn client_hello<T: AsyncWrite>(connection: T, gc: Vec<u8>) -> WriteAll<T, Vec<u8
|
||||||
packet
|
packet
|
||||||
.mut_build_info()
|
.mut_build_info()
|
||||||
.set_platform(protocol::keyexchange::Platform::PLATFORM_LINUX_X86);
|
.set_platform(protocol::keyexchange::Platform::PLATFORM_LINUX_X86);
|
||||||
packet.mut_build_info().set_version(0x10800000000);
|
packet.mut_build_info().set_version(109800078);
|
||||||
packet
|
packet
|
||||||
.mut_cryptosuites_supported()
|
.mut_cryptosuites_supported()
|
||||||
.push(protocol::keyexchange::Cryptosuite::CRYPTO_SUITE_SHANNON);
|
.push(protocol::keyexchange::Cryptosuite::CRYPTO_SUITE_SHANNON);
|
||||||
|
@ -187,17 +186,19 @@ fn read_into_accumulator<T: AsyncRead>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compute_keys(shared_secret: &[u8], packets: &[u8]) -> (Vec<u8>, Vec<u8>, Vec<u8>) {
|
fn compute_keys(shared_secret: &[u8], packets: &[u8]) -> (Vec<u8>, Vec<u8>, Vec<u8>) {
|
||||||
let mut data = Vec::with_capacity(0x64);
|
type HmacSha1 = Hmac<Sha1>;
|
||||||
let mut mac = Hmac::new(Sha1::new(), &shared_secret);
|
|
||||||
|
|
||||||
|
let mut data = Vec::with_capacity(0x64);
|
||||||
for i in 1..6 {
|
for i in 1..6 {
|
||||||
|
let mut mac = HmacSha1::new_varkey(&shared_secret)
|
||||||
|
.expect("HMAC can take key of any size");
|
||||||
mac.input(packets);
|
mac.input(packets);
|
||||||
mac.input(&[i]);
|
mac.input(&[i]);
|
||||||
data.extend_from_slice(&mac.result().code());
|
data.extend_from_slice(&mac.result().code());
|
||||||
mac.reset();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mac = Hmac::new(Sha1::new(), &data[..0x14]);
|
let mut mac = HmacSha1::new_varkey(&data[..0x14])
|
||||||
|
.expect("HMAC can take key of any size");;
|
||||||
mac.input(packets);
|
mac.input(packets);
|
||||||
|
|
||||||
(
|
(
|
||||||
|
|
|
@ -14,7 +14,6 @@ extern crate serde_derive;
|
||||||
extern crate base64;
|
extern crate base64;
|
||||||
extern crate byteorder;
|
extern crate byteorder;
|
||||||
extern crate bytes;
|
extern crate bytes;
|
||||||
extern crate crypto;
|
|
||||||
extern crate extprim;
|
extern crate extprim;
|
||||||
extern crate httparse;
|
extern crate httparse;
|
||||||
extern crate hyper;
|
extern crate hyper;
|
||||||
|
@ -33,6 +32,10 @@ extern crate tokio_core;
|
||||||
extern crate tokio_io;
|
extern crate tokio_io;
|
||||||
extern crate url;
|
extern crate url;
|
||||||
extern crate uuid;
|
extern crate uuid;
|
||||||
|
extern crate sha1;
|
||||||
|
extern crate hmac;
|
||||||
|
extern crate pbkdf2;
|
||||||
|
extern crate aes;
|
||||||
|
|
||||||
extern crate librespot_protocol as protocol;
|
extern crate librespot_protocol as protocol;
|
||||||
|
|
||||||
|
|
|
@ -20,9 +20,12 @@ portaudio-rs = { version = "0.3.0", optional = true }
|
||||||
libpulse-sys = { version = "0.0.0", optional = true }
|
libpulse-sys = { version = "0.0.0", optional = true }
|
||||||
jack = { version = "0.5.3", optional = true }
|
jack = { version = "0.5.3", optional = true }
|
||||||
libc = { version = "0.2", optional = true }
|
libc = { version = "0.2", optional = true }
|
||||||
|
rodio = { git = "https://github.com/tomaka/rodio", optional = true, default-features = false }
|
||||||
|
cpal = { version = "*", optional = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
alsa-backend = ["alsa"]
|
alsa-backend = ["alsa"]
|
||||||
portaudio-backend = ["portaudio-rs"]
|
portaudio-backend = ["portaudio-rs"]
|
||||||
pulseaudio-backend = ["libpulse-sys", "libc"]
|
pulseaudio-backend = ["libpulse-sys", "libc"]
|
||||||
jackaudio-backend = ["jack"]
|
jackaudio-backend = ["jack"]
|
||||||
|
rodio-backend = ["rodio", "cpal"]
|
||||||
|
|
|
@ -34,6 +34,11 @@ mod jackaudio;
|
||||||
#[cfg(feature = "jackaudio-backend")]
|
#[cfg(feature = "jackaudio-backend")]
|
||||||
use self::jackaudio::JackSink;
|
use self::jackaudio::JackSink;
|
||||||
|
|
||||||
|
#[cfg(feature = "rodio-backend")]
|
||||||
|
mod rodio;
|
||||||
|
#[cfg(feature = "rodio-backend")]
|
||||||
|
use self::rodio::RodioSink;
|
||||||
|
|
||||||
mod pipe;
|
mod pipe;
|
||||||
use self::pipe::StdoutSink;
|
use self::pipe::StdoutSink;
|
||||||
|
|
||||||
|
@ -46,6 +51,8 @@ pub const BACKENDS: &'static [(&'static str, fn(Option<String>) -> Box<Sink>)] =
|
||||||
("pulseaudio", mk_sink::<PulseAudioSink>),
|
("pulseaudio", mk_sink::<PulseAudioSink>),
|
||||||
#[cfg(feature = "jackaudio-backend")]
|
#[cfg(feature = "jackaudio-backend")]
|
||||||
("jackaudio", mk_sink::<JackSink>),
|
("jackaudio", mk_sink::<JackSink>),
|
||||||
|
#[cfg(feature = "rodio-backend")]
|
||||||
|
("rodio", mk_sink::<RodioSink>),
|
||||||
("pipe", mk_sink::<StdoutSink>),
|
("pipe", mk_sink::<StdoutSink>),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
115
playback/src/audio_backend/rodio.rs
Normal file
115
playback/src/audio_backend/rodio.rs
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
use super::{Open, Sink};
|
||||||
|
extern crate rodio;
|
||||||
|
extern crate cpal;
|
||||||
|
use std::{io, thread, time};
|
||||||
|
use std::process::exit;
|
||||||
|
|
||||||
|
pub struct RodioSink {
|
||||||
|
rodio_sink: rodio::Sink,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn list_formats(ref device: &rodio::Device) {
|
||||||
|
let default_fmt = match device.default_output_format() {
|
||||||
|
Ok(fmt) => cpal::SupportedFormat::from(fmt),
|
||||||
|
Err(e) => {
|
||||||
|
warn!("Error getting default rodio::Sink format: {:?}", e);
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut output_formats = match device.supported_output_formats() {
|
||||||
|
Ok(f) => f.peekable(),
|
||||||
|
Err(e) => {
|
||||||
|
warn!("Error getting supported rodio::Sink formats: {:?}", e);
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if output_formats.peek().is_some() {
|
||||||
|
debug!(" Available formats:");
|
||||||
|
for format in output_formats {
|
||||||
|
let s = format!("{}ch, {:?}, min {:?}, max {:?}", format.channels, format.data_type, format.min_sample_rate, format.max_sample_rate);
|
||||||
|
if format == default_fmt {
|
||||||
|
debug!(" (default) {}", s);
|
||||||
|
} else {
|
||||||
|
debug!(" {:?}", format);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn list_outputs() {
|
||||||
|
let default_device = rodio::default_output_device().unwrap();
|
||||||
|
println!("Default Audio Device:\n {}", default_device.name());
|
||||||
|
list_formats(&default_device);
|
||||||
|
|
||||||
|
println!("Other Available Audio Devices:");
|
||||||
|
for device in rodio::output_devices() {
|
||||||
|
if device.name() != default_device.name() {
|
||||||
|
println!(" {}", device.name());
|
||||||
|
list_formats(&device);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Open for RodioSink {
|
||||||
|
fn open(device: Option<String>) -> RodioSink {
|
||||||
|
debug!("Using rodio sink");
|
||||||
|
|
||||||
|
let mut rodio_device = rodio::default_output_device().expect("no output device available");
|
||||||
|
if device.is_some() {
|
||||||
|
let device_name = device.unwrap();
|
||||||
|
|
||||||
|
if device_name == "?".to_string() {
|
||||||
|
list_outputs();
|
||||||
|
exit(0)
|
||||||
|
}
|
||||||
|
let mut found = false;
|
||||||
|
for d in rodio::output_devices() {
|
||||||
|
if d.name() == device_name {
|
||||||
|
rodio_device = d;
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
println!("No output sink matching '{}' found.", device_name);
|
||||||
|
exit(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let sink = rodio::Sink::new(&rodio_device);
|
||||||
|
|
||||||
|
RodioSink {
|
||||||
|
rodio_sink: sink,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sink for RodioSink {
|
||||||
|
fn start(&mut self) -> io::Result<()> {
|
||||||
|
// More similar to an "unpause" than "play". Doesn't undo "stop".
|
||||||
|
// self.rodio_sink.play();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stop(&mut self) -> io::Result<()> {
|
||||||
|
// This will immediately stop playback, but the sink is then unusable.
|
||||||
|
// We just have to let the current buffer play till the end.
|
||||||
|
// self.rodio_sink.stop();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(&mut self, data: &[i16]) -> io::Result<()> {
|
||||||
|
let source = rodio::buffer::SamplesBuffer::new(2, 44100, data);
|
||||||
|
self.rodio_sink.append(source);
|
||||||
|
|
||||||
|
// Chunk sizes seem to be about 256 to 3000 ish items long.
|
||||||
|
// Assuming they're on average 1628 then a half second buffer is:
|
||||||
|
// 44100 elements --> about 27 chunks
|
||||||
|
while self.rodio_sink.len() > 26 {
|
||||||
|
// sleep and wait for rodio to drain a bit
|
||||||
|
thread::sleep(time::Duration::from_millis(10));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -560,12 +560,12 @@ impl PlayerInternal {
|
||||||
let key = self
|
let key = self
|
||||||
.session
|
.session
|
||||||
.audio_key()
|
.audio_key()
|
||||||
.request(track.id, file_id)
|
.request(track.id, file_id);
|
||||||
.wait()
|
let encrypted_file = AudioFile::open(&self.session, file_id);
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let encrypted_file = AudioFile::open(&self.session, file_id).wait().unwrap();
|
|
||||||
|
|
||||||
|
let encrypted_file = encrypted_file.wait().unwrap();
|
||||||
|
let key = key.wait().unwrap();
|
||||||
let mut decrypted_file = AudioDecrypt::new(key, encrypted_file);
|
let mut decrypted_file = AudioDecrypt::new(key, encrypted_file);
|
||||||
|
|
||||||
let normalisation_factor = match NormalisationData::parse_from_file(&mut decrypted_file) {
|
let normalisation_factor = match NormalisationData::parse_from_file(&mut decrypted_file) {
|
||||||
|
@ -580,10 +580,12 @@ impl PlayerInternal {
|
||||||
|
|
||||||
let mut decoder = VorbisDecoder::new(audio_file).unwrap();
|
let mut decoder = VorbisDecoder::new(audio_file).unwrap();
|
||||||
|
|
||||||
|
if position != 0 {
|
||||||
match decoder.seek(position) {
|
match decoder.seek(position) {
|
||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
Err(err) => error!("Vorbis error: {:?}", err),
|
Err(err) => error!("Vorbis error: {:?}", err),
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
info!("Track \"{}\" loaded", track.name);
|
info!("Track \"{}\" loaded", track.name);
|
||||||
|
|
||||||
|
|
|
@ -8,4 +8,4 @@ build = "build.rs"
|
||||||
protobuf = "2.0.5"
|
protobuf = "2.0.5"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
protoc-rust = "2.0.5"
|
protobuf-codegen-pure = "2.0.5"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
extern crate protoc_rust;
|
extern crate protobuf_codegen_pure;
|
||||||
use protoc_rust::Customize;
|
use protobuf_codegen_pure::Customize;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::prelude::*;
|
use std::io::prelude::*;
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ fn main() {
|
||||||
for &(path, expected_checksum) in files::FILES {
|
for &(path, expected_checksum) in files::FILES {
|
||||||
let actual = cksum_file(path).unwrap();
|
let actual = cksum_file(path).unwrap();
|
||||||
if expected_checksum != actual {
|
if expected_checksum != actual {
|
||||||
protoc_rust::run(protoc_rust::Args {
|
protobuf_codegen_pure::run(protobuf_codegen_pure::Args {
|
||||||
out_dir: "src",
|
out_dir: "src",
|
||||||
input: &[path],
|
input: &[path],
|
||||||
includes: &["proto"],
|
includes: &["proto"],
|
||||||
|
@ -28,7 +28,6 @@ fn main() {
|
||||||
if changed {
|
if changed {
|
||||||
// Write new checksums to file
|
// Write new checksums to file
|
||||||
let mut file = File::create("files.rs").unwrap();
|
let mut file = File::create("files.rs").unwrap();
|
||||||
println!("f_str: {:?}",f_str);
|
|
||||||
file.write_all(f_str.as_bytes()).unwrap();
|
file.write_all(f_str.as_bytes()).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
set -eu
|
|
||||||
|
|
||||||
SRC="authentication keyexchange mercury
|
|
||||||
metadata pubsub spirc"
|
|
||||||
|
|
||||||
cat > src/lib.rs <<EOF
|
|
||||||
// Autogenerated by build.sh
|
|
||||||
|
|
||||||
extern crate protobuf;
|
|
||||||
EOF
|
|
||||||
|
|
||||||
cat > files.rs <<EOF
|
|
||||||
// Autogenerated by build.sh
|
|
||||||
|
|
||||||
pub const FILES : &'static [(&'static str, u32)] = &[
|
|
||||||
EOF
|
|
||||||
|
|
||||||
for name in $SRC; do
|
|
||||||
src=proto/$name.proto
|
|
||||||
out=src/$name.rs
|
|
||||||
checksum=$(cksum $src | cut -f 1 -d' ')
|
|
||||||
|
|
||||||
protoc --rust_out src/ -I proto/ proto/$name.proto
|
|
||||||
|
|
||||||
echo "pub mod $name;" >> src/lib.rs
|
|
||||||
echo " (\"$src\", $checksum)," >> files.rs
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "];" >> files.rs
|
|
|
@ -4965,84 +4965,83 @@ impl ::protobuf::reflect::ProtobufValue for AccountType {
|
||||||
}
|
}
|
||||||
|
|
||||||
static file_descriptor_proto_data: &'static [u8] = b"\
|
static file_descriptor_proto_data: &'static [u8] = b"\
|
||||||
\n\x14authentication.proto\"\xec\x03\n\x17ClientResponseEncrypted\x12>\n\
|
\n\x14authentication.proto\x12\0\"\x8e\x03\n\x17ClientResponseEncrypted\
|
||||||
\x11login_credentials\x18\n\x20\x02(\x0b2\x11.LoginCredentialsR\x10login\
|
\x120\n\x11login_credentials\x18\n\x20\x02(\x0b2\x11.LoginCredentialsB\
|
||||||
Credentials\x12;\n\x10account_creation\x18\x14\x20\x01(\x0e2\x10.Account\
|
\x02\x18\0\x12.\n\x10account_creation\x18\x14\x20\x01(\x0e2\x10.AccountC\
|
||||||
CreationR\x0faccountCreation\x12L\n\x14fingerprint_response\x18\x1e\x20\
|
reationB\x02\x18\0\x12;\n\x14fingerprint_response\x18\x1e\x20\x01(\x0b2\
|
||||||
\x01(\x0b2\x19.FingerprintResponseUnionR\x13fingerprintResponse\x121\n\
|
\x19.FingerprintResponseUnionB\x02\x18\0\x12)\n\x0bpeer_ticket\x18(\x20\
|
||||||
\x0bpeer_ticket\x18(\x20\x01(\x0b2\x10.PeerTicketUnionR\npeerTicket\x12,\
|
\x01(\x0b2\x10.PeerTicketUnionB\x02\x18\0\x12$\n\x0bsystem_info\x182\x20\
|
||||||
\n\x0bsystem_info\x182\x20\x02(\x0b2\x0b.SystemInfoR\nsystemInfo\x12%\n\
|
\x02(\x0b2\x0b.SystemInfoB\x02\x18\0\x12\x1a\n\x0eplatform_model\x18<\
|
||||||
\x0eplatform_model\x18<\x20\x01(\tR\rplatformModel\x12%\n\x0eversion_str\
|
\x20\x01(\tB\x02\x18\0\x12\x1a\n\x0eversion_string\x18F\x20\x01(\tB\x02\
|
||||||
ing\x18F\x20\x01(\tR\rversionString\x12)\n\x06appkey\x18P\x20\x01(\x0b2\
|
\x18\0\x12%\n\x06appkey\x18P\x20\x01(\x0b2\x11.LibspotifyAppKeyB\x02\x18\
|
||||||
\x11.LibspotifyAppKeyR\x06appkey\x12,\n\x0bclient_info\x18Z\x20\x01(\x0b\
|
\0\x12$\n\x0bclient_info\x18Z\x20\x01(\x0b2\x0b.ClientInfoB\x02\x18\0\"e\
|
||||||
2\x0b.ClientInfoR\nclientInfo\"r\n\x10LoginCredentials\x12\x1a\n\x08user\
|
\n\x10LoginCredentials\x12\x14\n\x08username\x18\n\x20\x01(\tB\x02\x18\0\
|
||||||
name\x18\n\x20\x01(\tR\x08username\x12%\n\x03typ\x18\x14\x20\x02(\x0e2\
|
\x12$\n\x03typ\x18\x14\x20\x02(\x0e2\x13.AuthenticationTypeB\x02\x18\0\
|
||||||
\x13.AuthenticationTypeR\x03typ\x12\x1b\n\tauth_data\x18\x1e\x20\x01(\
|
\x12\x15\n\tauth_data\x18\x1e\x20\x01(\x0cB\x02\x18\0\"\x81\x01\n\x18Fin\
|
||||||
\x0cR\x08authData\"\x8c\x01\n\x18FingerprintResponseUnion\x12/\n\x05grai\
|
gerprintResponseUnion\x12,\n\x05grain\x18\n\x20\x01(\x0b2\x19.Fingerprin\
|
||||||
n\x18\n\x20\x01(\x0b2\x19.FingerprintGrainResponseR\x05grain\x12?\n\x0bh\
|
tGrainResponseB\x02\x18\0\x127\n\x0bhmac_ripemd\x18\x14\x20\x01(\x0b2\
|
||||||
mac_ripemd\x18\x14\x20\x01(\x0b2\x1e.FingerprintHmacRipemdResponseR\nhma\
|
\x1e.FingerprintHmacRipemdResponseB\x02\x18\0\"5\n\x18FingerprintGrainRe\
|
||||||
cRipemd\"?\n\x18FingerprintGrainResponse\x12#\n\rencrypted_key\x18\n\x20\
|
sponse\x12\x19\n\rencrypted_key\x18\n\x20\x02(\x0cB\x02\x18\0\"1\n\x1dFi\
|
||||||
\x02(\x0cR\x0cencryptedKey\"3\n\x1dFingerprintHmacRipemdResponse\x12\x12\
|
ngerprintHmacRipemdResponse\x12\x10\n\x04hmac\x18\n\x20\x02(\x0cB\x02\
|
||||||
\n\x04hmac\x18\n\x20\x02(\x0cR\x04hmac\"u\n\x0fPeerTicketUnion\x123\n\np\
|
\x18\0\"g\n\x0fPeerTicketUnion\x12,\n\npublic_key\x18\n\x20\x01(\x0b2\
|
||||||
ublic_key\x18\n\x20\x01(\x0b2\x14.PeerTicketPublicKeyR\tpublicKey\x12-\n\
|
\x14.PeerTicketPublicKeyB\x02\x18\0\x12&\n\nold_ticket\x18\x14\x20\x01(\
|
||||||
\nold_ticket\x18\x14\x20\x01(\x0b2\x0e.PeerTicketOldR\toldTicket\"4\n\
|
\x0b2\x0e.PeerTicketOldB\x02\x18\0\"-\n\x13PeerTicketPublicKey\x12\x16\n\
|
||||||
\x13PeerTicketPublicKey\x12\x1d\n\npublic_key\x18\n\x20\x02(\x0cR\tpubli\
|
\npublic_key\x18\n\x20\x02(\x0cB\x02\x18\0\"K\n\rPeerTicketOld\x12\x17\n\
|
||||||
cKey\"d\n\rPeerTicketOld\x12\x1f\n\x0bpeer_ticket\x18\n\x20\x02(\x0cR\np\
|
\x0bpeer_ticket\x18\n\x20\x02(\x0cB\x02\x18\0\x12!\n\x15peer_ticket_sign\
|
||||||
eerTicket\x122\n\x15peer_ticket_signature\x18\x14\x20\x02(\x0cR\x13peerT\
|
ature\x18\x14\x20\x02(\x0cB\x02\x18\0\"\x91\x02\n\nSystemInfo\x12\"\n\nc\
|
||||||
icketSignature\"\xd4\x02\n\nSystemInfo\x12)\n\ncpu_family\x18\n\x20\x02(\
|
pu_family\x18\n\x20\x02(\x0e2\n.CpuFamilyB\x02\x18\0\x12\x17\n\x0bcpu_su\
|
||||||
\x0e2\n.CpuFamilyR\tcpuFamily\x12\x1f\n\x0bcpu_subtype\x18\x14\x20\x01(\
|
btype\x18\x14\x20\x01(\rB\x02\x18\0\x12\x13\n\x07cpu_ext\x18\x1e\x20\x01\
|
||||||
\rR\ncpuSubtype\x12\x17\n\x07cpu_ext\x18\x1e\x20\x01(\rR\x06cpuExt\x12\
|
(\rB\x02\x18\0\x12\x19\n\x05brand\x18(\x20\x01(\x0e2\x06.BrandB\x02\x18\
|
||||||
\x1c\n\x05brand\x18(\x20\x01(\x0e2\x06.BrandR\x05brand\x12\x1f\n\x0bbran\
|
\0\x12\x17\n\x0bbrand_flags\x182\x20\x01(\rB\x02\x18\0\x12\x13\n\x02os\
|
||||||
d_flags\x182\x20\x01(\rR\nbrandFlags\x12\x13\n\x02os\x18<\x20\x02(\x0e2\
|
\x18<\x20\x02(\x0e2\x03.OsB\x02\x18\0\x12\x16\n\nos_version\x18F\x20\x01\
|
||||||
\x03.OsR\x02os\x12\x1d\n\nos_version\x18F\x20\x01(\rR\tosVersion\x12\x15\
|
(\rB\x02\x18\0\x12\x12\n\x06os_ext\x18P\x20\x01(\rB\x02\x18\0\x12%\n\x19\
|
||||||
\n\x06os_ext\x18P\x20\x01(\rR\x05osExt\x12:\n\x19system_information_stri\
|
system_information_string\x18Z\x20\x01(\tB\x02\x18\0\x12\x15\n\tdevice_i\
|
||||||
ng\x18Z\x20\x01(\tR\x17systemInformationString\x12\x1b\n\tdevice_id\x18d\
|
d\x18d\x20\x01(\tB\x02\x18\0\"\x84\x01\n\x10LibspotifyAppKey\x12\x13\n\
|
||||||
\x20\x01(\tR\x08deviceId\"\xa5\x01\n\x10LibspotifyAppKey\x12\x18\n\x07ve\
|
\x07version\x18\x01\x20\x02(\rB\x02\x18\0\x12\x12\n\x06devkey\x18\x02\
|
||||||
rsion\x18\x01\x20\x02(\rR\x07version\x12\x16\n\x06devkey\x18\x02\x20\x02\
|
\x20\x02(\x0cB\x02\x18\0\x12\x15\n\tsignature\x18\x03\x20\x02(\x0cB\x02\
|
||||||
(\x0cR\x06devkey\x12\x1c\n\tsignature\x18\x03\x20\x02(\x0cR\tsignature\
|
\x18\0\x12\x15\n\tuseragent\x18\x04\x20\x02(\tB\x02\x18\0\x12\x19\n\rcal\
|
||||||
\x12\x1c\n\tuseragent\x18\x04\x20\x02(\tR\tuseragent\x12#\n\rcallback_ha\
|
lback_hash\x18\x05\x20\x02(\x0cB\x02\x18\0\"\\\n\nClientInfo\x12\x13\n\
|
||||||
sh\x18\x05\x20\x02(\x0cR\x0ccallbackHash\"g\n\nClientInfo\x12\x18\n\x07l\
|
\x07limited\x18\x01\x20\x01(\x08B\x02\x18\0\x12#\n\x02fb\x18\x02\x20\x01\
|
||||||
imited\x18\x01\x20\x01(\x08R\x07limited\x12#\n\x02fb\x18\x02\x20\x01(\
|
(\x0b2\x13.ClientInfoFacebookB\x02\x18\0\x12\x14\n\x08language\x18\x03\
|
||||||
\x0b2\x13.ClientInfoFacebookR\x02fb\x12\x1a\n\x08language\x18\x03\x20\
|
\x20\x01(\tB\x02\x18\0\",\n\x12ClientInfoFacebook\x12\x16\n\nmachine_id\
|
||||||
\x01(\tR\x08language\"3\n\x12ClientInfoFacebook\x12\x1d\n\nmachine_id\
|
\x18\x01\x20\x01(\tB\x02\x18\0\"\xe1\x02\n\tAPWelcome\x12\x1e\n\x12canon\
|
||||||
\x18\x01\x20\x01(\tR\tmachineId\"\xd4\x03\n\tAPWelcome\x12-\n\x12canonic\
|
ical_username\x18\n\x20\x02(\tB\x02\x18\0\x120\n\x16account_type_logged_\
|
||||||
al_username\x18\n\x20\x02(\tR\x11canonicalUsername\x12A\n\x16account_typ\
|
in\x18\x14\x20\x02(\x0e2\x0c.AccountTypeB\x02\x18\0\x124\n\x1acredential\
|
||||||
e_logged_in\x18\x14\x20\x02(\x0e2\x0c.AccountTypeR\x13accountTypeLoggedI\
|
s_type_logged_in\x18\x19\x20\x02(\x0e2\x0c.AccountTypeB\x02\x18\0\x12?\n\
|
||||||
n\x12I\n\x1acredentials_type_logged_in\x18\x19\x20\x02(\x0e2\x0c.Account\
|
\x1ereusable_auth_credentials_type\x18\x1e\x20\x02(\x0e2\x13.Authenticat\
|
||||||
TypeR\x17credentialsTypeLoggedIn\x12X\n\x1ereusable_auth_credentials_typ\
|
ionTypeB\x02\x18\0\x12%\n\x19reusable_auth_credentials\x18(\x20\x02(\x0c\
|
||||||
e\x18\x1e\x20\x02(\x0e2\x13.AuthenticationTypeR\x1breusableAuthCredentia\
|
B\x02\x18\0\x12\x16\n\nlfs_secret\x182\x20\x01(\x0cB\x02\x18\0\x12&\n\
|
||||||
lsType\x12:\n\x19reusable_auth_credentials\x18(\x20\x02(\x0cR\x17reusabl\
|
\x0caccount_info\x18<\x20\x01(\x0b2\x0c.AccountInfoB\x02\x18\0\x12$\n\
|
||||||
eAuthCredentials\x12\x1d\n\nlfs_secret\x182\x20\x01(\x0cR\tlfsSecret\x12\
|
\x02fb\x18F\x20\x01(\x0b2\x14.AccountInfoFacebookB\x02\x18\0\"c\n\x0bAcc\
|
||||||
/\n\x0caccount_info\x18<\x20\x01(\x0b2\x0c.AccountInfoR\x0baccountInfo\
|
ountInfo\x12(\n\x07spotify\x18\x01\x20\x01(\x0b2\x13.AccountInfoSpotifyB\
|
||||||
\x12$\n\x02fb\x18F\x20\x01(\x0b2\x14.AccountInfoFacebookR\x02fb\"n\n\x0b\
|
\x02\x18\0\x12*\n\x08facebook\x18\x02\x20\x01(\x0b2\x14.AccountInfoFaceb\
|
||||||
AccountInfo\x12-\n\x07spotify\x18\x01\x20\x01(\x0b2\x13.AccountInfoSpoti\
|
ookB\x02\x18\0\"\x14\n\x12AccountInfoSpotify\"G\n\x13AccountInfoFacebook\
|
||||||
fyR\x07spotify\x120\n\x08facebook\x18\x02\x20\x01(\x0b2\x14.AccountInfoF\
|
\x12\x18\n\x0caccess_token\x18\x01\x20\x01(\tB\x02\x18\0\x12\x16\n\nmach\
|
||||||
acebookR\x08facebook\"\x14\n\x12AccountInfoSpotify\"W\n\x13AccountInfoFa\
|
ine_id\x18\x02\x20\x01(\tB\x02\x18\0*\xda\x01\n\x12AuthenticationType\
|
||||||
cebook\x12!\n\x0caccess_token\x18\x01\x20\x01(\tR\x0baccessToken\x12\x1d\
|
\x12\x1c\n\x18AUTHENTICATION_USER_PASS\x10\0\x12-\n)AUTHENTICATION_STORE\
|
||||||
\n\nmachine_id\x18\x02\x20\x01(\tR\tmachineId*\xd6\x01\n\x12Authenticati\
|
D_SPOTIFY_CREDENTIALS\x10\x01\x12.\n*AUTHENTICATION_STORED_FACEBOOK_CRED\
|
||||||
onType\x12\x1c\n\x18AUTHENTICATION_USER_PASS\x10\0\x12-\n)AUTHENTICATION\
|
ENTIALS\x10\x02\x12\x20\n\x1cAUTHENTICATION_SPOTIFY_TOKEN\x10\x03\x12!\n\
|
||||||
_STORED_SPOTIFY_CREDENTIALS\x10\x01\x12.\n*AUTHENTICATION_STORED_FACEBOO\
|
\x1dAUTHENTICATION_FACEBOOK_TOKEN\x10\x04\x1a\x02\x10\0*]\n\x0fAccountCr\
|
||||||
K_CREDENTIALS\x10\x02\x12\x20\n\x1cAUTHENTICATION_SPOTIFY_TOKEN\x10\x03\
|
eation\x12\"\n\x1eACCOUNT_CREATION_ALWAYS_PROMPT\x10\x01\x12\"\n\x1eACCO\
|
||||||
\x12!\n\x1dAUTHENTICATION_FACEBOOK_TOKEN\x10\x04*Y\n\x0fAccountCreation\
|
UNT_CREATION_ALWAYS_CREATE\x10\x03\x1a\x02\x10\0*\xa1\x01\n\tCpuFamily\
|
||||||
\x12\"\n\x1eACCOUNT_CREATION_ALWAYS_PROMPT\x10\x01\x12\"\n\x1eACCOUNT_CR\
|
\x12\x0f\n\x0bCPU_UNKNOWN\x10\0\x12\x0b\n\x07CPU_X86\x10\x01\x12\x0e\n\n\
|
||||||
EATION_ALWAYS_CREATE\x10\x03*\x9d\x01\n\tCpuFamily\x12\x0f\n\x0bCPU_UNKN\
|
CPU_X86_64\x10\x02\x12\x0b\n\x07CPU_PPC\x10\x03\x12\x0e\n\nCPU_PPC_64\
|
||||||
OWN\x10\0\x12\x0b\n\x07CPU_X86\x10\x01\x12\x0e\n\nCPU_X86_64\x10\x02\x12\
|
\x10\x04\x12\x0b\n\x07CPU_ARM\x10\x05\x12\x0c\n\x08CPU_IA64\x10\x06\x12\
|
||||||
\x0b\n\x07CPU_PPC\x10\x03\x12\x0e\n\nCPU_PPC_64\x10\x04\x12\x0b\n\x07CPU\
|
\n\n\x06CPU_SH\x10\x07\x12\x0c\n\x08CPU_MIPS\x10\x08\x12\x10\n\x0cCPU_BL\
|
||||||
_ARM\x10\x05\x12\x0c\n\x08CPU_IA64\x10\x06\x12\n\n\x06CPU_SH\x10\x07\x12\
|
ACKFIN\x10\t\x1a\x02\x10\0*O\n\x05Brand\x12\x13\n\x0fBRAND_UNBRANDED\x10\
|
||||||
\x0c\n\x08CPU_MIPS\x10\x08\x12\x10\n\x0cCPU_BLACKFIN\x10\t*K\n\x05Brand\
|
\0\x12\r\n\tBRAND_INQ\x10\x01\x12\r\n\tBRAND_HTC\x10\x02\x12\x0f\n\x0bBR\
|
||||||
\x12\x13\n\x0fBRAND_UNBRANDED\x10\0\x12\r\n\tBRAND_INQ\x10\x01\x12\r\n\t\
|
AND_NOKIA\x10\x03\x1a\x02\x10\0*\xd5\x02\n\x02Os\x12\x0e\n\nOS_UNKNOWN\
|
||||||
BRAND_HTC\x10\x02\x12\x0f\n\x0bBRAND_NOKIA\x10\x03*\xd1\x02\n\x02Os\x12\
|
\x10\0\x12\x0e\n\nOS_WINDOWS\x10\x01\x12\n\n\x06OS_OSX\x10\x02\x12\r\n\t\
|
||||||
\x0e\n\nOS_UNKNOWN\x10\0\x12\x0e\n\nOS_WINDOWS\x10\x01\x12\n\n\x06OS_OSX\
|
OS_IPHONE\x10\x03\x12\n\n\x06OS_S60\x10\x04\x12\x0c\n\x08OS_LINUX\x10\
|
||||||
\x10\x02\x12\r\n\tOS_IPHONE\x10\x03\x12\n\n\x06OS_S60\x10\x04\x12\x0c\n\
|
\x05\x12\x11\n\rOS_WINDOWS_CE\x10\x06\x12\x0e\n\nOS_ANDROID\x10\x07\x12\
|
||||||
\x08OS_LINUX\x10\x05\x12\x11\n\rOS_WINDOWS_CE\x10\x06\x12\x0e\n\nOS_ANDR\
|
\x0b\n\x07OS_PALM\x10\x08\x12\x0e\n\nOS_FREEBSD\x10\t\x12\x11\n\rOS_BLAC\
|
||||||
OID\x10\x07\x12\x0b\n\x07OS_PALM\x10\x08\x12\x0e\n\nOS_FREEBSD\x10\t\x12\
|
KBERRY\x10\n\x12\x0c\n\x08OS_SONOS\x10\x0b\x12\x0f\n\x0bOS_LOGITECH\x10\
|
||||||
\x11\n\rOS_BLACKBERRY\x10\n\x12\x0c\n\x08OS_SONOS\x10\x0b\x12\x0f\n\x0bO\
|
\x0c\x12\n\n\x06OS_WP7\x10\r\x12\x0c\n\x08OS_ONKYO\x10\x0e\x12\x0e\n\nOS\
|
||||||
S_LOGITECH\x10\x0c\x12\n\n\x06OS_WP7\x10\r\x12\x0c\n\x08OS_ONKYO\x10\x0e\
|
_PHILIPS\x10\x0f\x12\t\n\x05OS_WD\x10\x10\x12\x0c\n\x08OS_VOLVO\x10\x11\
|
||||||
\x12\x0e\n\nOS_PHILIPS\x10\x0f\x12\t\n\x05OS_WD\x10\x10\x12\x0c\n\x08OS_\
|
\x12\x0b\n\x07OS_TIVO\x10\x12\x12\x0b\n\x07OS_AWOX\x10\x13\x12\x0c\n\x08\
|
||||||
VOLVO\x10\x11\x12\x0b\n\x07OS_TIVO\x10\x12\x12\x0b\n\x07OS_AWOX\x10\x13\
|
OS_MEEGO\x10\x14\x12\r\n\tOS_QNXNTO\x10\x15\x12\n\n\x06OS_BCO\x10\x16\
|
||||||
\x12\x0c\n\x08OS_MEEGO\x10\x14\x12\r\n\tOS_QNXNTO\x10\x15\x12\n\n\x06OS_\
|
\x1a\x02\x10\0*,\n\x0bAccountType\x12\x0b\n\x07Spotify\x10\0\x12\x0c\n\
|
||||||
BCO\x10\x16*(\n\x0bAccountType\x12\x0b\n\x07Spotify\x10\0\x12\x0c\n\x08F\
|
\x08Facebook\x10\x01\x1a\x02\x10\0B\0b\x06proto2\
|
||||||
acebook\x10\x01\
|
|
||||||
";
|
";
|
||||||
|
|
||||||
static mut file_descriptor_proto_lazy: ::protobuf::lazy::Lazy<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::lazy::Lazy {
|
static mut file_descriptor_proto_lazy: ::protobuf::lazy::Lazy<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::lazy::Lazy {
|
||||||
|
|
|
@ -6705,105 +6705,103 @@ impl ::protobuf::reflect::ProtobufValue for ErrorCode {
|
||||||
}
|
}
|
||||||
|
|
||||||
static file_descriptor_proto_data: &'static [u8] = b"\
|
static file_descriptor_proto_data: &'static [u8] = b"\
|
||||||
\n\x11keyexchange.proto\"\xb2\x03\n\x0bClientHello\x12)\n\nbuild_info\
|
\n\x11keyexchange.proto\x12\0\"\xd0\x02\n\x0bClientHello\x12\"\n\nbuild_\
|
||||||
\x18\n\x20\x02(\x0b2\n.BuildInfoR\tbuildInfo\x12C\n\x16fingerprints_supp\
|
info\x18\n\x20\x02(\x0b2\n.BuildInfoB\x02\x18\0\x120\n\x16fingerprints_s\
|
||||||
orted\x18\x14\x20\x03(\x0e2\x0c.FingerprintR\x15fingerprintsSupported\
|
upported\x18\x14\x20\x03(\x0e2\x0c.FingerprintB\x02\x18\0\x120\n\x16cryp\
|
||||||
\x12C\n\x16cryptosuites_supported\x18\x1e\x20\x03(\x0e2\x0c.CryptosuiteR\
|
tosuites_supported\x18\x1e\x20\x03(\x0e2\x0c.CryptosuiteB\x02\x18\0\x12,\
|
||||||
\x15cryptosuitesSupported\x12=\n\x14powschemes_supported\x18(\x20\x03(\
|
\n\x14powschemes_supported\x18(\x20\x03(\x0e2\n.PowschemeB\x02\x18\0\x12\
|
||||||
\x0e2\n.PowschemeR\x13powschemesSupported\x12D\n\x12login_crypto_hello\
|
6\n\x12login_crypto_hello\x182\x20\x02(\x0b2\x16.LoginCryptoHelloUnionB\
|
||||||
\x182\x20\x02(\x0b2\x16.LoginCryptoHelloUnionR\x10loginCryptoHello\x12!\
|
\x02\x18\0\x12\x18\n\x0cclient_nonce\x18<\x20\x02(\x0cB\x02\x18\0\x12\
|
||||||
\n\x0cclient_nonce\x18<\x20\x02(\x0cR\x0bclientNonce\x12\x18\n\x07paddin\
|
\x13\n\x07padding\x18F\x20\x01(\x0cB\x02\x18\0\x12$\n\x0bfeature_set\x18\
|
||||||
g\x18F\x20\x01(\x0cR\x07padding\x12,\n\x0bfeature_set\x18P\x20\x01(\x0b2\
|
P\x20\x01(\x0b2\x0b.FeatureSetB\x02\x18\0\"\x8a\x01\n\tBuildInfo\x12\x1d\
|
||||||
\x0b.FeatureSetR\nfeatureSet\"\xa4\x01\n\tBuildInfo\x12\"\n\x07product\
|
\n\x07product\x18\n\x20\x02(\x0e2\x08.ProductB\x02\x18\0\x12(\n\rproduct\
|
||||||
\x18\n\x20\x02(\x0e2\x08.ProductR\x07product\x122\n\rproduct_flags\x18\
|
_flags\x18\x14\x20\x03(\x0e2\r.ProductFlagsB\x02\x18\0\x12\x1f\n\x08plat\
|
||||||
\x14\x20\x03(\x0e2\r.ProductFlagsR\x0cproductFlags\x12%\n\x08platform\
|
form\x18\x1e\x20\x02(\x0e2\t.PlatformB\x02\x18\0\x12\x13\n\x07version\
|
||||||
\x18\x1e\x20\x02(\x0e2\t.PlatformR\x08platform\x12\x18\n\x07version\x18(\
|
\x18(\x20\x02(\x04B\x02\x18\0\"S\n\x15LoginCryptoHelloUnion\x12:\n\x0edi\
|
||||||
\x20\x02(\x04R\x07version\"^\n\x15LoginCryptoHelloUnion\x12E\n\x0ediffie\
|
ffie_hellman\x18\n\x20\x01(\x0b2\x1e.LoginCryptoDiffieHellmanHelloB\x02\
|
||||||
_hellman\x18\n\x20\x01(\x0b2\x1e.LoginCryptoDiffieHellmanHelloR\rdiffieH\
|
\x18\0\"N\n\x1dLoginCryptoDiffieHellmanHello\x12\x0e\n\x02gc\x18\n\x20\
|
||||||
ellman\"[\n\x1dLoginCryptoDiffieHellmanHello\x12\x0e\n\x02gc\x18\n\x20\
|
\x02(\x0cB\x02\x18\0\x12\x1d\n\x11server_keys_known\x18\x14\x20\x02(\rB\
|
||||||
\x02(\x0cR\x02gc\x12*\n\x11server_keys_known\x18\x14\x20\x02(\rR\x0fserv\
|
\x02\x18\0\"C\n\nFeatureSet\x12\x17\n\x0bautoupdate2\x18\x01\x20\x01(\
|
||||||
erKeysKnown\"Y\n\nFeatureSet\x12\x20\n\x0bautoupdate2\x18\x01\x20\x01(\
|
\x08B\x02\x18\0\x12\x1c\n\x10current_location\x18\x02\x20\x01(\x08B\x02\
|
||||||
\x08R\x0bautoupdate2\x12)\n\x10current_location\x18\x02\x20\x01(\x08R\
|
\x18\0\"\x90\x01\n\x11APResponseMessage\x12#\n\tchallenge\x18\n\x20\x01(\
|
||||||
\x0fcurrentLocation\"\xa5\x01\n\x11APResponseMessage\x12*\n\tchallenge\
|
\x0b2\x0c.APChallengeB\x02\x18\0\x12,\n\x07upgrade\x18\x14\x20\x01(\x0b2\
|
||||||
\x18\n\x20\x01(\x0b2\x0c.APChallengeR\tchallenge\x121\n\x07upgrade\x18\
|
\x17.UpgradeRequiredMessageB\x02\x18\0\x12(\n\x0clogin_failed\x18\x1e\
|
||||||
\x14\x20\x01(\x0b2\x17.UpgradeRequiredMessageR\x07upgrade\x121\n\x0clogi\
|
\x20\x01(\x0b2\x0e.APLoginFailedB\x02\x18\0\"\x9f\x02\n\x0bAPChallenge\
|
||||||
n_failed\x18\x1e\x20\x01(\x0b2\x0e.APLoginFailedR\x0bloginFailed\"\xe8\
|
\x12>\n\x16login_crypto_challenge\x18\n\x20\x02(\x0b2\x1a.LoginCryptoCha\
|
||||||
\x02\n\x0bAPChallenge\x12P\n\x16login_crypto_challenge\x18\n\x20\x02(\
|
llengeUnionB\x02\x18\0\x12=\n\x15fingerprint_challenge\x18\x14\x20\x02(\
|
||||||
\x0b2\x1a.LoginCryptoChallengeUnionR\x14loginCryptoChallenge\x12O\n\x15f\
|
\x0b2\x1a.FingerprintChallengeUnionB\x02\x18\0\x12-\n\rpow_challenge\x18\
|
||||||
ingerprint_challenge\x18\x14\x20\x02(\x0b2\x1a.FingerprintChallengeUnion\
|
\x1e\x20\x02(\x0b2\x12.PoWChallengeUnionB\x02\x18\0\x123\n\x10crypto_cha\
|
||||||
R\x14fingerprintChallenge\x127\n\rpow_challenge\x18\x1e\x20\x02(\x0b2\
|
llenge\x18(\x20\x02(\x0b2\x15.CryptoChallengeUnionB\x02\x18\0\x12\x18\n\
|
||||||
\x12.PoWChallengeUnionR\x0cpowChallenge\x12@\n\x10crypto_challenge\x18(\
|
\x0cserver_nonce\x182\x20\x02(\x0cB\x02\x18\0\x12\x13\n\x07padding\x18<\
|
||||||
\x20\x02(\x0b2\x15.CryptoChallengeUnionR\x0fcryptoChallenge\x12!\n\x0cse\
|
\x20\x01(\x0cB\x02\x18\0\"[\n\x19LoginCryptoChallengeUnion\x12>\n\x0edif\
|
||||||
rver_nonce\x182\x20\x02(\x0cR\x0bserverNonce\x12\x18\n\x07padding\x18<\
|
fie_hellman\x18\n\x20\x01(\x0b2\".LoginCryptoDiffieHellmanChallengeB\x02\
|
||||||
\x20\x01(\x0cR\x07padding\"f\n\x19LoginCryptoChallengeUnion\x12I\n\x0edi\
|
\x18\0\"o\n!LoginCryptoDiffieHellmanChallenge\x12\x0e\n\x02gs\x18\n\x20\
|
||||||
ffie_hellman\x18\n\x20\x01(\x0b2\".LoginCryptoDiffieHellmanChallengeR\rd\
|
\x02(\x0cB\x02\x18\0\x12\x20\n\x14server_signature_key\x18\x14\x20\x02(\
|
||||||
iffieHellman\"\x88\x01\n!LoginCryptoDiffieHellmanChallenge\x12\x0e\n\x02\
|
\x05B\x02\x18\0\x12\x18\n\x0cgs_signature\x18\x1e\x20\x02(\x0cB\x02\x18\
|
||||||
gs\x18\n\x20\x02(\x0cR\x02gs\x120\n\x14server_signature_key\x18\x14\x20\
|
\0\"\x84\x01\n\x19FingerprintChallengeUnion\x12-\n\x05grain\x18\n\x20\
|
||||||
\x02(\x05R\x12serverSignatureKey\x12!\n\x0cgs_signature\x18\x1e\x20\x02(\
|
\x01(\x0b2\x1a.FingerprintGrainChallengeB\x02\x18\0\x128\n\x0bhmac_ripem\
|
||||||
\x0cR\x0bgsSignature\"\x8f\x01\n\x19FingerprintChallengeUnion\x120\n\x05\
|
d\x18\x14\x20\x01(\x0b2\x1f.FingerprintHmacRipemdChallengeB\x02\x18\0\",\
|
||||||
grain\x18\n\x20\x01(\x0b2\x1a.FingerprintGrainChallengeR\x05grain\x12@\n\
|
\n\x19FingerprintGrainChallenge\x12\x0f\n\x03kek\x18\n\x20\x02(\x0cB\x02\
|
||||||
\x0bhmac_ripemd\x18\x14\x20\x01(\x0b2\x1f.FingerprintHmacRipemdChallenge\
|
\x18\0\"7\n\x1eFingerprintHmacRipemdChallenge\x12\x15\n\tchallenge\x18\n\
|
||||||
R\nhmacRipemd\"-\n\x19FingerprintGrainChallenge\x12\x10\n\x03kek\x18\n\
|
\x20\x02(\x0cB\x02\x18\0\"A\n\x11PoWChallengeUnion\x12,\n\thash_cash\x18\
|
||||||
\x20\x02(\x0cR\x03kek\">\n\x1eFingerprintHmacRipemdChallenge\x12\x1c\n\t\
|
\n\x20\x01(\x0b2\x15.PoWHashCashChallengeB\x02\x18\0\"R\n\x14PoWHashCash\
|
||||||
challenge\x18\n\x20\x02(\x0cR\tchallenge\"G\n\x11PoWChallengeUnion\x122\
|
Challenge\x12\x12\n\x06prefix\x18\n\x20\x01(\x0cB\x02\x18\0\x12\x12\n\
|
||||||
\n\thash_cash\x18\n\x20\x01(\x0b2\x15.PoWHashCashChallengeR\x08hashCash\
|
\x06length\x18\x14\x20\x01(\x05B\x02\x18\0\x12\x12\n\x06target\x18\x1e\
|
||||||
\"^\n\x14PoWHashCashChallenge\x12\x16\n\x06prefix\x18\n\x20\x01(\x0cR\
|
\x20\x01(\x05B\x02\x18\0\"|\n\x14CryptoChallengeUnion\x12,\n\x07shannon\
|
||||||
\x06prefix\x12\x16\n\x06length\x18\x14\x20\x01(\x05R\x06length\x12\x16\n\
|
\x18\n\x20\x01(\x0b2\x17.CryptoShannonChallengeB\x02\x18\0\x126\n\rrc4_s\
|
||||||
\x06target\x18\x1e\x20\x01(\x05R\x06target\"\x8a\x01\n\x14CryptoChalleng\
|
ha1_hmac\x18\x14\x20\x01(\x0b2\x1b.CryptoRc4Sha1HmacChallengeB\x02\x18\0\
|
||||||
eUnion\x121\n\x07shannon\x18\n\x20\x01(\x0b2\x17.CryptoShannonChallengeR\
|
\"\x18\n\x16CryptoShannonChallenge\"\x1c\n\x1aCryptoRc4Sha1HmacChallenge\
|
||||||
\x07shannon\x12?\n\rrc4_sha1_hmac\x18\x14\x20\x01(\x0b2\x1b.CryptoRc4Sha\
|
\"i\n\x16UpgradeRequiredMessage\x12\x1f\n\x13upgrade_signed_part\x18\n\
|
||||||
1HmacChallengeR\x0brc4Sha1Hmac\"\x18\n\x16CryptoShannonChallenge\"\x1c\n\
|
\x20\x02(\x0cB\x02\x18\0\x12\x15\n\tsignature\x18\x14\x20\x02(\x0cB\x02\
|
||||||
\x1aCryptoRc4Sha1HmacChallenge\"\x87\x01\n\x16UpgradeRequiredMessage\x12\
|
\x18\0\x12\x17\n\x0bhttp_suffix\x18\x1e\x20\x01(\tB\x02\x18\0\"\x7f\n\rA\
|
||||||
.\n\x13upgrade_signed_part\x18\n\x20\x02(\x0cR\x11upgradeSignedPart\x12\
|
PLoginFailed\x12\"\n\nerror_code\x18\n\x20\x02(\x0e2\n.ErrorCodeB\x02\
|
||||||
\x1c\n\tsignature\x18\x14\x20\x02(\x0cR\tsignature\x12\x1f\n\x0bhttp_suf\
|
\x18\0\x12\x17\n\x0bretry_delay\x18\x14\x20\x01(\x05B\x02\x18\0\x12\x12\
|
||||||
fix\x18\x1e\x20\x01(\tR\nhttpSuffix\"\xa0\x01\n\rAPLoginFailed\x12)\n\ne\
|
\n\x06expiry\x18\x1e\x20\x01(\x05B\x02\x18\0\x12\x1d\n\x11error_descript\
|
||||||
rror_code\x18\n\x20\x02(\x0e2\n.ErrorCodeR\terrorCode\x12\x1f\n\x0bretry\
|
ion\x18(\x20\x01(\tB\x02\x18\0\"\xb7\x01\n\x17ClientResponsePlaintext\
|
||||||
_delay\x18\x14\x20\x01(\x05R\nretryDelay\x12\x16\n\x06expiry\x18\x1e\x20\
|
\x12<\n\x15login_crypto_response\x18\n\x20\x02(\x0b2\x19.LoginCryptoResp\
|
||||||
\x01(\x05R\x06expiry\x12+\n\x11error_description\x18(\x20\x01(\tR\x10err\
|
onseUnionB\x02\x18\0\x12+\n\x0cpow_response\x18\x14\x20\x02(\x0b2\x11.Po\
|
||||||
orDescription\"\xdd\x01\n\x17ClientResponsePlaintext\x12M\n\x15login_cry\
|
WResponseUnionB\x02\x18\0\x121\n\x0fcrypto_response\x18\x1e\x20\x02(\x0b\
|
||||||
pto_response\x18\n\x20\x02(\x0b2\x19.LoginCryptoResponseUnionR\x13loginC\
|
2\x14.CryptoResponseUnionB\x02\x18\0\"Y\n\x18LoginCryptoResponseUnion\
|
||||||
ryptoResponse\x124\n\x0cpow_response\x18\x14\x20\x02(\x0b2\x11.PoWRespon\
|
\x12=\n\x0ediffie_hellman\x18\n\x20\x01(\x0b2!.LoginCryptoDiffieHellmanR\
|
||||||
seUnionR\x0bpowResponse\x12=\n\x0fcrypto_response\x18\x1e\x20\x02(\x0b2\
|
esponseB\x02\x18\0\"4\n\x20LoginCryptoDiffieHellmanResponse\x12\x10\n\
|
||||||
\x14.CryptoResponseUnionR\x0ecryptoResponse\"d\n\x18LoginCryptoResponseU\
|
\x04hmac\x18\n\x20\x02(\x0cB\x02\x18\0\"?\n\x10PoWResponseUnion\x12+\n\t\
|
||||||
nion\x12H\n\x0ediffie_hellman\x18\n\x20\x01(\x0b2!.LoginCryptoDiffieHell\
|
hash_cash\x18\n\x20\x01(\x0b2\x14.PoWHashCashResponseB\x02\x18\0\".\n\
|
||||||
manResponseR\rdiffieHellman\"6\n\x20LoginCryptoDiffieHellmanResponse\x12\
|
\x13PoWHashCashResponse\x12\x17\n\x0bhash_suffix\x18\n\x20\x02(\x0cB\x02\
|
||||||
\x12\n\x04hmac\x18\n\x20\x02(\x0cR\x04hmac\"E\n\x10PoWResponseUnion\x121\
|
\x18\0\"y\n\x13CryptoResponseUnion\x12+\n\x07shannon\x18\n\x20\x01(\x0b2\
|
||||||
\n\thash_cash\x18\n\x20\x01(\x0b2\x14.PoWHashCashResponseR\x08hashCash\"\
|
\x16.CryptoShannonResponseB\x02\x18\0\x125\n\rrc4_sha1_hmac\x18\x14\x20\
|
||||||
6\n\x13PoWHashCashResponse\x12\x1f\n\x0bhash_suffix\x18\n\x20\x02(\x0cR\
|
\x01(\x0b2\x1a.CryptoRc4Sha1HmacResponseB\x02\x18\0\"*\n\x15CryptoShanno\
|
||||||
\nhashSuffix\"\x87\x01\n\x13CryptoResponseUnion\x120\n\x07shannon\x18\n\
|
nResponse\x12\x11\n\x05dummy\x18\x01\x20\x01(\x05B\x02\x18\0\".\n\x19Cry\
|
||||||
\x20\x01(\x0b2\x16.CryptoShannonResponseR\x07shannon\x12>\n\rrc4_sha1_hm\
|
ptoRc4Sha1HmacResponse\x12\x11\n\x05dummy\x18\x01\x20\x01(\x05B\x02\x18\
|
||||||
ac\x18\x14\x20\x01(\x0b2\x1a.CryptoRc4Sha1HmacResponseR\x0brc4Sha1Hmac\"\
|
\0*\x83\x01\n\x07Product\x12\x12\n\x0ePRODUCT_CLIENT\x10\0\x12\x16\n\x12\
|
||||||
-\n\x15CryptoShannonResponse\x12\x14\n\x05dummy\x18\x01\x20\x01(\x05R\
|
PRODUCT_LIBSPOTIFY\x10\x01\x12\x12\n\x0ePRODUCT_MOBILE\x10\x02\x12\x13\n\
|
||||||
\x05dummy\"1\n\x19CryptoRc4Sha1HmacResponse\x12\x14\n\x05dummy\x18\x01\
|
\x0fPRODUCT_PARTNER\x10\x03\x12\x1f\n\x1bPRODUCT_LIBSPOTIFY_EMBEDDED\x10\
|
||||||
\x20\x01(\x05R\x05dummy*\x7f\n\x07Product\x12\x12\n\x0ePRODUCT_CLIENT\
|
\x05\x1a\x02\x10\0*E\n\x0cProductFlags\x12\x15\n\x11PRODUCT_FLAG_NONE\
|
||||||
\x10\0\x12\x16\n\x12PRODUCT_LIBSPOTIFY\x10\x01\x12\x12\n\x0ePRODUCT_MOBI\
|
\x10\0\x12\x1a\n\x16PRODUCT_FLAG_DEV_BUILD\x10\x01\x1a\x02\x10\0*\xe0\
|
||||||
LE\x10\x02\x12\x13\n\x0fPRODUCT_PARTNER\x10\x03\x12\x1f\n\x1bPRODUCT_LIB\
|
\x04\n\x08Platform\x12\x16\n\x12PLATFORM_WIN32_X86\x10\0\x12\x14\n\x10PL\
|
||||||
SPOTIFY_EMBEDDED\x10\x05*A\n\x0cProductFlags\x12\x15\n\x11PRODUCT_FLAG_N\
|
ATFORM_OSX_X86\x10\x01\x12\x16\n\x12PLATFORM_LINUX_X86\x10\x02\x12\x17\n\
|
||||||
ONE\x10\0\x12\x1a\n\x16PRODUCT_FLAG_DEV_BUILD\x10\x01*\xdc\x04\n\x08Plat\
|
\x13PLATFORM_IPHONE_ARM\x10\x03\x12\x14\n\x10PLATFORM_S60_ARM\x10\x04\
|
||||||
form\x12\x16\n\x12PLATFORM_WIN32_X86\x10\0\x12\x14\n\x10PLATFORM_OSX_X86\
|
\x12\x14\n\x10PLATFORM_OSX_PPC\x10\x05\x12\x18\n\x14PLATFORM_ANDROID_ARM\
|
||||||
\x10\x01\x12\x16\n\x12PLATFORM_LINUX_X86\x10\x02\x12\x17\n\x13PLATFORM_I\
|
\x10\x06\x12\x1b\n\x17PLATFORM_WINDOWS_CE_ARM\x10\x07\x12\x19\n\x15PLATF\
|
||||||
PHONE_ARM\x10\x03\x12\x14\n\x10PLATFORM_S60_ARM\x10\x04\x12\x14\n\x10PLA\
|
ORM_LINUX_X86_64\x10\x08\x12\x17\n\x13PLATFORM_OSX_X86_64\x10\t\x12\x15\
|
||||||
TFORM_OSX_PPC\x10\x05\x12\x18\n\x14PLATFORM_ANDROID_ARM\x10\x06\x12\x1b\
|
\n\x11PLATFORM_PALM_ARM\x10\n\x12\x15\n\x11PLATFORM_LINUX_SH\x10\x0b\x12\
|
||||||
\n\x17PLATFORM_WINDOWS_CE_ARM\x10\x07\x12\x19\n\x15PLATFORM_LINUX_X86_64\
|
\x18\n\x14PLATFORM_FREEBSD_X86\x10\x0c\x12\x1b\n\x17PLATFORM_FREEBSD_X86\
|
||||||
\x10\x08\x12\x17\n\x13PLATFORM_OSX_X86_64\x10\t\x12\x15\n\x11PLATFORM_PA\
|
_64\x10\r\x12\x1b\n\x17PLATFORM_BLACKBERRY_ARM\x10\x0e\x12\x12\n\x0ePLAT\
|
||||||
LM_ARM\x10\n\x12\x15\n\x11PLATFORM_LINUX_SH\x10\x0b\x12\x18\n\x14PLATFOR\
|
FORM_SONOS\x10\x0f\x12\x17\n\x13PLATFORM_LINUX_MIPS\x10\x10\x12\x16\n\
|
||||||
M_FREEBSD_X86\x10\x0c\x12\x1b\n\x17PLATFORM_FREEBSD_X86_64\x10\r\x12\x1b\
|
\x12PLATFORM_LINUX_ARM\x10\x11\x12\x19\n\x15PLATFORM_LOGITECH_ARM\x10\
|
||||||
\n\x17PLATFORM_BLACKBERRY_ARM\x10\x0e\x12\x12\n\x0ePLATFORM_SONOS\x10\
|
\x12\x12\x1b\n\x17PLATFORM_LINUX_BLACKFIN\x10\x13\x12\x14\n\x10PLATFORM_\
|
||||||
\x0f\x12\x17\n\x13PLATFORM_LINUX_MIPS\x10\x10\x12\x16\n\x12PLATFORM_LINU\
|
WP7_ARM\x10\x14\x12\x16\n\x12PLATFORM_ONKYO_ARM\x10\x15\x12\x17\n\x13PLA\
|
||||||
X_ARM\x10\x11\x12\x19\n\x15PLATFORM_LOGITECH_ARM\x10\x12\x12\x1b\n\x17PL\
|
TFORM_QNXNTO_ARM\x10\x16\x12\x14\n\x10PLATFORM_BCO_ARM\x10\x17\x1a\x02\
|
||||||
ATFORM_LINUX_BLACKFIN\x10\x13\x12\x14\n\x10PLATFORM_WP7_ARM\x10\x14\x12\
|
\x10\0*E\n\x0bFingerprint\x12\x15\n\x11FINGERPRINT_GRAIN\x10\0\x12\x1b\n\
|
||||||
\x16\n\x12PLATFORM_ONKYO_ARM\x10\x15\x12\x17\n\x13PLATFORM_QNXNTO_ARM\
|
\x17FINGERPRINT_HMAC_RIPEMD\x10\x01\x1a\x02\x10\0*K\n\x0bCryptosuite\x12\
|
||||||
\x10\x16\x12\x14\n\x10PLATFORM_BCO_ARM\x10\x17*A\n\x0bFingerprint\x12\
|
\x18\n\x14CRYPTO_SUITE_SHANNON\x10\0\x12\x1e\n\x1aCRYPTO_SUITE_RC4_SHA1_\
|
||||||
\x15\n\x11FINGERPRINT_GRAIN\x10\0\x12\x1b\n\x17FINGERPRINT_HMAC_RIPEMD\
|
HMAC\x10\x01\x1a\x02\x10\0*\"\n\tPowscheme\x12\x11\n\rPOW_HASH_CASH\x10\
|
||||||
\x10\x01*G\n\x0bCryptosuite\x12\x18\n\x14CRYPTO_SUITE_SHANNON\x10\0\x12\
|
\0\x1a\x02\x10\0*\x8d\x02\n\tErrorCode\x12\x11\n\rProtocolError\x10\0\
|
||||||
\x1e\n\x1aCRYPTO_SUITE_RC4_SHA1_HMAC\x10\x01*\x1e\n\tPowscheme\x12\x11\n\
|
\x12\x10\n\x0cTryAnotherAP\x10\x02\x12\x13\n\x0fBadConnectionId\x10\x05\
|
||||||
\rPOW_HASH_CASH\x10\0*\x89\x02\n\tErrorCode\x12\x11\n\rProtocolError\x10\
|
\x12\x15\n\x11TravelRestriction\x10\t\x12\x1a\n\x16PremiumAccountRequire\
|
||||||
\0\x12\x10\n\x0cTryAnotherAP\x10\x02\x12\x13\n\x0fBadConnectionId\x10\
|
d\x10\x0b\x12\x12\n\x0eBadCredentials\x10\x0c\x12\x1f\n\x1bCouldNotValid\
|
||||||
\x05\x12\x15\n\x11TravelRestriction\x10\t\x12\x1a\n\x16PremiumAccountReq\
|
ateCredentials\x10\r\x12\x11\n\rAccountExists\x10\x0e\x12\x1d\n\x19Extra\
|
||||||
uired\x10\x0b\x12\x12\n\x0eBadCredentials\x10\x0c\x12\x1f\n\x1bCouldNotV\
|
VerificationRequired\x10\x0f\x12\x11\n\rInvalidAppKey\x10\x10\x12\x15\n\
|
||||||
alidateCredentials\x10\r\x12\x11\n\rAccountExists\x10\x0e\x12\x1d\n\x19E\
|
\x11ApplicationBanned\x10\x11\x1a\x02\x10\0B\0b\x06proto2\
|
||||||
xtraVerificationRequired\x10\x0f\x12\x11\n\rInvalidAppKey\x10\x10\x12\
|
|
||||||
\x15\n\x11ApplicationBanned\x10\x11\
|
|
||||||
";
|
";
|
||||||
|
|
||||||
static mut file_descriptor_proto_lazy: ::protobuf::lazy::Lazy<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::lazy::Lazy {
|
static mut file_descriptor_proto_lazy: ::protobuf::lazy::Lazy<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::lazy::Lazy {
|
||||||
|
|
|
@ -1775,26 +1775,27 @@ impl ::protobuf::reflect::ProtobufValue for UserField {
|
||||||
}
|
}
|
||||||
|
|
||||||
static file_descriptor_proto_data: &'static [u8] = b"\
|
static file_descriptor_proto_data: &'static [u8] = b"\
|
||||||
\n\rmercury.proto\"C\n\x16MercuryMultiGetRequest\x12)\n\x07request\x18\
|
\n\rmercury.proto\x12\0\">\n\x16MercuryMultiGetRequest\x12$\n\x07request\
|
||||||
\x01\x20\x03(\x0b2\x0f.MercuryRequestR\x07request\";\n\x14MercuryMultiGe\
|
\x18\x01\x20\x03(\x0b2\x0f.MercuryRequestB\x02\x18\0\"8\n\x14MercuryMult\
|
||||||
tReply\x12#\n\x05reply\x18\x01\x20\x03(\x0b2\r.MercuryReplyR\x05reply\"m\
|
iGetReply\x12\x20\n\x05reply\x18\x01\x20\x03(\x0b2\r.MercuryReplyB\x02\
|
||||||
\n\x0eMercuryRequest\x12\x10\n\x03uri\x18\x01\x20\x01(\tR\x03uri\x12!\n\
|
\x18\0\"_\n\x0eMercuryRequest\x12\x0f\n\x03uri\x18\x01\x20\x01(\tB\x02\
|
||||||
\x0ccontent_type\x18\x02\x20\x01(\tR\x0bcontentType\x12\x12\n\x04body\
|
\x18\0\x12\x18\n\x0ccontent_type\x18\x02\x20\x01(\tB\x02\x18\0\x12\x10\n\
|
||||||
\x18\x03\x20\x01(\x0cR\x04body\x12\x12\n\x04etag\x18\x04\x20\x01(\x0cR\
|
\x04body\x18\x03\x20\x01(\x0cB\x02\x18\0\x12\x10\n\x04etag\x18\x04\x20\
|
||||||
\x04etag\"\xb3\x02\n\x0cMercuryReply\x12\x1f\n\x0bstatus_code\x18\x01\
|
\x01(\x0cB\x02\x18\0\"\x8d\x02\n\x0cMercuryReply\x12\x17\n\x0bstatus_cod\
|
||||||
\x20\x01(\x11R\nstatusCode\x12%\n\x0estatus_message\x18\x02\x20\x01(\tR\
|
e\x18\x01\x20\x01(\x11B\x02\x18\0\x12\x1a\n\x0estatus_message\x18\x02\
|
||||||
\rstatusMessage\x12<\n\x0ccache_policy\x18\x03\x20\x01(\x0e2\x19.Mercury\
|
\x20\x01(\tB\x02\x18\0\x123\n\x0ccache_policy\x18\x03\x20\x01(\x0e2\x19.\
|
||||||
Reply.CachePolicyR\x0bcachePolicy\x12\x10\n\x03ttl\x18\x04\x20\x01(\x11R\
|
MercuryReply.CachePolicyB\x02\x18\0\x12\x0f\n\x03ttl\x18\x04\x20\x01(\
|
||||||
\x03ttl\x12\x12\n\x04etag\x18\x05\x20\x01(\x0cR\x04etag\x12!\n\x0cconten\
|
\x11B\x02\x18\0\x12\x10\n\x04etag\x18\x05\x20\x01(\x0cB\x02\x18\0\x12\
|
||||||
t_type\x18\x06\x20\x01(\tR\x0bcontentType\x12\x12\n\x04body\x18\x07\x20\
|
\x18\n\x0ccontent_type\x18\x06\x20\x01(\tB\x02\x18\0\x12\x10\n\x04body\
|
||||||
\x01(\x0cR\x04body\"@\n\x0bCachePolicy\x12\x0c\n\x08CACHE_NO\x10\x01\x12\
|
\x18\x07\x20\x01(\x0cB\x02\x18\0\"D\n\x0bCachePolicy\x12\x0c\n\x08CACHE_\
|
||||||
\x11\n\rCACHE_PRIVATE\x10\x02\x12\x10\n\x0cCACHE_PUBLIC\x10\x03\"\xa3\
|
NO\x10\x01\x12\x11\n\rCACHE_PRIVATE\x10\x02\x12\x10\n\x0cCACHE_PUBLIC\
|
||||||
\x01\n\x06Header\x12\x10\n\x03uri\x18\x01\x20\x01(\tR\x03uri\x12!\n\x0cc\
|
\x10\x03\x1a\x02\x10\0\"\x85\x01\n\x06Header\x12\x0f\n\x03uri\x18\x01\
|
||||||
ontent_type\x18\x02\x20\x01(\tR\x0bcontentType\x12\x16\n\x06method\x18\
|
\x20\x01(\tB\x02\x18\0\x12\x18\n\x0ccontent_type\x18\x02\x20\x01(\tB\x02\
|
||||||
\x03\x20\x01(\tR\x06method\x12\x1f\n\x0bstatus_code\x18\x04\x20\x01(\x11\
|
\x18\0\x12\x12\n\x06method\x18\x03\x20\x01(\tB\x02\x18\0\x12\x17\n\x0bst\
|
||||||
R\nstatusCode\x12+\n\x0buser_fields\x18\x06\x20\x03(\x0b2\n.UserFieldR\n\
|
atus_code\x18\x04\x20\x01(\x11B\x02\x18\0\x12#\n\x0buser_fields\x18\x06\
|
||||||
userFields\"3\n\tUserField\x12\x10\n\x03key\x18\x01\x20\x01(\tR\x03key\
|
\x20\x03(\x0b2\n.UserFieldB\x02\x18\0\"/\n\tUserField\x12\x0f\n\x03key\
|
||||||
\x12\x14\n\x05value\x18\x02\x20\x01(\x0cR\x05value\
|
\x18\x01\x20\x01(\tB\x02\x18\0\x12\x11\n\x05value\x18\x02\x20\x01(\x0cB\
|
||||||
|
\x02\x18\0B\0b\x06proto2\
|
||||||
";
|
";
|
||||||
|
|
||||||
static mut file_descriptor_proto_lazy: ::protobuf::lazy::Lazy<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::lazy::Lazy {
|
static mut file_descriptor_proto_lazy: ::protobuf::lazy::Lazy<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::lazy::Lazy {
|
||||||
|
|
|
@ -6093,91 +6093,92 @@ impl ::protobuf::reflect::ProtobufValue for AudioFile_Format {
|
||||||
}
|
}
|
||||||
|
|
||||||
static file_descriptor_proto_data: &'static [u8] = b"\
|
static file_descriptor_proto_data: &'static [u8] = b"\
|
||||||
\n\x0emetadata.proto\"C\n\tTopTracks\x12\x18\n\x07country\x18\x01\x20\
|
\n\x0emetadata.proto\x12\0\";\n\tTopTracks\x12\x13\n\x07country\x18\x01\
|
||||||
\x01(\tR\x07country\x12\x1c\n\x05track\x18\x02\x20\x03(\x0b2\x06.TrackR\
|
\x20\x01(\tB\x02\x18\0\x12\x19\n\x05track\x18\x02\x20\x03(\x0b2\x06.Trac\
|
||||||
\x05track\"b\n\x0eActivityPeriod\x12\x1d\n\nstart_year\x18\x01\x20\x01(\
|
kB\x02\x18\0\"R\n\x0eActivityPeriod\x12\x16\n\nstart_year\x18\x01\x20\
|
||||||
\x11R\tstartYear\x12\x19\n\x08end_year\x18\x02\x20\x01(\x11R\x07endYear\
|
\x01(\x11B\x02\x18\0\x12\x14\n\x08end_year\x18\x02\x20\x01(\x11B\x02\x18\
|
||||||
\x12\x16\n\x06decade\x18\x03\x20\x01(\x11R\x06decade\"\xd0\x05\n\x06Arti\
|
\0\x12\x12\n\x06decade\x18\x03\x20\x01(\x11B\x02\x18\0\"\xc5\x04\n\x06Ar\
|
||||||
st\x12\x10\n\x03gid\x18\x01\x20\x01(\x0cR\x03gid\x12\x12\n\x04name\x18\
|
tist\x12\x0f\n\x03gid\x18\x01\x20\x01(\x0cB\x02\x18\0\x12\x10\n\x04name\
|
||||||
\x02\x20\x01(\tR\x04name\x12\x1e\n\npopularity\x18\x03\x20\x01(\x11R\npo\
|
\x18\x02\x20\x01(\tB\x02\x18\0\x12\x16\n\npopularity\x18\x03\x20\x01(\
|
||||||
pularity\x12'\n\ttop_track\x18\x04\x20\x03(\x0b2\n.TopTracksR\x08topTrac\
|
\x11B\x02\x18\0\x12!\n\ttop_track\x18\x04\x20\x03(\x0b2\n.TopTracksB\x02\
|
||||||
k\x12,\n\x0balbum_group\x18\x05\x20\x03(\x0b2\x0b.AlbumGroupR\nalbumGrou\
|
\x18\0\x12$\n\x0balbum_group\x18\x05\x20\x03(\x0b2\x0b.AlbumGroupB\x02\
|
||||||
p\x12.\n\x0csingle_group\x18\x06\x20\x03(\x0b2\x0b.AlbumGroupR\x0bsingle\
|
\x18\0\x12%\n\x0csingle_group\x18\x06\x20\x03(\x0b2\x0b.AlbumGroupB\x02\
|
||||||
Group\x128\n\x11compilation_group\x18\x07\x20\x03(\x0b2\x0b.AlbumGroupR\
|
\x18\0\x12*\n\x11compilation_group\x18\x07\x20\x03(\x0b2\x0b.AlbumGroupB\
|
||||||
\x10compilationGroup\x125\n\x10appears_on_group\x18\x08\x20\x03(\x0b2\
|
\x02\x18\0\x12)\n\x10appears_on_group\x18\x08\x20\x03(\x0b2\x0b.AlbumGro\
|
||||||
\x0b.AlbumGroupR\x0eappearsOnGroup\x12\x14\n\x05genre\x18\t\x20\x03(\tR\
|
upB\x02\x18\0\x12\x11\n\x05genre\x18\t\x20\x03(\tB\x02\x18\0\x12$\n\x0be\
|
||||||
\x05genre\x12,\n\x0bexternal_id\x18\n\x20\x03(\x0b2\x0b.ExternalIdR\next\
|
xternal_id\x18\n\x20\x03(\x0b2\x0b.ExternalIdB\x02\x18\0\x12\x1c\n\x08po\
|
||||||
ernalId\x12\"\n\x08portrait\x18\x0b\x20\x03(\x0b2\x06.ImageR\x08portrait\
|
rtrait\x18\x0b\x20\x03(\x0b2\x06.ImageB\x02\x18\0\x12!\n\tbiography\x18\
|
||||||
\x12(\n\tbiography\x18\x0c\x20\x03(\x0b2\n.BiographyR\tbiography\x128\n\
|
\x0c\x20\x03(\x0b2\n.BiographyB\x02\x18\0\x12,\n\x0factivity_period\x18\
|
||||||
\x0factivity_period\x18\r\x20\x03(\x0b2\x0f.ActivityPeriodR\x0eactivityP\
|
\r\x20\x03(\x0b2\x0f.ActivityPeriodB\x02\x18\0\x12%\n\x0brestriction\x18\
|
||||||
eriod\x12.\n\x0brestriction\x18\x0e\x20\x03(\x0b2\x0c.RestrictionR\x0bre\
|
\x0e\x20\x03(\x0b2\x0c.RestrictionB\x02\x18\0\x12\x1c\n\x07related\x18\
|
||||||
striction\x12!\n\x07related\x18\x0f\x20\x03(\x0b2\x07.ArtistR\x07related\
|
\x0f\x20\x03(\x0b2\x07.ArtistB\x02\x18\0\x12#\n\x17is_portrait_album_cov\
|
||||||
\x125\n\x17is_portrait_album_cover\x18\x10\x20\x01(\x08R\x14isPortraitAl\
|
er\x18\x10\x20\x01(\x08B\x02\x18\0\x12'\n\x0eportrait_group\x18\x11\x20\
|
||||||
bumCover\x122\n\x0eportrait_group\x18\x11\x20\x01(\x0b2\x0b.ImageGroupR\
|
\x01(\x0b2\x0b.ImageGroupB\x02\x18\0\"'\n\nAlbumGroup\x12\x19\n\x05album\
|
||||||
\rportraitGroup\"*\n\nAlbumGroup\x12\x1c\n\x05album\x18\x01\x20\x03(\x0b\
|
\x18\x01\x20\x03(\x0b2\x06.AlbumB\x02\x18\0\"<\n\x04Date\x12\x10\n\x04ye\
|
||||||
2\x06.AlbumR\x05album\"B\n\x04Date\x12\x12\n\x04year\x18\x01\x20\x01(\
|
ar\x18\x01\x20\x01(\x11B\x02\x18\0\x12\x11\n\x05month\x18\x02\x20\x01(\
|
||||||
\x11R\x04year\x12\x14\n\x05month\x18\x02\x20\x01(\x11R\x05month\x12\x10\
|
\x11B\x02\x18\0\x12\x0f\n\x03day\x18\x03\x20\x01(\x11B\x02\x18\0\"\x99\
|
||||||
\n\x03day\x18\x03\x20\x01(\x11R\x03day\"\xe3\x04\n\x05Album\x12\x10\n\
|
\x04\n\x05Album\x12\x0f\n\x03gid\x18\x01\x20\x01(\x0cB\x02\x18\0\x12\x10\
|
||||||
\x03gid\x18\x01\x20\x01(\x0cR\x03gid\x12\x12\n\x04name\x18\x02\x20\x01(\
|
\n\x04name\x18\x02\x20\x01(\tB\x02\x18\0\x12\x1b\n\x06artist\x18\x03\x20\
|
||||||
\tR\x04name\x12\x1f\n\x06artist\x18\x03\x20\x03(\x0b2\x07.ArtistR\x06art\
|
\x03(\x0b2\x07.ArtistB\x02\x18\0\x12\x1c\n\x03typ\x18\x04\x20\x01(\x0e2\
|
||||||
ist\x12\x1d\n\x03typ\x18\x04\x20\x01(\x0e2\x0b.Album.TypeR\x03typ\x12\
|
\x0b.Album.TypeB\x02\x18\0\x12\x11\n\x05label\x18\x05\x20\x01(\tB\x02\
|
||||||
\x14\n\x05label\x18\x05\x20\x01(\tR\x05label\x12\x19\n\x04date\x18\x06\
|
\x18\0\x12\x17\n\x04date\x18\x06\x20\x01(\x0b2\x05.DateB\x02\x18\0\x12\
|
||||||
\x20\x01(\x0b2\x05.DateR\x04date\x12\x1e\n\npopularity\x18\x07\x20\x01(\
|
\x16\n\npopularity\x18\x07\x20\x01(\x11B\x02\x18\0\x12\x11\n\x05genre\
|
||||||
\x11R\npopularity\x12\x14\n\x05genre\x18\x08\x20\x03(\tR\x05genre\x12\
|
\x18\x08\x20\x03(\tB\x02\x18\0\x12\x19\n\x05cover\x18\t\x20\x03(\x0b2\
|
||||||
\x1c\n\x05cover\x18\t\x20\x03(\x0b2\x06.ImageR\x05cover\x12,\n\x0bextern\
|
\x06.ImageB\x02\x18\0\x12$\n\x0bexternal_id\x18\n\x20\x03(\x0b2\x0b.Exte\
|
||||||
al_id\x18\n\x20\x03(\x0b2\x0b.ExternalIdR\nexternalId\x12\x19\n\x04disc\
|
rnalIdB\x02\x18\0\x12\x17\n\x04disc\x18\x0b\x20\x03(\x0b2\x05.DiscB\x02\
|
||||||
\x18\x0b\x20\x03(\x0b2\x05.DiscR\x04disc\x12\x16\n\x06review\x18\x0c\x20\
|
\x18\0\x12\x12\n\x06review\x18\x0c\x20\x03(\tB\x02\x18\0\x12!\n\tcopyrig\
|
||||||
\x03(\tR\x06review\x12(\n\tcopyright\x18\r\x20\x03(\x0b2\n.CopyrightR\tc\
|
ht\x18\r\x20\x03(\x0b2\n.CopyrightB\x02\x18\0\x12%\n\x0brestriction\x18\
|
||||||
opyright\x12.\n\x0brestriction\x18\x0e\x20\x03(\x0b2\x0c.RestrictionR\
|
\x0e\x20\x03(\x0b2\x0c.RestrictionB\x02\x18\0\x12\x1b\n\x07related\x18\
|
||||||
\x0brestriction\x12\x20\n\x07related\x18\x0f\x20\x03(\x0b2\x06.AlbumR\
|
\x0f\x20\x03(\x0b2\x06.AlbumB\x02\x18\0\x12$\n\x0bsale_period\x18\x10\
|
||||||
\x07related\x12,\n\x0bsale_period\x18\x10\x20\x03(\x0b2\x0b.SalePeriodR\
|
\x20\x03(\x0b2\x0b.SalePeriodB\x02\x18\0\x12$\n\x0bcover_group\x18\x11\
|
||||||
\nsalePeriod\x12,\n\x0bcover_group\x18\x11\x20\x01(\x0b2\x0b.ImageGroupR\
|
\x20\x01(\x0b2\x0b.ImageGroupB\x02\x18\0\":\n\x04Type\x12\t\n\x05ALBUM\
|
||||||
\ncoverGroup\"6\n\x04Type\x12\t\n\x05ALBUM\x10\x01\x12\n\n\x06SINGLE\x10\
|
\x10\x01\x12\n\n\x06SINGLE\x10\x02\x12\x0f\n\x0bCOMPILATION\x10\x03\x12\
|
||||||
\x02\x12\x0f\n\x0bCOMPILATION\x10\x03\x12\x06\n\x02EP\x10\x04\"\xf9\x03\
|
\x06\n\x02EP\x10\x04\x1a\x02\x10\0\"\xa6\x03\n\x05Track\x12\x0f\n\x03gid\
|
||||||
\n\x05Track\x12\x10\n\x03gid\x18\x01\x20\x01(\x0cR\x03gid\x12\x12\n\x04n\
|
\x18\x01\x20\x01(\x0cB\x02\x18\0\x12\x10\n\x04name\x18\x02\x20\x01(\tB\
|
||||||
ame\x18\x02\x20\x01(\tR\x04name\x12\x1c\n\x05album\x18\x03\x20\x01(\x0b2\
|
\x02\x18\0\x12\x19\n\x05album\x18\x03\x20\x01(\x0b2\x06.AlbumB\x02\x18\0\
|
||||||
\x06.AlbumR\x05album\x12\x1f\n\x06artist\x18\x04\x20\x03(\x0b2\x07.Artis\
|
\x12\x1b\n\x06artist\x18\x04\x20\x03(\x0b2\x07.ArtistB\x02\x18\0\x12\x12\
|
||||||
tR\x06artist\x12\x16\n\x06number\x18\x05\x20\x01(\x11R\x06number\x12\x1f\
|
\n\x06number\x18\x05\x20\x01(\x11B\x02\x18\0\x12\x17\n\x0bdisc_number\
|
||||||
\n\x0bdisc_number\x18\x06\x20\x01(\x11R\ndiscNumber\x12\x1a\n\x08duratio\
|
\x18\x06\x20\x01(\x11B\x02\x18\0\x12\x14\n\x08duration\x18\x07\x20\x01(\
|
||||||
n\x18\x07\x20\x01(\x11R\x08duration\x12\x1e\n\npopularity\x18\x08\x20\
|
\x11B\x02\x18\0\x12\x16\n\npopularity\x18\x08\x20\x01(\x11B\x02\x18\0\
|
||||||
\x01(\x11R\npopularity\x12\x1a\n\x08explicit\x18\t\x20\x01(\x08R\x08expl\
|
\x12\x14\n\x08explicit\x18\t\x20\x01(\x08B\x02\x18\0\x12$\n\x0bexternal_\
|
||||||
icit\x12,\n\x0bexternal_id\x18\n\x20\x03(\x0b2\x0b.ExternalIdR\nexternal\
|
id\x18\n\x20\x03(\x0b2\x0b.ExternalIdB\x02\x18\0\x12%\n\x0brestriction\
|
||||||
Id\x12.\n\x0brestriction\x18\x0b\x20\x03(\x0b2\x0c.RestrictionR\x0brestr\
|
\x18\x0b\x20\x03(\x0b2\x0c.RestrictionB\x02\x18\0\x12\x1c\n\x04file\x18\
|
||||||
iction\x12\x1e\n\x04file\x18\x0c\x20\x03(\x0b2\n.AudioFileR\x04file\x12(\
|
\x0c\x20\x03(\x0b2\n.AudioFileB\x02\x18\0\x12\x1f\n\x0balternative\x18\r\
|
||||||
\n\x0balternative\x18\r\x20\x03(\x0b2\x06.TrackR\x0balternative\x12,\n\
|
\x20\x03(\x0b2\x06.TrackB\x02\x18\0\x12$\n\x0bsale_period\x18\x0e\x20\
|
||||||
\x0bsale_period\x18\x0e\x20\x03(\x0b2\x0b.SalePeriodR\nsalePeriod\x12$\n\
|
\x03(\x0b2\x0b.SalePeriodB\x02\x18\0\x12\x1f\n\x07preview\x18\x0f\x20\
|
||||||
\x07preview\x18\x0f\x20\x03(\x0b2\n.AudioFileR\x07preview\"\xa6\x01\n\
|
\x03(\x0b2\n.AudioFileB\x02\x18\0\"\x9d\x01\n\x05Image\x12\x13\n\x07file\
|
||||||
\x05Image\x12\x17\n\x07file_id\x18\x01\x20\x01(\x0cR\x06fileId\x12\x1f\n\
|
_id\x18\x01\x20\x01(\x0cB\x02\x18\0\x12\x1d\n\x04size\x18\x02\x20\x01(\
|
||||||
\x04size\x18\x02\x20\x01(\x0e2\x0b.Image.SizeR\x04size\x12\x14\n\x05widt\
|
\x0e2\x0b.Image.SizeB\x02\x18\0\x12\x11\n\x05width\x18\x03\x20\x01(\x11B\
|
||||||
h\x18\x03\x20\x01(\x11R\x05width\x12\x16\n\x06height\x18\x04\x20\x01(\
|
\x02\x18\0\x12\x12\n\x06height\x18\x04\x20\x01(\x11B\x02\x18\0\"9\n\x04S\
|
||||||
\x11R\x06height\"5\n\x04Size\x12\x0b\n\x07DEFAULT\x10\0\x12\t\n\x05SMALL\
|
ize\x12\x0b\n\x07DEFAULT\x10\0\x12\t\n\x05SMALL\x10\x01\x12\t\n\x05LARGE\
|
||||||
\x10\x01\x12\t\n\x05LARGE\x10\x02\x12\n\n\x06XLARGE\x10\x03\"*\n\nImageG\
|
\x10\x02\x12\n\n\x06XLARGE\x10\x03\x1a\x02\x10\0\"'\n\nImageGroup\x12\
|
||||||
roup\x12\x1c\n\x05image\x18\x01\x20\x03(\x0b2\x06.ImageR\x05image\"w\n\t\
|
\x19\n\x05image\x18\x01\x20\x03(\x0b2\x06.ImageB\x02\x18\0\"d\n\tBiograp\
|
||||||
Biography\x12\x12\n\x04text\x18\x01\x20\x01(\tR\x04text\x12\"\n\x08portr\
|
hy\x12\x10\n\x04text\x18\x01\x20\x01(\tB\x02\x18\0\x12\x1c\n\x08portrait\
|
||||||
ait\x18\x02\x20\x03(\x0b2\x06.ImageR\x08portrait\x122\n\x0eportrait_grou\
|
\x18\x02\x20\x03(\x0b2\x06.ImageB\x02\x18\0\x12'\n\x0eportrait_group\x18\
|
||||||
p\x18\x03\x20\x03(\x0b2\x0b.ImageGroupR\rportraitGroup\"P\n\x04Disc\x12\
|
\x03\x20\x03(\x0b2\x0b.ImageGroupB\x02\x18\0\"G\n\x04Disc\x12\x12\n\x06n\
|
||||||
\x16\n\x06number\x18\x01\x20\x01(\x11R\x06number\x12\x12\n\x04name\x18\
|
umber\x18\x01\x20\x01(\x11B\x02\x18\0\x12\x10\n\x04name\x18\x02\x20\x01(\
|
||||||
\x02\x20\x01(\tR\x04name\x12\x1c\n\x05track\x18\x03\x20\x03(\x0b2\x06.Tr\
|
\tB\x02\x18\0\x12\x19\n\x05track\x18\x03\x20\x03(\x0b2\x06.TrackB\x02\
|
||||||
ackR\x05track\"X\n\tCopyright\x12!\n\x03typ\x18\x01\x20\x01(\x0e2\x0f.Co\
|
\x18\0\"Y\n\tCopyright\x12\x20\n\x03typ\x18\x01\x20\x01(\x0e2\x0f.Copyri\
|
||||||
pyright.TypeR\x03typ\x12\x12\n\x04text\x18\x02\x20\x01(\tR\x04text\"\x14\
|
ght.TypeB\x02\x18\0\x12\x10\n\x04text\x18\x02\x20\x01(\tB\x02\x18\0\"\
|
||||||
\n\x04Type\x12\x05\n\x01P\x10\0\x12\x05\n\x01C\x10\x01\"\xcc\x01\n\x0bRe\
|
\x18\n\x04Type\x12\x05\n\x01P\x10\0\x12\x05\n\x01C\x10\x01\x1a\x02\x10\0\
|
||||||
striction\x12+\n\x11countries_allowed\x18\x02\x20\x01(\tR\x10countriesAl\
|
\"\xa7\x01\n\x0bRestriction\x12\x1d\n\x11countries_allowed\x18\x02\x20\
|
||||||
lowed\x12/\n\x13countries_forbidden\x18\x03\x20\x01(\tR\x12countriesForb\
|
\x01(\tB\x02\x18\0\x12\x1f\n\x13countries_forbidden\x18\x03\x20\x01(\tB\
|
||||||
idden\x12#\n\x03typ\x18\x04\x20\x01(\x0e2\x11.Restriction.TypeR\x03typ\
|
\x02\x18\0\x12\"\n\x03typ\x18\x04\x20\x01(\x0e2\x11.Restriction.TypeB\
|
||||||
\x12#\n\rcatalogue_str\x18\x05\x20\x03(\tR\x0ccatalogueStr\"\x15\n\x04Ty\
|
\x02\x18\0\x12\x19\n\rcatalogue_str\x18\x05\x20\x03(\tB\x02\x18\0\"\x19\
|
||||||
pe\x12\r\n\tSTREAMING\x10\0\"r\n\nSalePeriod\x12.\n\x0brestriction\x18\
|
\n\x04Type\x12\r\n\tSTREAMING\x10\0\x1a\x02\x10\0\"e\n\nSalePeriod\x12%\
|
||||||
\x01\x20\x03(\x0b2\x0c.RestrictionR\x0brestriction\x12\x1b\n\x05start\
|
\n\x0brestriction\x18\x01\x20\x03(\x0b2\x0c.RestrictionB\x02\x18\0\x12\
|
||||||
\x18\x02\x20\x01(\x0b2\x05.DateR\x05start\x12\x17\n\x03end\x18\x03\x20\
|
\x18\n\x05start\x18\x02\x20\x01(\x0b2\x05.DateB\x02\x18\0\x12\x16\n\x03e\
|
||||||
\x01(\x0b2\x05.DateR\x03end\".\n\nExternalId\x12\x10\n\x03typ\x18\x01\
|
nd\x18\x03\x20\x01(\x0b2\x05.DateB\x02\x18\0\"-\n\nExternalId\x12\x0f\n\
|
||||||
\x20\x01(\tR\x03typ\x12\x0e\n\x02id\x18\x02\x20\x01(\tR\x02id\"\xa3\x02\
|
\x03typ\x18\x01\x20\x01(\tB\x02\x18\0\x12\x0e\n\x02id\x18\x02\x20\x01(\t\
|
||||||
\n\tAudioFile\x12\x17\n\x07file_id\x18\x01\x20\x01(\x0cR\x06fileId\x12)\
|
B\x02\x18\0\"\x9f\x02\n\tAudioFile\x12\x13\n\x07file_id\x18\x01\x20\x01(\
|
||||||
\n\x06format\x18\x02\x20\x01(\x0e2\x11.AudioFile.FormatR\x06format\"\xd1\
|
\x0cB\x02\x18\0\x12%\n\x06format\x18\x02\x20\x01(\x0e2\x11.AudioFile.For\
|
||||||
\x01\n\x06Format\x12\x11\n\rOGG_VORBIS_96\x10\0\x12\x12\n\x0eOGG_VORBIS_\
|
matB\x02\x18\0\"\xd5\x01\n\x06Format\x12\x11\n\rOGG_VORBIS_96\x10\0\x12\
|
||||||
160\x10\x01\x12\x12\n\x0eOGG_VORBIS_320\x10\x02\x12\x0b\n\x07MP3_256\x10\
|
\x12\n\x0eOGG_VORBIS_160\x10\x01\x12\x12\n\x0eOGG_VORBIS_320\x10\x02\x12\
|
||||||
\x03\x12\x0b\n\x07MP3_320\x10\x04\x12\x0b\n\x07MP3_160\x10\x05\x12\n\n\
|
\x0b\n\x07MP3_256\x10\x03\x12\x0b\n\x07MP3_320\x10\x04\x12\x0b\n\x07MP3_\
|
||||||
\x06MP3_96\x10\x06\x12\x0f\n\x0bMP3_160_ENC\x10\x07\x12\n\n\x06OTHER2\
|
160\x10\x05\x12\n\n\x06MP3_96\x10\x06\x12\x0f\n\x0bMP3_160_ENC\x10\x07\
|
||||||
\x10\x08\x12\n\n\x06OTHER3\x10\t\x12\x0b\n\x07AAC_160\x10\n\x12\x0b\n\
|
\x12\n\n\x06OTHER2\x10\x08\x12\n\n\x06OTHER3\x10\t\x12\x0b\n\x07AAC_160\
|
||||||
\x07AAC_320\x10\x0b\x12\n\n\x06OTHER4\x10\x0c\x12\n\n\x06OTHER5\x10\r\
|
\x10\n\x12\x0b\n\x07AAC_320\x10\x0b\x12\n\n\x06OTHER4\x10\x0c\x12\n\n\
|
||||||
|
\x06OTHER5\x10\r\x1a\x02\x10\0B\0b\x06proto2\
|
||||||
";
|
";
|
||||||
|
|
||||||
static mut file_descriptor_proto_lazy: ::protobuf::lazy::Lazy<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::lazy::Lazy {
|
static mut file_descriptor_proto_lazy: ::protobuf::lazy::Lazy<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::lazy::Lazy {
|
||||||
|
|
|
@ -273,9 +273,10 @@ impl ::protobuf::reflect::ProtobufValue for Subscription {
|
||||||
}
|
}
|
||||||
|
|
||||||
static file_descriptor_proto_data: &'static [u8] = b"\
|
static file_descriptor_proto_data: &'static [u8] = b"\
|
||||||
\n\x0cpubsub.proto\"Y\n\x0cSubscription\x12\x10\n\x03uri\x18\x01\x20\x01\
|
\n\x0cpubsub.proto\x12\0\"L\n\x0cSubscription\x12\x0f\n\x03uri\x18\x01\
|
||||||
(\tR\x03uri\x12\x16\n\x06expiry\x18\x02\x20\x01(\x05R\x06expiry\x12\x1f\
|
\x20\x01(\tB\x02\x18\0\x12\x12\n\x06expiry\x18\x02\x20\x01(\x05B\x02\x18\
|
||||||
\n\x0bstatus_code\x18\x03\x20\x01(\x05R\nstatusCode\
|
\0\x12\x17\n\x0bstatus_code\x18\x03\x20\x01(\x05B\x02\x18\0B\0b\x06proto\
|
||||||
|
2\
|
||||||
";
|
";
|
||||||
|
|
||||||
static mut file_descriptor_proto_lazy: ::protobuf::lazy::Lazy<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::lazy::Lazy {
|
static mut file_descriptor_proto_lazy: ::protobuf::lazy::Lazy<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::lazy::Lazy {
|
||||||
|
|
|
@ -4002,79 +4002,79 @@ impl ::protobuf::reflect::ProtobufValue for PlayStatus {
|
||||||
}
|
}
|
||||||
|
|
||||||
static file_descriptor_proto_data: &'static [u8] = b"\
|
static file_descriptor_proto_data: &'static [u8] = b"\
|
||||||
\n\x0bspirc.proto\"\xfa\x03\n\x05Frame\x12\x18\n\x07version\x18\x01\x20\
|
\n\x0bspirc.proto\x12\0\"\x99\x03\n\x05Frame\x12\x13\n\x07version\x18\
|
||||||
\x01(\rR\x07version\x12\x14\n\x05ident\x18\x02\x20\x01(\tR\x05ident\x12)\
|
\x01\x20\x01(\rB\x02\x18\0\x12\x11\n\x05ident\x18\x02\x20\x01(\tB\x02\
|
||||||
\n\x10protocol_version\x18\x03\x20\x01(\tR\x0fprotocolVersion\x12\x15\n\
|
\x18\0\x12\x1c\n\x10protocol_version\x18\x03\x20\x01(\tB\x02\x18\0\x12\
|
||||||
\x06seq_nr\x18\x04\x20\x01(\rR\x05seqNr\x12\x1e\n\x03typ\x18\x05\x20\x01\
|
\x12\n\x06seq_nr\x18\x04\x20\x01(\rB\x02\x18\0\x12\x1d\n\x03typ\x18\x05\
|
||||||
(\x0e2\x0c.MessageTypeR\x03typ\x12/\n\x0cdevice_state\x18\x07\x20\x01(\
|
\x20\x01(\x0e2\x0c.MessageTypeB\x02\x18\0\x12&\n\x0cdevice_state\x18\x07\
|
||||||
\x0b2\x0c.DeviceStateR\x0bdeviceState\x12\"\n\x07goodbye\x18\x0b\x20\x01\
|
\x20\x01(\x0b2\x0c.DeviceStateB\x02\x18\0\x12\x1d\n\x07goodbye\x18\x0b\
|
||||||
(\x0b2\x08.GoodbyeR\x07goodbye\x12\x1c\n\x05state\x18\x0c\x20\x01(\x0b2\
|
\x20\x01(\x0b2\x08.GoodbyeB\x02\x18\0\x12\x19\n\x05state\x18\x0c\x20\x01\
|
||||||
\x06.StateR\x05state\x12\x1a\n\x08position\x18\r\x20\x01(\rR\x08position\
|
(\x0b2\x06.StateB\x02\x18\0\x12\x14\n\x08position\x18\r\x20\x01(\rB\x02\
|
||||||
\x12\x16\n\x06volume\x18\x0e\x20\x01(\rR\x06volume\x12&\n\x0fstate_updat\
|
\x18\0\x12\x12\n\x06volume\x18\x0e\x20\x01(\rB\x02\x18\0\x12\x1b\n\x0fst\
|
||||||
e_id\x18\x11\x20\x01(\x03R\rstateUpdateId\x12\x1c\n\trecipient\x18\x12\
|
ate_update_id\x18\x11\x20\x01(\x03B\x02\x18\0\x12\x15\n\trecipient\x18\
|
||||||
\x20\x03(\tR\trecipient\x120\n\x14context_player_state\x18\x13\x20\x01(\
|
\x12\x20\x03(\tB\x02\x18\0\x12\x20\n\x14context_player_state\x18\x13\x20\
|
||||||
\x0cR\x12contextPlayerState\x12\x19\n\x08new_name\x18\x14\x20\x01(\tR\
|
\x01(\x0cB\x02\x18\0\x12\x14\n\x08new_name\x18\x14\x20\x01(\tB\x02\x18\0\
|
||||||
\x07newName\x12%\n\x08metadata\x18\x19\x20\x01(\x0b2\t.MetadataR\x08meta\
|
\x12\x1f\n\x08metadata\x18\x19\x20\x01(\x0b2\t.MetadataB\x02\x18\0\"\xb3\
|
||||||
data\"\x88\x03\n\x0bDeviceState\x12\x1d\n\nsw_version\x18\x01\x20\x01(\t\
|
\x02\n\x0bDeviceState\x12\x16\n\nsw_version\x18\x01\x20\x01(\tB\x02\x18\
|
||||||
R\tswVersion\x12\x1b\n\tis_active\x18\n\x20\x01(\x08R\x08isActive\x12\
|
\0\x12\x15\n\tis_active\x18\n\x20\x01(\x08B\x02\x18\0\x12\x14\n\x08can_p\
|
||||||
\x19\n\x08can_play\x18\x0b\x20\x01(\x08R\x07canPlay\x12\x16\n\x06volume\
|
lay\x18\x0b\x20\x01(\x08B\x02\x18\0\x12\x12\n\x06volume\x18\x0c\x20\x01(\
|
||||||
\x18\x0c\x20\x01(\rR\x06volume\x12\x12\n\x04name\x18\r\x20\x01(\tR\x04na\
|
\rB\x02\x18\0\x12\x10\n\x04name\x18\r\x20\x01(\tB\x02\x18\0\x12\x16\n\ne\
|
||||||
me\x12\x1d\n\nerror_code\x18\x0e\x20\x01(\rR\terrorCode\x12(\n\x10became\
|
rror_code\x18\x0e\x20\x01(\rB\x02\x18\0\x12\x1c\n\x10became_active_at\
|
||||||
_active_at\x18\x0f\x20\x01(\x03R\x0ebecameActiveAt\x12#\n\rerror_message\
|
\x18\x0f\x20\x01(\x03B\x02\x18\0\x12\x19\n\rerror_message\x18\x10\x20\
|
||||||
\x18\x10\x20\x01(\tR\x0cerrorMessage\x12/\n\x0ccapabilities\x18\x11\x20\
|
\x01(\tB\x02\x18\0\x12%\n\x0ccapabilities\x18\x11\x20\x03(\x0b2\x0b.Capa\
|
||||||
\x03(\x0b2\x0b.CapabilityR\x0ccapabilities\x120\n\x14context_player_erro\
|
bilityB\x02\x18\0\x12\x20\n\x14context_player_error\x18\x14\x20\x01(\tB\
|
||||||
r\x18\x14\x20\x01(\tR\x12contextPlayerError\x12%\n\x08metadata\x18\x19\
|
\x02\x18\0\x12\x1f\n\x08metadata\x18\x19\x20\x03(\x0b2\t.MetadataB\x02\
|
||||||
\x20\x03(\x0b2\t.MetadataR\x08metadata\"m\n\nCapability\x12!\n\x03typ\
|
\x18\0\"]\n\nCapability\x12\x20\n\x03typ\x18\x01\x20\x01(\x0e2\x0f.Capab\
|
||||||
\x18\x01\x20\x01(\x0e2\x0f.CapabilityTypeR\x03typ\x12\x1a\n\x08intValue\
|
ilityTypeB\x02\x18\0\x12\x14\n\x08intValue\x18\x02\x20\x03(\x03B\x02\x18\
|
||||||
\x18\x02\x20\x03(\x03R\x08intValue\x12\x20\n\x0bstringValue\x18\x03\x20\
|
\0\x12\x17\n\x0bstringValue\x18\x03\x20\x03(\tB\x02\x18\0\"\x1d\n\x07Goo\
|
||||||
\x03(\tR\x0bstringValue\"!\n\x07Goodbye\x12\x16\n\x06reason\x18\x01\x20\
|
dbye\x12\x12\n\x06reason\x18\x01\x20\x01(\tB\x02\x18\0\"\xa1\x03\n\x05St\
|
||||||
\x01(\tR\x06reason\"\xa1\x04\n\x05State\x12\x1f\n\x0bcontext_uri\x18\x02\
|
ate\x12\x17\n\x0bcontext_uri\x18\x02\x20\x01(\tB\x02\x18\0\x12\x11\n\x05\
|
||||||
\x20\x01(\tR\ncontextUri\x12\x14\n\x05index\x18\x03\x20\x01(\rR\x05index\
|
index\x18\x03\x20\x01(\rB\x02\x18\0\x12\x17\n\x0bposition_ms\x18\x04\x20\
|
||||||
\x12\x1f\n\x0bposition_ms\x18\x04\x20\x01(\rR\npositionMs\x12#\n\x06stat\
|
\x01(\rB\x02\x18\0\x12\x1f\n\x06status\x18\x05\x20\x01(\x0e2\x0b.PlaySta\
|
||||||
us\x18\x05\x20\x01(\x0e2\x0b.PlayStatusR\x06status\x120\n\x14position_me\
|
tusB\x02\x18\0\x12\x20\n\x14position_measured_at\x18\x07\x20\x01(\x04B\
|
||||||
asured_at\x18\x07\x20\x01(\x04R\x12positionMeasuredAt\x12/\n\x13context_\
|
\x02\x18\0\x12\x1f\n\x13context_description\x18\x08\x20\x01(\tB\x02\x18\
|
||||||
description\x18\x08\x20\x01(\tR\x12contextDescription\x12\x18\n\x07shuff\
|
\0\x12\x13\n\x07shuffle\x18\r\x20\x01(\x08B\x02\x18\0\x12\x12\n\x06repea\
|
||||||
le\x18\r\x20\x01(\x08R\x07shuffle\x12\x16\n\x06repeat\x18\x0e\x20\x01(\
|
t\x18\x0e\x20\x01(\x08B\x02\x18\0\x12\x1e\n\x12last_command_ident\x18\
|
||||||
\x08R\x06repeat\x12,\n\x12last_command_ident\x18\x14\x20\x01(\tR\x10last\
|
\x14\x20\x01(\tB\x02\x18\0\x12\x1e\n\x12last_command_msgid\x18\x15\x20\
|
||||||
CommandIdent\x12,\n\x12last_command_msgid\x18\x15\x20\x01(\rR\x10lastCom\
|
\x01(\rB\x02\x18\0\x12!\n\x15playing_from_fallback\x18\x18\x20\x01(\x08B\
|
||||||
mandMsgid\x122\n\x15playing_from_fallback\x18\x18\x20\x01(\x08R\x13playi\
|
\x02\x18\0\x12\x0f\n\x03row\x18\x19\x20\x01(\rB\x02\x18\0\x12\x1f\n\x13p\
|
||||||
ngFromFallback\x12\x10\n\x03row\x18\x19\x20\x01(\rR\x03row\x12.\n\x13pla\
|
laying_track_index\x18\x1a\x20\x01(\rB\x02\x18\0\x12\x1c\n\x05track\x18\
|
||||||
ying_track_index\x18\x1a\x20\x01(\rR\x11playingTrackIndex\x12\x1f\n\x05t\
|
\x1b\x20\x03(\x0b2\t.TrackRefB\x02\x18\0\x12\x13\n\x02ad\x18\x1c\x20\x01\
|
||||||
rack\x18\x1b\x20\x03(\x0b2\t.TrackRefR\x05track\x12\x13\n\x02ad\x18\x1c\
|
(\x0b2\x03.AdB\x02\x18\0\"U\n\x08TrackRef\x12\x0f\n\x03gid\x18\x01\x20\
|
||||||
\x20\x01(\x0b2\x03.AdR\x02ad\"`\n\x08TrackRef\x12\x10\n\x03gid\x18\x01\
|
\x01(\x0cB\x02\x18\0\x12\x0f\n\x03uri\x18\x02\x20\x01(\tB\x02\x18\0\x12\
|
||||||
\x20\x01(\x0cR\x03gid\x12\x10\n\x03uri\x18\x02\x20\x01(\tR\x03uri\x12\
|
\x12\n\x06queued\x18\x03\x20\x01(\x08B\x02\x18\0\x12\x13\n\x07context\
|
||||||
\x16\n\x06queued\x18\x03\x20\x01(\x08R\x06queued\x12\x18\n\x07context\
|
\x18\x04\x20\x01(\tB\x02\x18\0\"\xc9\x01\n\x02Ad\x12\x10\n\x04next\x18\
|
||||||
\x18\x04\x20\x01(\tR\x07context\"\xfa\x01\n\x02Ad\x12\x12\n\x04next\x18\
|
\x01\x20\x01(\x05B\x02\x18\0\x12\x13\n\x07ogg_fid\x18\x02\x20\x01(\x0cB\
|
||||||
\x01\x20\x01(\x05R\x04next\x12\x17\n\x07ogg_fid\x18\x02\x20\x01(\x0cR\
|
\x02\x18\0\x12\x15\n\timage_fid\x18\x03\x20\x01(\x0cB\x02\x18\0\x12\x14\
|
||||||
\x06oggFid\x12\x1b\n\timage_fid\x18\x03\x20\x01(\x0cR\x08imageFid\x12\
|
\n\x08duration\x18\x04\x20\x01(\x05B\x02\x18\0\x12\x15\n\tclick_url\x18\
|
||||||
\x1a\n\x08duration\x18\x04\x20\x01(\x05R\x08duration\x12\x1b\n\tclick_ur\
|
\x05\x20\x01(\tB\x02\x18\0\x12\x1a\n\x0eimpression_url\x18\x06\x20\x01(\
|
||||||
l\x18\x05\x20\x01(\tR\x08clickUrl\x12%\n\x0eimpression_url\x18\x06\x20\
|
\tB\x02\x18\0\x12\x13\n\x07product\x18\x07\x20\x01(\tB\x02\x18\0\x12\x16\
|
||||||
\x01(\tR\rimpressionUrl\x12\x18\n\x07product\x18\x07\x20\x01(\tR\x07prod\
|
\n\nadvertiser\x18\x08\x20\x01(\tB\x02\x18\0\x12\x0f\n\x03gid\x18\t\x20\
|
||||||
uct\x12\x1e\n\nadvertiser\x18\x08\x20\x01(\tR\nadvertiser\x12\x10\n\x03g\
|
\x01(\x0cB\x02\x18\0\"2\n\x08Metadata\x12\x10\n\x04type\x18\x01\x20\x01(\
|
||||||
id\x18\t\x20\x01(\x0cR\x03gid\":\n\x08Metadata\x12\x12\n\x04type\x18\x01\
|
\tB\x02\x18\0\x12\x14\n\x08metadata\x18\x02\x20\x01(\tB\x02\x18\0*\x91\
|
||||||
\x20\x01(\tR\x04type\x12\x1a\n\x08metadata\x18\x02\x20\x01(\tR\x08metada\
|
\x04\n\x0bMessageType\x12\x15\n\x11kMessageTypeHello\x10\x01\x12\x17\n\
|
||||||
ta*\x8d\x04\n\x0bMessageType\x12\x15\n\x11kMessageTypeHello\x10\x01\x12\
|
\x13kMessageTypeGoodbye\x10\x02\x12\x15\n\x11kMessageTypeProbe\x10\x03\
|
||||||
\x17\n\x13kMessageTypeGoodbye\x10\x02\x12\x15\n\x11kMessageTypeProbe\x10\
|
\x12\x16\n\x12kMessageTypeNotify\x10\n\x12\x14\n\x10kMessageTypeLoad\x10\
|
||||||
\x03\x12\x16\n\x12kMessageTypeNotify\x10\n\x12\x14\n\x10kMessageTypeLoad\
|
\x14\x12\x14\n\x10kMessageTypePlay\x10\x15\x12\x15\n\x11kMessageTypePaus\
|
||||||
\x10\x14\x12\x14\n\x10kMessageTypePlay\x10\x15\x12\x15\n\x11kMessageType\
|
e\x10\x16\x12\x19\n\x15kMessageTypePlayPause\x10\x17\x12\x14\n\x10kMessa\
|
||||||
Pause\x10\x16\x12\x19\n\x15kMessageTypePlayPause\x10\x17\x12\x14\n\x10kM\
|
geTypeSeek\x10\x18\x12\x14\n\x10kMessageTypePrev\x10\x19\x12\x14\n\x10kM\
|
||||||
essageTypeSeek\x10\x18\x12\x14\n\x10kMessageTypePrev\x10\x19\x12\x14\n\
|
essageTypeNext\x10\x1a\x12\x16\n\x12kMessageTypeVolume\x10\x1b\x12\x17\n\
|
||||||
\x10kMessageTypeNext\x10\x1a\x12\x16\n\x12kMessageTypeVolume\x10\x1b\x12\
|
\x13kMessageTypeShuffle\x10\x1c\x12\x16\n\x12kMessageTypeRepeat\x10\x1d\
|
||||||
\x17\n\x13kMessageTypeShuffle\x10\x1c\x12\x16\n\x12kMessageTypeRepeat\
|
\x12\x1a\n\x16kMessageTypeVolumeDown\x10\x1f\x12\x18\n\x14kMessageTypeVo\
|
||||||
\x10\x1d\x12\x1a\n\x16kMessageTypeVolumeDown\x10\x1f\x12\x18\n\x14kMessa\
|
lumeUp\x10\x20\x12\x17\n\x13kMessageTypeReplace\x10!\x12\x16\n\x12kMessa\
|
||||||
geTypeVolumeUp\x10\x20\x12\x17\n\x13kMessageTypeReplace\x10!\x12\x16\n\
|
geTypeLogout\x10\"\x12\x16\n\x12kMessageTypeAction\x10#\x12\x16\n\x12kMe\
|
||||||
\x12kMessageTypeLogout\x10\"\x12\x16\n\x12kMessageTypeAction\x10#\x12\
|
ssageTypeRename\x10$\x12\x1f\n\x1akMessageTypeUpdateMetadata\x10\x80\x01\
|
||||||
\x16\n\x12kMessageTypeRename\x10$\x12\x1f\n\x1akMessageTypeUpdateMetadat\
|
\x1a\x02\x10\0*\xb6\x02\n\x0eCapabilityType\x12\x16\n\x12kSupportedConte\
|
||||||
a\x10\x80\x01*\xb2\x02\n\x0eCapabilityType\x12\x16\n\x12kSupportedContex\
|
xts\x10\x01\x12\x10\n\x0ckCanBePlayer\x10\x02\x12\x14\n\x10kRestrictToLo\
|
||||||
ts\x10\x01\x12\x10\n\x0ckCanBePlayer\x10\x02\x12\x14\n\x10kRestrictToLoc\
|
cal\x10\x03\x12\x0f\n\x0bkDeviceType\x10\x04\x12\x14\n\x10kGaiaEqConnect\
|
||||||
al\x10\x03\x12\x0f\n\x0bkDeviceType\x10\x04\x12\x14\n\x10kGaiaEqConnectI\
|
Id\x10\x05\x12\x13\n\x0fkSupportsLogout\x10\x06\x12\x11\n\rkIsObservable\
|
||||||
d\x10\x05\x12\x13\n\x0fkSupportsLogout\x10\x06\x12\x11\n\rkIsObservable\
|
|
||||||
\x10\x07\x12\x10\n\x0ckVolumeSteps\x10\x08\x12\x13\n\x0fkSupportedTypes\
|
\x10\x07\x12\x10\n\x0ckVolumeSteps\x10\x08\x12\x13\n\x0fkSupportedTypes\
|
||||||
\x10\t\x12\x10\n\x0ckCommandAcks\x10\n\x12\x13\n\x0fkSupportsRename\x10\
|
\x10\t\x12\x10\n\x0ckCommandAcks\x10\n\x12\x13\n\x0fkSupportsRename\x10\
|
||||||
\x0b\x12\x0b\n\x07kHidden\x10\x0c\x12\x17\n\x13kSupportsPlaylistV2\x10\r\
|
\x0b\x12\x0b\n\x07kHidden\x10\x0c\x12\x17\n\x13kSupportsPlaylistV2\x10\r\
|
||||||
\x12\x1d\n\x19kSupportsExternalEpisodes\x10\x0e*d\n\nPlayStatus\x12\x13\
|
\x12\x1d\n\x19kSupportsExternalEpisodes\x10\x0e\x1a\x02\x10\0*h\n\nPlayS\
|
||||||
\n\x0fkPlayStatusStop\x10\0\x12\x13\n\x0fkPlayStatusPlay\x10\x01\x12\x14\
|
tatus\x12\x13\n\x0fkPlayStatusStop\x10\0\x12\x13\n\x0fkPlayStatusPlay\
|
||||||
\n\x10kPlayStatusPause\x10\x02\x12\x16\n\x12kPlayStatusLoading\x10\x03\
|
\x10\x01\x12\x14\n\x10kPlayStatusPause\x10\x02\x12\x16\n\x12kPlayStatusL\
|
||||||
|
oading\x10\x03\x1a\x02\x10\0B\0b\x06proto2\
|
||||||
";
|
";
|
||||||
|
|
||||||
static mut file_descriptor_proto_lazy: ::protobuf::lazy::Lazy<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::lazy::Lazy {
|
static mut file_descriptor_proto_lazy: ::protobuf::lazy::Lazy<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::lazy::Lazy {
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
#![cfg_attr(feature = "cargo-clippy", allow(unused_io_amount))]
|
#![cfg_attr(feature = "cargo-clippy", allow(unused_io_amount))]
|
||||||
|
|
||||||
extern crate base64;
|
extern crate base64;
|
||||||
extern crate crypto;
|
|
||||||
extern crate futures;
|
extern crate futures;
|
||||||
extern crate hyper;
|
extern crate hyper;
|
||||||
extern crate num_bigint;
|
extern crate num_bigint;
|
||||||
|
|
16
src/main.rs
16
src/main.rs
|
@ -1,4 +1,3 @@
|
||||||
extern crate crypto;
|
|
||||||
extern crate env_logger;
|
extern crate env_logger;
|
||||||
extern crate futures;
|
extern crate futures;
|
||||||
extern crate getopts;
|
extern crate getopts;
|
||||||
|
@ -11,9 +10,10 @@ extern crate tokio_io;
|
||||||
extern crate tokio_process;
|
extern crate tokio_process;
|
||||||
extern crate tokio_signal;
|
extern crate tokio_signal;
|
||||||
extern crate url;
|
extern crate url;
|
||||||
|
extern crate sha1;
|
||||||
|
extern crate hex;
|
||||||
|
|
||||||
use crypto::digest::Digest;
|
use sha1::{Sha1, Digest};
|
||||||
use crypto::sha1::Sha1;
|
|
||||||
use env_logger::LogBuilder;
|
use env_logger::LogBuilder;
|
||||||
use futures::sync::mpsc::UnboundedReceiver;
|
use futures::sync::mpsc::UnboundedReceiver;
|
||||||
use futures::{Async, Future, Poll, Stream};
|
use futures::{Async, Future, Poll, Stream};
|
||||||
|
@ -44,9 +44,7 @@ mod player_event_handler;
|
||||||
use player_event_handler::run_program_on_events;
|
use player_event_handler::run_program_on_events;
|
||||||
|
|
||||||
fn device_id(name: &str) -> String {
|
fn device_id(name: &str) -> String {
|
||||||
let mut h = Sha1::new();
|
hex::encode(Sha1::digest(name.as_bytes()))
|
||||||
h.input_str(name);
|
|
||||||
h.result_str()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage(program: &str, opts: &getopts::Options) -> String {
|
fn usage(program: &str, opts: &getopts::Options) -> String {
|
||||||
|
@ -202,6 +200,10 @@ fn setup(args: &[String]) -> Setup {
|
||||||
let backend = audio_backend::find(backend_name).expect("Invalid backend");
|
let backend = audio_backend::find(backend_name).expect("Invalid backend");
|
||||||
|
|
||||||
let device = matches.opt_str("device");
|
let device = matches.opt_str("device");
|
||||||
|
if device == Some("?".into()) {
|
||||||
|
backend(device);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
let mixer_name = matches.opt_str("mixer");
|
let mixer_name = matches.opt_str("mixer");
|
||||||
let mixer = mixer::find(mixer_name.as_ref()).expect("Invalid mixer");
|
let mixer = mixer::find(mixer_name.as_ref()).expect("Invalid mixer");
|
||||||
|
@ -445,6 +447,8 @@ impl Future for Main {
|
||||||
if !self.shutdown {
|
if !self.shutdown {
|
||||||
if let Some(ref spirc) = self.spirc {
|
if let Some(ref spirc) = self.spirc {
|
||||||
spirc.shutdown();
|
spirc.shutdown();
|
||||||
|
} else {
|
||||||
|
return Ok(Async::Ready(()));
|
||||||
}
|
}
|
||||||
self.shutdown = true;
|
self.shutdown = true;
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Reference in a new issue