librespot_core/
config.rs

1use std::{fmt, path::PathBuf, str::FromStr};
2
3use librespot_protocol::devices::DeviceType as ProtoDeviceType;
4use url::Url;
5
6pub(crate) const KEYMASTER_CLIENT_ID: &str = "65b708073fc0480ea92a077233ca87bd";
7pub(crate) const ANDROID_CLIENT_ID: &str = "9a8d2f0ce77a4e248bb71fefcb557637";
8pub(crate) const IOS_CLIENT_ID: &str = "58bd3c95768941ea9eb4350aaa033eb3";
9
10// Easily adjust the current platform to mock the behavior on it. If for example
11// android or ios needs to be mocked, the `os_version` has to be set to a valid version.
12// Otherwise, client-token or login5 requests will fail with a generic invalid-credential error.
13/// See [std::env::consts::OS]
14pub const OS: &str = std::env::consts::OS;
15
16// valid versions for some os:
17// 'android': 30
18// 'ios': 17
19/// See [sysinfo::System::os_version]
20pub fn os_version() -> String {
21    sysinfo::System::os_version().unwrap_or("0".into())
22}
23
24#[derive(Clone, Debug)]
25pub struct SessionConfig {
26    pub client_id: String,
27    pub device_id: String,
28    pub proxy: Option<Url>,
29    pub ap_port: Option<u16>,
30    pub tmp_dir: PathBuf,
31    pub autoplay: Option<bool>,
32}
33
34impl SessionConfig {
35    pub(crate) fn default_for_os(os: &str) -> Self {
36        let device_id = uuid::Uuid::new_v4().as_hyphenated().to_string();
37        let client_id = match os {
38            "android" => ANDROID_CLIENT_ID,
39            "ios" => IOS_CLIENT_ID,
40            _ => KEYMASTER_CLIENT_ID,
41        }
42        .to_owned();
43
44        Self {
45            client_id,
46            device_id,
47            proxy: None,
48            ap_port: None,
49            tmp_dir: std::env::temp_dir(),
50            autoplay: None,
51        }
52    }
53}
54
55impl Default for SessionConfig {
56    fn default() -> Self {
57        Self::default_for_os(OS)
58    }
59}
60
61#[derive(Clone, Copy, Debug, Hash, PartialOrd, Ord, PartialEq, Eq, Default)]
62pub enum DeviceType {
63    Unknown = 0,
64    Computer = 1,
65    Tablet = 2,
66    Smartphone = 3,
67    #[default]
68    Speaker = 4,
69    Tv = 5,
70    Avr = 6,
71    Stb = 7,
72    AudioDongle = 8,
73    GameConsole = 9,
74    CastAudio = 10,
75    CastVideo = 11,
76    Automobile = 12,
77    Smartwatch = 13,
78    Chromebook = 14,
79    UnknownSpotify = 100,
80    CarThing = 101,
81    Observer = 102,
82}
83
84impl FromStr for DeviceType {
85    type Err = ();
86    fn from_str(s: &str) -> Result<Self, Self::Err> {
87        use self::DeviceType::*;
88        match s.to_lowercase().as_ref() {
89            "computer" => Ok(Computer),
90            "tablet" => Ok(Tablet),
91            "smartphone" => Ok(Smartphone),
92            "speaker" => Ok(Speaker),
93            "tv" => Ok(Tv),
94            "avr" => Ok(Avr),
95            "stb" => Ok(Stb),
96            "audiodongle" => Ok(AudioDongle),
97            "gameconsole" => Ok(GameConsole),
98            "castaudio" => Ok(CastAudio),
99            "castvideo" => Ok(CastVideo),
100            "automobile" => Ok(Automobile),
101            "smartwatch" => Ok(Smartwatch),
102            "chromebook" => Ok(Chromebook),
103            "carthing" => Ok(CarThing),
104            _ => Err(()),
105        }
106    }
107}
108
109impl From<&DeviceType> for &str {
110    fn from(d: &DeviceType) -> &'static str {
111        use self::DeviceType::*;
112        match d {
113            Unknown => "Unknown",
114            Computer => "Computer",
115            Tablet => "Tablet",
116            Smartphone => "Smartphone",
117            Speaker => "Speaker",
118            Tv => "TV",
119            Avr => "AVR",
120            Stb => "STB",
121            AudioDongle => "AudioDongle",
122            GameConsole => "GameConsole",
123            CastAudio => "CastAudio",
124            CastVideo => "CastVideo",
125            Automobile => "Automobile",
126            Smartwatch => "Smartwatch",
127            Chromebook => "Chromebook",
128            UnknownSpotify => "UnknownSpotify",
129            CarThing => "CarThing",
130            Observer => "Observer",
131        }
132    }
133}
134
135impl From<DeviceType> for &str {
136    fn from(d: DeviceType) -> &'static str {
137        (&d).into()
138    }
139}
140
141impl fmt::Display for DeviceType {
142    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
143        let str: &str = self.into();
144        f.write_str(str)
145    }
146}
147
148impl From<DeviceType> for ProtoDeviceType {
149    fn from(value: DeviceType) -> Self {
150        match value {
151            DeviceType::Unknown => ProtoDeviceType::UNKNOWN,
152            DeviceType::Computer => ProtoDeviceType::COMPUTER,
153            DeviceType::Tablet => ProtoDeviceType::TABLET,
154            DeviceType::Smartphone => ProtoDeviceType::SMARTPHONE,
155            DeviceType::Speaker => ProtoDeviceType::SPEAKER,
156            DeviceType::Tv => ProtoDeviceType::TV,
157            DeviceType::Avr => ProtoDeviceType::AVR,
158            DeviceType::Stb => ProtoDeviceType::STB,
159            DeviceType::AudioDongle => ProtoDeviceType::AUDIO_DONGLE,
160            DeviceType::GameConsole => ProtoDeviceType::GAME_CONSOLE,
161            DeviceType::CastAudio => ProtoDeviceType::CAST_VIDEO,
162            DeviceType::CastVideo => ProtoDeviceType::CAST_AUDIO,
163            DeviceType::Automobile => ProtoDeviceType::AUTOMOBILE,
164            DeviceType::Smartwatch => ProtoDeviceType::SMARTWATCH,
165            DeviceType::Chromebook => ProtoDeviceType::CHROMEBOOK,
166            DeviceType::UnknownSpotify => ProtoDeviceType::UNKNOWN_SPOTIFY,
167            DeviceType::CarThing => ProtoDeviceType::CAR_THING,
168            DeviceType::Observer => ProtoDeviceType::OBSERVER,
169        }
170    }
171}