spirc.rs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. use eventual::Async;
  2. use protobuf::{self, Message};
  3. use std::sync::{mpsc, MutexGuard};
  4. use util;
  5. use session::Session;
  6. use util::SpotifyId;
  7. use util::version::version_string;
  8. use mercury::{MercuryRequest, MercuryMethod};
  9. use librespot_protocol as protocol;
  10. pub use librespot_protocol::spirc::PlayStatus;
  11. pub struct SpircManager<D: SpircDelegate> {
  12. delegate: D,
  13. session: Session,
  14. state_update_id: i64,
  15. seq_nr: u32,
  16. name: String,
  17. ident: String,
  18. device_type: u8,
  19. can_play: bool,
  20. repeat: bool,
  21. shuffle: bool,
  22. volume: u16,
  23. is_active: bool,
  24. became_active_at: i64,
  25. last_command_ident: String,
  26. last_command_msgid: u32,
  27. tracks: Vec<SpotifyId>,
  28. index: u32,
  29. }
  30. pub trait SpircDelegate {
  31. type State : SpircState;
  32. fn load(&self, track: SpotifyId, start_playing: bool, position_ms: u32);
  33. fn play(&self);
  34. fn pause(&self);
  35. fn seek(&self, position_ms: u32);
  36. fn stop(&self);
  37. fn state(&self) -> MutexGuard<Self::State>;
  38. fn updates(&self) -> mpsc::Receiver<i64>;
  39. }
  40. pub trait SpircState {
  41. fn status(&self) -> PlayStatus;
  42. fn position(&self) -> (u32, i64);
  43. fn update_time(&self) -> i64;
  44. fn end_of_track(&self) -> bool;
  45. }
  46. impl<D: SpircDelegate> SpircManager<D> {
  47. pub fn new(session: Session, delegate: D) -> SpircManager<D> {
  48. let ident = session.0.data.read().unwrap().device_id.clone();
  49. let name = session.0.config.device_name.clone();
  50. SpircManager {
  51. delegate: delegate,
  52. session: session,
  53. state_update_id: 0,
  54. seq_nr: 0,
  55. name: name,
  56. ident: ident,
  57. device_type: 5,
  58. can_play: true,
  59. repeat: false,
  60. shuffle: false,
  61. volume: 0x8000,
  62. is_active: false,
  63. became_active_at: 0,
  64. last_command_ident: String::new(),
  65. last_command_msgid: 0,
  66. tracks: Vec::new(),
  67. index: 0,
  68. }
  69. }
  70. pub fn run(&mut self) {
  71. let rx = self.session.mercury_sub(format!("hm://remote/user/{}/",
  72. self.session
  73. .0
  74. .data
  75. .read()
  76. .unwrap()
  77. .canonical_username
  78. .clone()));
  79. let updates = self.delegate.updates();
  80. self.notify(true, None);
  81. loop {
  82. select! {
  83. pkt = rx.recv() => {
  84. let frame = protobuf::parse_from_bytes::<protocol::spirc::Frame>(
  85. pkt.unwrap().payload.first().unwrap()).unwrap();
  86. println!("{:?} {} {} {} {}",
  87. frame.get_typ(),
  88. frame.get_device_state().get_name(),
  89. frame.get_ident(),
  90. frame.get_seq_nr(),
  91. frame.get_state_update_id());
  92. if frame.get_ident() != self.ident &&
  93. (frame.get_recipient().len() == 0 ||
  94. frame.get_recipient().contains(&self.ident)) {
  95. self.handle(frame);
  96. }
  97. },
  98. update_time = updates.recv() => {
  99. let end_of_track = self.delegate.state().end_of_track();
  100. if end_of_track {
  101. self.index = (self.index + 1) % self.tracks.len() as u32;
  102. let track = self.tracks[self.index as usize];
  103. self.delegate.load(track, true, 0);
  104. } else {
  105. self.state_update_id = update_time.unwrap();
  106. self.notify(false, None);
  107. }
  108. }
  109. }
  110. }
  111. }
  112. fn handle(&mut self, frame: protocol::spirc::Frame) {
  113. if frame.get_recipient().len() > 0 {
  114. self.last_command_ident = frame.get_ident().to_owned();
  115. self.last_command_msgid = frame.get_seq_nr();
  116. }
  117. match frame.get_typ() {
  118. protocol::spirc::MessageType::kMessageTypeHello => {
  119. self.notify(false, Some(frame.get_ident()));
  120. }
  121. protocol::spirc::MessageType::kMessageTypeLoad => {
  122. if !self.is_active {
  123. self.is_active = true;
  124. self.became_active_at = util::now_ms();
  125. }
  126. self.index = frame.get_state().get_playing_track_index();
  127. self.tracks = frame.get_state()
  128. .get_track()
  129. .iter()
  130. .map(|track| SpotifyId::from_raw(track.get_gid()))
  131. .collect();
  132. let play = frame.get_state().get_status() == PlayStatus::kPlayStatusPlay;
  133. let track = self.tracks[self.index as usize];
  134. let position = frame.get_state().get_position_ms();
  135. self.delegate.load(track, play, position);
  136. }
  137. protocol::spirc::MessageType::kMessageTypePlay => {
  138. self.delegate.play();
  139. }
  140. protocol::spirc::MessageType::kMessageTypePause => {
  141. self.delegate.pause();
  142. }
  143. protocol::spirc::MessageType::kMessageTypeSeek => {
  144. self.delegate.seek(frame.get_position());
  145. }
  146. protocol::spirc::MessageType::kMessageTypeNotify => {
  147. if self.is_active && frame.get_device_state().get_is_active() {
  148. self.is_active = false;
  149. self.delegate.stop();
  150. }
  151. }
  152. _ => (),
  153. }
  154. }
  155. fn notify(&mut self, hello: bool, recipient: Option<&str>) {
  156. let mut pkt = protobuf_init!(protocol::spirc::Frame::new(), {
  157. version: 1,
  158. ident: self.ident.clone(),
  159. protocol_version: "2.0.0".to_owned(),
  160. seq_nr: { self.seq_nr += 1; self.seq_nr },
  161. typ: if hello {
  162. protocol::spirc::MessageType::kMessageTypeHello
  163. } else {
  164. protocol::spirc::MessageType::kMessageTypeNotify
  165. },
  166. device_state: self.device_state(),
  167. recipient: protobuf::RepeatedField::from_vec(
  168. recipient.map(|r| vec![r.to_owned()] ).unwrap_or(vec![])
  169. ),
  170. state_update_id: self.state_update_id as i64
  171. });
  172. if self.is_active {
  173. pkt.set_state(self.spirc_state());
  174. }
  175. self.session
  176. .mercury(MercuryRequest {
  177. method: MercuryMethod::SEND,
  178. uri: format!("hm://remote/user/{}",
  179. self.session.0.data.read().unwrap().canonical_username.clone()),
  180. content_type: None,
  181. payload: vec![pkt.write_to_bytes().unwrap()],
  182. })
  183. .await()
  184. .unwrap();
  185. }
  186. fn spirc_state(&self) -> protocol::spirc::State {
  187. let state = self.delegate.state();
  188. let (position_ms, position_measured_at) = state.position();
  189. protobuf_init!(protocol::spirc::State::new(), {
  190. status: state.status(),
  191. position_ms: position_ms,
  192. position_measured_at: position_measured_at as u64,
  193. playing_track_index: self.index,
  194. track: self.tracks.iter().map(|track| {
  195. protobuf_init!(protocol::spirc::TrackRef::new(), {
  196. gid: track.to_raw().to_vec()
  197. })
  198. }).collect(),
  199. shuffle: self.shuffle,
  200. repeat: self.repeat,
  201. playing_from_fallback: true,
  202. last_command_ident: self.last_command_ident.clone(),
  203. last_command_msgid: self.last_command_msgid
  204. })
  205. }
  206. fn device_state(&self) -> protocol::spirc::DeviceState {
  207. protobuf_init!(protocol::spirc::DeviceState::new(), {
  208. sw_version: version_string(),
  209. is_active: self.is_active,
  210. can_play: self.can_play,
  211. volume: self.volume as u32,
  212. name: self.name.clone(),
  213. error_code: 0,
  214. became_active_at: if self.is_active { self.became_active_at as i64 } else { 0 },
  215. capabilities => [
  216. @{
  217. typ: protocol::spirc::CapabilityType::kCanBePlayer,
  218. intValue => [0]
  219. },
  220. @{
  221. typ: protocol::spirc::CapabilityType::kDeviceType,
  222. intValue => [ self.device_type as i64 ]
  223. },
  224. @{
  225. typ: protocol::spirc::CapabilityType::kGaiaEqConnectId,
  226. intValue => [1]
  227. },
  228. @{
  229. typ: protocol::spirc::CapabilityType::kSupportsLogout,
  230. intValue => [0]
  231. },
  232. @{
  233. typ: protocol::spirc::CapabilityType::kIsObservable,
  234. intValue => [1]
  235. },
  236. @{
  237. typ: protocol::spirc::CapabilityType::kVolumeSteps,
  238. intValue => [10]
  239. },
  240. @{
  241. typ: protocol::spirc::CapabilityType::kSupportedContexts,
  242. stringValue => [
  243. "album".to_owned(),
  244. "playlist".to_owned(),
  245. "search".to_owned(),
  246. "inbox".to_owned(),
  247. "toplist".to_owned(),
  248. "starred".to_owned(),
  249. "publishedstarred".to_owned(),
  250. "track".to_owned(),
  251. ]
  252. },
  253. @{
  254. typ: protocol::spirc::CapabilityType::kSupportedTypes,
  255. stringValue => [
  256. "audio/local".to_owned(),
  257. "audio/track".to_owned(),
  258. "local".to_owned(),
  259. "track".to_owned(),
  260. ]
  261. }
  262. ],
  263. })
  264. }
  265. }