mirror of
https://github.com/librespot-org/librespot.git
synced 2024-11-08 16:45:43 +00:00
spirc: Keep track of player status
This commit is contained in:
parent
df11960946
commit
c1ce87dbbd
1 changed files with 169 additions and 36 deletions
205
src/main.rs
205
src/main.rs
|
@ -48,6 +48,7 @@ use util::version::version_string;
|
||||||
use player::Player;
|
use player::Player;
|
||||||
use mercury::{MercuryRequest, MercuryMethod};
|
use mercury::{MercuryRequest, MercuryMethod};
|
||||||
use librespot_protocol as protocol;
|
use librespot_protocol as protocol;
|
||||||
|
use librespot_protocol::spirc::PlayStatus;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut args = std::env::args().skip(1);
|
let mut args = std::env::args().skip(1);
|
||||||
|
@ -83,6 +84,8 @@ fn main() {
|
||||||
can_play: true,
|
can_play: true,
|
||||||
is_active: false,
|
is_active: false,
|
||||||
became_active_at: 0,
|
became_active_at: 0,
|
||||||
|
|
||||||
|
state: PlayerState::new()
|
||||||
}.run();
|
}.run();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -118,6 +121,88 @@ fn print_track(cache: &mut MetadataCache, track_id: SpotifyId) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct PlayerState {
|
||||||
|
status: PlayStatus,
|
||||||
|
|
||||||
|
context_uri: String,
|
||||||
|
index: u32,
|
||||||
|
queue: Vec<SpotifyId>,
|
||||||
|
|
||||||
|
repeat: bool,
|
||||||
|
shuffle: bool,
|
||||||
|
|
||||||
|
position_ms: u32,
|
||||||
|
position_measured_at: i64,
|
||||||
|
|
||||||
|
last_command_ident: String,
|
||||||
|
last_command_msgid: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PlayerState {
|
||||||
|
fn new() -> PlayerState {
|
||||||
|
PlayerState {
|
||||||
|
status: PlayStatus::kPlayStatusPause,
|
||||||
|
|
||||||
|
context_uri: String::new(),
|
||||||
|
index: 0,
|
||||||
|
queue: Vec::new(),
|
||||||
|
|
||||||
|
repeat: false,
|
||||||
|
shuffle: false,
|
||||||
|
|
||||||
|
position_ms: 0,
|
||||||
|
position_measured_at: 0,
|
||||||
|
|
||||||
|
last_command_ident: String::new(),
|
||||||
|
last_command_msgid: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn import(&mut self, state: &protocol::spirc::State) {
|
||||||
|
//println!("{:?}", state);
|
||||||
|
self.status = state.get_status();
|
||||||
|
|
||||||
|
self.context_uri = state.get_context_uri().to_string();
|
||||||
|
self.index = state.get_playing_track_index();
|
||||||
|
self.queue = state.get_track().iter().filter(|t| {
|
||||||
|
t.has_gid()
|
||||||
|
}).map(|t| {
|
||||||
|
SpotifyId::from_raw(t.get_gid())
|
||||||
|
}).collect();
|
||||||
|
|
||||||
|
self.repeat = state.get_repeat();
|
||||||
|
self.shuffle = state.get_shuffle();
|
||||||
|
|
||||||
|
self.position_ms = state.get_position_ms();
|
||||||
|
self.position_measured_at = SpircManager::now();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn export(&self) -> protocol::spirc::State {
|
||||||
|
protobuf_init!(protocol::spirc::State::new(), {
|
||||||
|
status: self.status,
|
||||||
|
|
||||||
|
context_uri: self.context_uri.to_string(),
|
||||||
|
playing_track_index: self.index,
|
||||||
|
track: self.queue.iter().map(|t| {
|
||||||
|
protobuf_init!(protocol::spirc::TrackRef::new(), {
|
||||||
|
gid: t.to_raw().to_vec()
|
||||||
|
})
|
||||||
|
}).collect(),
|
||||||
|
|
||||||
|
shuffle: self.shuffle,
|
||||||
|
repeat: self.repeat,
|
||||||
|
|
||||||
|
position_ms: self.position_ms,
|
||||||
|
position_measured_at: self.position_measured_at as u64,
|
||||||
|
|
||||||
|
playing_from_fallback: true,
|
||||||
|
|
||||||
|
last_command_ident: self.last_command_ident.clone(),
|
||||||
|
last_command_msgid: self.last_command_msgid
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct SpircManager {
|
struct SpircManager {
|
||||||
session: Session,
|
session: Session,
|
||||||
username: String,
|
username: String,
|
||||||
|
@ -132,6 +217,8 @@ struct SpircManager {
|
||||||
can_play: bool,
|
can_play: bool,
|
||||||
is_active: bool,
|
is_active: bool,
|
||||||
became_active_at: i64,
|
became_active_at: i64,
|
||||||
|
|
||||||
|
state: PlayerState
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SpircManager {
|
impl SpircManager {
|
||||||
|
@ -140,7 +227,7 @@ impl SpircManager {
|
||||||
|
|
||||||
self.session.mercury.send(MercuryRequest{
|
self.session.mercury.send(MercuryRequest{
|
||||||
method: MercuryMethod::SUB,
|
method: MercuryMethod::SUB,
|
||||||
uri: format!("hm://remote/user/{}/v23", self.username).to_string(),
|
uri: format!("hm://remote/user/{}/v23", self.username),
|
||||||
content_type: None,
|
content_type: None,
|
||||||
callback: Some(tx),
|
callback: Some(tx),
|
||||||
payload: Vec::new()
|
payload: Vec::new()
|
||||||
|
@ -148,23 +235,22 @@ impl SpircManager {
|
||||||
|
|
||||||
self.notify(None);
|
self.notify(None);
|
||||||
|
|
||||||
for pkt in rx.iter() {
|
let rx = rx.into_iter().map(|pkt| {
|
||||||
let frame : protocol::spirc::Frame =
|
protobuf::parse_from_bytes::<protocol::spirc::Frame>(pkt.payload.front().unwrap()).unwrap()
|
||||||
protobuf::parse_from_bytes(pkt.payload.front().unwrap()).unwrap();
|
});
|
||||||
|
|
||||||
println!("{:?} {} {} {}",
|
for frame in rx {
|
||||||
|
println!("{:?} {} {} {} {}",
|
||||||
frame.get_typ(),
|
frame.get_typ(),
|
||||||
frame.get_device_state().get_name(),
|
frame.get_device_state().get_name(),
|
||||||
frame.get_ident(),
|
frame.get_ident(),
|
||||||
frame.get_device_state().get_became_active_at());
|
frame.get_seq_nr(),
|
||||||
|
frame.get_state_update_id());
|
||||||
if frame.get_ident() == self.ident ||
|
if frame.get_ident() != self.ident &&
|
||||||
(frame.get_recipient().len() > 0 &&
|
(frame.get_recipient().len() == 0 ||
|
||||||
!frame.get_recipient().contains(&self.ident)) {
|
frame.get_recipient().contains(&self.ident)) {
|
||||||
continue;
|
self.handle(frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.handle(frame);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,37 +261,79 @@ impl SpircManager {
|
||||||
}
|
}
|
||||||
protocol::spirc::MessageType::kMessageTypeLoad => {
|
protocol::spirc::MessageType::kMessageTypeLoad => {
|
||||||
self.is_active = true;
|
self.is_active = true;
|
||||||
self.became_active_at = {
|
self.became_active_at = SpircManager::now();
|
||||||
let ts = time::now_utc().to_timespec();
|
|
||||||
ts.sec * 1000 + ts.nsec as i64 / 1000000
|
self.state.import(frame.get_state());
|
||||||
};
|
|
||||||
println!("{:?} {}", frame, self.became_active_at);
|
self.state.last_command_ident = frame.get_ident().to_string();
|
||||||
self.notify(None)
|
self.state.last_command_msgid = frame.get_seq_nr();
|
||||||
|
|
||||||
|
self.state_update_id = SpircManager::now();
|
||||||
|
self.notify(None);
|
||||||
|
}
|
||||||
|
protocol::spirc::MessageType::kMessageTypePlay => {
|
||||||
|
self.state.status = PlayStatus::kPlayStatusPlay;
|
||||||
|
self.state.position_measured_at = SpircManager::now();
|
||||||
|
|
||||||
|
self.state.last_command_ident = frame.get_ident().to_string();
|
||||||
|
self.state.last_command_msgid = frame.get_seq_nr();
|
||||||
|
|
||||||
|
self.state_update_id = SpircManager::now();
|
||||||
|
self.notify(None);
|
||||||
|
}
|
||||||
|
protocol::spirc::MessageType::kMessageTypePause => {
|
||||||
|
self.state.status = PlayStatus::kPlayStatusPause;
|
||||||
|
self.state.position_measured_at = SpircManager::now();
|
||||||
|
|
||||||
|
self.state.last_command_ident = frame.get_ident().to_string();
|
||||||
|
self.state.last_command_msgid = frame.get_seq_nr();
|
||||||
|
|
||||||
|
self.state_update_id = SpircManager::now();
|
||||||
|
self.notify(None);
|
||||||
|
}
|
||||||
|
protocol::spirc::MessageType::kMessageTypeSeek => {
|
||||||
|
self.state.position_ms = frame.get_position();
|
||||||
|
self.state.position_measured_at = SpircManager::now();
|
||||||
|
|
||||||
|
self.state.last_command_ident = frame.get_ident().to_string();
|
||||||
|
self.state.last_command_msgid = frame.get_seq_nr();
|
||||||
|
|
||||||
|
self.state_update_id = SpircManager::now();
|
||||||
|
self.notify(None);
|
||||||
|
}
|
||||||
|
protocol::spirc::MessageType::kMessageTypeNotify => {
|
||||||
|
if frame.get_device_state().get_is_active() {
|
||||||
|
//println!("{:?}", frame.get_state());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => ()
|
_ => ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn notify(&mut self, recipient: Option<&str>) {
|
fn notify(&mut self, recipient: Option<&str>) {
|
||||||
let device_state = self.device_state();
|
let mut pkt = protobuf_init!(protocol::spirc::Frame::new(), {
|
||||||
|
version: 1,
|
||||||
|
ident: self.ident.clone(),
|
||||||
|
protocol_version: "2.0.0".to_string(),
|
||||||
|
seq_nr: { self.seq_nr += 1; self.seq_nr },
|
||||||
|
typ: protocol::spirc::MessageType::kMessageTypeNotify,
|
||||||
|
device_state: self.device_state(),
|
||||||
|
recipient: protobuf::RepeatedField::from_vec(
|
||||||
|
recipient.map(|r| vec![r.to_string()] ).unwrap_or(vec![])
|
||||||
|
),
|
||||||
|
state_update_id: self.state_update_id
|
||||||
|
});
|
||||||
|
|
||||||
|
if self.is_active {
|
||||||
|
pkt.set_state(self.state.export());
|
||||||
|
}
|
||||||
|
|
||||||
self.session.mercury.send(MercuryRequest{
|
self.session.mercury.send(MercuryRequest{
|
||||||
method: MercuryMethod::SEND,
|
method: MercuryMethod::SEND,
|
||||||
uri: format!("hm://remote/user/{}", self.username).to_string(),
|
uri: format!("hm://remote/user/{}", self.username),
|
||||||
content_type: None,
|
content_type: None,
|
||||||
callback: None,
|
callback: None,
|
||||||
payload: vec![
|
payload: vec![ pkt.write_to_bytes().unwrap() ]
|
||||||
protobuf_init!(protocol::spirc::Frame::new(), {
|
|
||||||
version: 1,
|
|
||||||
ident: self.ident.clone(),
|
|
||||||
protocol_version: "2.0.0".to_string(),
|
|
||||||
seq_nr: { self.seq_nr += 1; self.seq_nr },
|
|
||||||
typ: protocol::spirc::MessageType::kMessageTypeNotify,
|
|
||||||
device_state: device_state,
|
|
||||||
recipient: protobuf::RepeatedField::from_vec(
|
|
||||||
recipient.map(|r| vec![r.to_string()] ).unwrap_or(vec![])
|
|
||||||
)
|
|
||||||
}).write_to_bytes().unwrap()
|
|
||||||
]
|
|
||||||
}).unwrap();
|
}).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -268,5 +396,10 @@ impl SpircManager {
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn now() -> i64 {
|
||||||
|
let ts = time::now_utc().to_timespec();
|
||||||
|
ts.sec * 1000 + ts.nsec as i64 / 1000000
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue