mirror of
https://github.com/librespot-org/librespot.git
synced 2024-12-18 17:11:53 +00:00
Use PacketType
instead of hex identifiers
This commit is contained in:
parent
4fe1183a80
commit
0703630041
12 changed files with 160 additions and 42 deletions
50
Cargo.lock
generated
50
Cargo.lock
generated
|
@ -635,7 +635,7 @@ dependencies = [
|
|||
"gstreamer-sys",
|
||||
"libc",
|
||||
"muldiv",
|
||||
"num-rational",
|
||||
"num-rational 0.3.2",
|
||||
"once_cell",
|
||||
"paste",
|
||||
"pretty-hex",
|
||||
|
@ -1223,7 +1223,9 @@ dependencies = [
|
|||
"hyper-proxy",
|
||||
"librespot-protocol",
|
||||
"log",
|
||||
"num",
|
||||
"num-bigint",
|
||||
"num-derive",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
"once_cell",
|
||||
|
@ -1475,6 +1477,20 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43db66d1170d347f9a065114077f7dccb00c1b9478c89384490a3425279a4606"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"num-complex",
|
||||
"num-integer",
|
||||
"num-iter",
|
||||
"num-rational 0.4.0",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-bigint"
|
||||
version = "0.4.0"
|
||||
|
@ -1487,6 +1503,15 @@ dependencies = [
|
|||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-complex"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26873667bbbb7c5182d4a37c1add32cdf09f841af72da53318fdb81543c15085"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-derive"
|
||||
version = "0.3.3"
|
||||
|
@ -1508,6 +1533,17 @@ dependencies = [
|
|||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-iter"
|
||||
version = "0.1.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-rational"
|
||||
version = "0.3.2"
|
||||
|
@ -1519,6 +1555,18 @@ dependencies = [
|
|||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-rational"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d41702bd167c2df5520b384281bc111a4b5efcf7fbc4c9c222c815b07e0a6a6a"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-bigint",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.14"
|
||||
|
|
|
@ -7,6 +7,7 @@ use byteorder::{BigEndian, WriteBytesExt};
|
|||
use bytes::Bytes;
|
||||
use futures_util::StreamExt;
|
||||
use librespot_core::channel::{Channel, ChannelData};
|
||||
use librespot_core::packet::PacketType;
|
||||
use librespot_core::session::Session;
|
||||
use librespot_core::spotify_id::FileId;
|
||||
use tempfile::NamedTempFile;
|
||||
|
@ -46,7 +47,7 @@ pub fn request_range(session: &Session, file: FileId, offset: usize, length: usi
|
|||
data.write_u32::<BigEndian>(start as u32).unwrap();
|
||||
data.write_u32::<BigEndian>(end as u32).unwrap();
|
||||
|
||||
session.send_packet(0x8, data);
|
||||
session.send_packet(PacketType::StreamChunk, data);
|
||||
|
||||
channel
|
||||
}
|
||||
|
|
|
@ -26,7 +26,9 @@ http = "0.2"
|
|||
hyper = { version = "0.14", features = ["client", "tcp", "http1"] }
|
||||
hyper-proxy = { version = "0.9.1", default-features = false }
|
||||
log = "0.4"
|
||||
num = "0.4"
|
||||
num-bigint = { version = "0.4", features = ["rand"] }
|
||||
num-derive = "0.3"
|
||||
num-integer = "0.1"
|
||||
num-traits = "0.2"
|
||||
once_cell = "1.5.2"
|
||||
|
|
|
@ -4,6 +4,7 @@ use std::collections::HashMap;
|
|||
use std::io::Write;
|
||||
use tokio::sync::oneshot;
|
||||
|
||||
use crate::packet::PacketType;
|
||||
use crate::spotify_id::{FileId, SpotifyId};
|
||||
use crate::util::SeqGenerator;
|
||||
|
||||
|
@ -21,19 +22,19 @@ component! {
|
|||
}
|
||||
|
||||
impl AudioKeyManager {
|
||||
pub(crate) fn dispatch(&self, cmd: u8, mut data: Bytes) {
|
||||
pub(crate) fn dispatch(&self, cmd: PacketType, mut data: Bytes) {
|
||||
let seq = BigEndian::read_u32(data.split_to(4).as_ref());
|
||||
|
||||
let sender = self.lock(|inner| inner.pending.remove(&seq));
|
||||
|
||||
if let Some(sender) = sender {
|
||||
match cmd {
|
||||
0xd => {
|
||||
PacketType::AesKey => {
|
||||
let mut key = [0u8; 16];
|
||||
key.copy_from_slice(data.as_ref());
|
||||
let _ = sender.send(Ok(AudioKey(key)));
|
||||
}
|
||||
0xe => {
|
||||
PacketType::AesKeyError => {
|
||||
warn!(
|
||||
"error audio key {:x} {:x}",
|
||||
data.as_ref()[0],
|
||||
|
@ -66,6 +67,6 @@ impl AudioKeyManager {
|
|||
data.write_u32::<BigEndian>(seq).unwrap();
|
||||
data.write_u16::<BigEndian>(0x0000).unwrap();
|
||||
|
||||
self.session().send_packet(0xc, data)
|
||||
self.session().send_packet(PacketType::RequestKey, data)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,8 +8,10 @@ use bytes::Bytes;
|
|||
use futures_core::Stream;
|
||||
use futures_util::lock::BiLock;
|
||||
use futures_util::{ready, StreamExt};
|
||||
use num_traits::FromPrimitive;
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
use crate::packet::PacketType;
|
||||
use crate::util::SeqGenerator;
|
||||
|
||||
component! {
|
||||
|
@ -66,7 +68,7 @@ impl ChannelManager {
|
|||
(seq, channel)
|
||||
}
|
||||
|
||||
pub(crate) fn dispatch(&self, cmd: u8, mut data: Bytes) {
|
||||
pub(crate) fn dispatch(&self, cmd: PacketType, mut data: Bytes) {
|
||||
use std::collections::hash_map::Entry;
|
||||
|
||||
let id: u16 = BigEndian::read_u16(data.split_to(2).as_ref());
|
||||
|
@ -87,7 +89,7 @@ impl ChannelManager {
|
|||
inner.download_measurement_bytes += data.len();
|
||||
|
||||
if let Entry::Occupied(entry) = inner.channels.entry(id) {
|
||||
let _ = entry.get().send((cmd, data));
|
||||
let _ = entry.get().send((cmd as u8, data));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -109,7 +111,8 @@ impl Channel {
|
|||
fn recv_packet(&mut self, cx: &mut Context<'_>) -> Poll<Result<Bytes, ChannelError>> {
|
||||
let (cmd, packet) = ready!(self.receiver.poll_recv(cx)).ok_or(ChannelError)?;
|
||||
|
||||
if cmd == 0xa {
|
||||
let packet_type = FromPrimitive::from_u8(cmd);
|
||||
if let Some(PacketType::ChannelError) = packet_type {
|
||||
let code = BigEndian::read_u16(&packet.as_ref()[..2]);
|
||||
error!("channel error: {} {}", packet.len(), code);
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ pub use self::handshake::handshake;
|
|||
use std::io::{self, ErrorKind};
|
||||
|
||||
use futures_util::{SinkExt, StreamExt};
|
||||
use num_traits::FromPrimitive;
|
||||
use protobuf::{self, Message, ProtobufError};
|
||||
use thiserror::Error;
|
||||
use tokio::net::TcpStream;
|
||||
|
@ -14,6 +15,7 @@ use tokio_util::codec::Framed;
|
|||
use url::Url;
|
||||
|
||||
use crate::authentication::Credentials;
|
||||
use crate::packet::PacketType;
|
||||
use crate::protocol::keyexchange::{APLoginFailed, ErrorCode};
|
||||
use crate::version;
|
||||
|
||||
|
@ -95,13 +97,14 @@ pub async fn authenticate(
|
|||
.set_device_id(device_id.to_string());
|
||||
packet.set_version_string(version::VERSION_STRING.to_string());
|
||||
|
||||
let cmd = 0xab;
|
||||
let cmd = PacketType::Login;
|
||||
let data = packet.write_to_bytes().unwrap();
|
||||
|
||||
transport.send((cmd, data)).await?;
|
||||
transport.send((cmd as u8, data)).await?;
|
||||
let (cmd, data) = transport.next().await.expect("EOF")?;
|
||||
match cmd {
|
||||
0xac => {
|
||||
let packet_type = FromPrimitive::from_u8(cmd);
|
||||
match packet_type {
|
||||
Some(PacketType::APWelcome) => {
|
||||
let welcome_data = APWelcome::parse_from_bytes(data.as_ref())?;
|
||||
|
||||
let reusable_credentials = Credentials {
|
||||
|
@ -112,7 +115,7 @@ pub async fn authenticate(
|
|||
|
||||
Ok(reusable_credentials)
|
||||
}
|
||||
0xad => {
|
||||
Some(PacketType::AuthFailure) => {
|
||||
let error_data = APLoginFailed::parse_from_bytes(data.as_ref())?;
|
||||
Err(error_data.into())
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate num_derive;
|
||||
|
||||
use librespot_protocol as protocol;
|
||||
|
||||
|
@ -21,6 +22,7 @@ mod dealer;
|
|||
pub mod diffie_hellman;
|
||||
mod http_client;
|
||||
pub mod mercury;
|
||||
pub mod packet;
|
||||
mod proxytunnel;
|
||||
pub mod session;
|
||||
mod socket;
|
||||
|
|
|
@ -11,6 +11,7 @@ use futures_util::FutureExt;
|
|||
use protobuf::Message;
|
||||
use tokio::sync::{mpsc, oneshot};
|
||||
|
||||
use crate::packet::PacketType;
|
||||
use crate::protocol;
|
||||
use crate::util::SeqGenerator;
|
||||
|
||||
|
@ -143,7 +144,7 @@ impl MercuryManager {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn dispatch(&self, cmd: u8, mut data: Bytes) {
|
||||
pub(crate) fn dispatch(&self, cmd: PacketType, mut data: Bytes) {
|
||||
let seq_len = BigEndian::read_u16(data.split_to(2).as_ref()) as usize;
|
||||
let seq = data.split_to(seq_len).as_ref().to_owned();
|
||||
|
||||
|
@ -154,15 +155,18 @@ impl MercuryManager {
|
|||
|
||||
let mut pending = match pending {
|
||||
Some(pending) => pending,
|
||||
None if cmd == 0xb5 => MercuryPending {
|
||||
None => {
|
||||
if let PacketType::MercuryEvent = cmd {
|
||||
MercuryPending {
|
||||
parts: Vec::new(),
|
||||
partial: None,
|
||||
callback: None,
|
||||
},
|
||||
None => {
|
||||
warn!("Ignore seq {:?} cmd {:x}", seq, cmd);
|
||||
}
|
||||
} else {
|
||||
warn!("Ignore seq {:?} cmd {:x}", seq, cmd as u8);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for i in 0..count {
|
||||
|
@ -191,7 +195,7 @@ impl MercuryManager {
|
|||
data.split_to(size).as_ref().to_owned()
|
||||
}
|
||||
|
||||
fn complete_request(&self, cmd: u8, mut pending: MercuryPending) {
|
||||
fn complete_request(&self, cmd: PacketType, mut pending: MercuryPending) {
|
||||
let header_data = pending.parts.remove(0);
|
||||
let header = protocol::mercury::Header::parse_from_bytes(&header_data).unwrap();
|
||||
|
||||
|
@ -208,7 +212,7 @@ impl MercuryManager {
|
|||
if let Some(cb) = pending.callback {
|
||||
let _ = cb.send(Err(MercuryError));
|
||||
}
|
||||
} else if cmd == 0xb5 {
|
||||
} else if let PacketType::MercuryEvent = cmd {
|
||||
self.lock(|inner| {
|
||||
let mut found = false;
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ use byteorder::{BigEndian, WriteBytesExt};
|
|||
use protobuf::Message;
|
||||
use std::io::Write;
|
||||
|
||||
use crate::packet::PacketType;
|
||||
use crate::protocol;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
|
@ -43,11 +44,12 @@ impl ToString for MercuryMethod {
|
|||
}
|
||||
|
||||
impl MercuryMethod {
|
||||
pub fn command(&self) -> u8 {
|
||||
pub fn command(&self) -> PacketType {
|
||||
use PacketType::*;
|
||||
match *self {
|
||||
MercuryMethod::Get | MercuryMethod::Send => 0xb2,
|
||||
MercuryMethod::Sub => 0xb3,
|
||||
MercuryMethod::Unsub => 0xb4,
|
||||
MercuryMethod::Get | MercuryMethod::Send => MercuryReq,
|
||||
MercuryMethod::Sub => MercurySub,
|
||||
MercuryMethod::Unsub => MercuryUnsub,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
37
core/src/packet.rs
Normal file
37
core/src/packet.rs
Normal file
|
@ -0,0 +1,37 @@
|
|||
// Ported from librespot-java. Relicensed under MIT with permission.
|
||||
|
||||
use num_derive::{FromPrimitive, ToPrimitive};
|
||||
|
||||
#[derive(Debug, FromPrimitive, ToPrimitive)]
|
||||
pub enum PacketType {
|
||||
SecretBlock = 0x02,
|
||||
Ping = 0x04,
|
||||
StreamChunk = 0x08,
|
||||
StreamChunkRes = 0x09,
|
||||
ChannelError = 0x0a,
|
||||
ChannelAbort = 0x0b,
|
||||
RequestKey = 0x0c,
|
||||
AesKey = 0x0d,
|
||||
AesKeyError = 0x0e,
|
||||
Image = 0x19,
|
||||
CountryCode = 0x1b,
|
||||
Pong = 0x49,
|
||||
PongAck = 0x4a,
|
||||
Pause = 0x4b,
|
||||
ProductInfo = 0x50,
|
||||
LegacyWelcome = 0x69,
|
||||
LicenseVersion = 0x76,
|
||||
Login = 0xab,
|
||||
APWelcome = 0xac,
|
||||
AuthFailure = 0xad,
|
||||
MercuryReq = 0xb2,
|
||||
MercurySub = 0xb3,
|
||||
MercuryUnsub = 0xb4,
|
||||
MercuryEvent = 0xb5,
|
||||
TrackEndedTime = 0x82,
|
||||
UnknownDataAllZeros = 0x1f,
|
||||
PreferredLocale = 0x74,
|
||||
Unknown0x4f = 0x4f,
|
||||
Unknown0x0f = 0x0f,
|
||||
Unknown0x10 = 0x10,
|
||||
}
|
|
@ -11,6 +11,7 @@ use byteorder::{BigEndian, ByteOrder};
|
|||
use bytes::Bytes;
|
||||
use futures_core::TryStream;
|
||||
use futures_util::{future, ready, StreamExt, TryStreamExt};
|
||||
use num_traits::FromPrimitive;
|
||||
use once_cell::sync::OnceCell;
|
||||
use thiserror::Error;
|
||||
use tokio::sync::mpsc;
|
||||
|
@ -25,6 +26,7 @@ use crate::config::SessionConfig;
|
|||
use crate::connection::{self, AuthenticationError};
|
||||
use crate::http_client::HttpClient;
|
||||
use crate::mercury::MercuryManager;
|
||||
use crate::packet::PacketType;
|
||||
use crate::token::TokenProvider;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
|
@ -184,11 +186,11 @@ impl Session {
|
|||
);
|
||||
}
|
||||
|
||||
#[allow(clippy::match_same_arms)]
|
||||
fn dispatch(&self, cmd: u8, data: Bytes) {
|
||||
match cmd {
|
||||
// TODO: add command types
|
||||
0x4 => {
|
||||
use PacketType::*;
|
||||
let packet_type = FromPrimitive::from_u8(cmd);
|
||||
match packet_type {
|
||||
Some(Ping) => {
|
||||
let server_timestamp = BigEndian::read_u32(data.as_ref()) as i64;
|
||||
let timestamp = match SystemTime::now().duration_since(UNIX_EPOCH) {
|
||||
Ok(dur) => dur,
|
||||
|
@ -199,24 +201,36 @@ impl Session {
|
|||
self.0.data.write().unwrap().time_delta = server_timestamp - timestamp;
|
||||
|
||||
self.debug_info();
|
||||
self.send_packet(0x49, vec![0, 0, 0, 0]);
|
||||
self.send_packet(Pong, vec![0, 0, 0, 0]);
|
||||
}
|
||||
0x4a => (),
|
||||
0x1b => {
|
||||
Some(PongAck) => {}
|
||||
Some(CountryCode) => {
|
||||
let country = String::from_utf8(data.as_ref().to_owned()).unwrap();
|
||||
info!("Country: {:?}", country);
|
||||
self.0.data.write().unwrap().country = country;
|
||||
}
|
||||
|
||||
0x9 | 0xa => self.channel().dispatch(cmd, data),
|
||||
0xd | 0xe => self.audio_key().dispatch(cmd, data),
|
||||
0xb2..=0xb6 => self.mercury().dispatch(cmd, data),
|
||||
_ => (),
|
||||
Some(StreamChunkRes) | Some(ChannelError) => {
|
||||
self.channel().dispatch(packet_type.unwrap(), data)
|
||||
}
|
||||
Some(AesKey) | Some(AesKeyError) => {
|
||||
self.audio_key().dispatch(packet_type.unwrap(), data)
|
||||
}
|
||||
Some(MercuryReq) | Some(MercurySub) | Some(MercuryUnsub) | Some(MercuryEvent) => {
|
||||
self.mercury().dispatch(packet_type.unwrap(), data)
|
||||
}
|
||||
_ => {
|
||||
if let Some(packet_type) = PacketType::from_u8(cmd) {
|
||||
trace!("Ignoring {:?} packet", packet_type);
|
||||
} else {
|
||||
trace!("Ignoring unknown packet {:x}", cmd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_packet(&self, cmd: u8, data: Vec<u8>) {
|
||||
self.0.tx_connection.send((cmd, data)).unwrap();
|
||||
pub fn send_packet(&self, cmd: PacketType, data: Vec<u8>) {
|
||||
self.0.tx_connection.send((cmd as u8, data)).unwrap();
|
||||
}
|
||||
|
||||
pub fn cache(&self) -> Option<&Arc<Cache>> {
|
||||
|
|
|
@ -2,6 +2,7 @@ use byteorder::{BigEndian, WriteBytesExt};
|
|||
use std::io::Write;
|
||||
|
||||
use librespot_core::channel::ChannelData;
|
||||
use librespot_core::packet::PacketType;
|
||||
use librespot_core::session::Session;
|
||||
use librespot_core::spotify_id::FileId;
|
||||
|
||||
|
@ -13,7 +14,7 @@ pub fn get(session: &Session, file: FileId) -> ChannelData {
|
|||
packet.write_u16::<BigEndian>(channel_id).unwrap();
|
||||
packet.write_u16::<BigEndian>(0).unwrap();
|
||||
packet.write(&file.0).unwrap();
|
||||
session.send_packet(0x19, packet);
|
||||
session.send_packet(PacketType::Image, packet);
|
||||
|
||||
data
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue