librespot_connect/
model.rs

1use crate::{
2    core::dealer::protocol::SkipTo, protocol::context_player_options::ContextPlayerOptionOverrides,
3};
4
5use std::ops::Deref;
6
7/// Request for loading playback
8#[derive(Debug, Clone)]
9pub struct LoadRequest {
10    pub(super) context: PlayContext,
11    pub(super) options: LoadRequestOptions,
12}
13
14impl Deref for LoadRequest {
15    type Target = LoadRequestOptions;
16
17    fn deref(&self) -> &Self::Target {
18        &self.options
19    }
20}
21
22#[derive(Debug, Clone)]
23pub(super) enum PlayContext {
24    Uri(String),
25    Tracks(Vec<String>),
26}
27
28/// The parameters for creating a load request
29#[derive(Debug, Default, Clone)]
30pub struct LoadRequestOptions {
31    /// Whether the given tracks should immediately start playing, or just be initially loaded.
32    pub start_playing: bool,
33    /// Start the playback at a specific point of the track.
34    ///
35    /// The provided value is used as milliseconds. Providing a value greater
36    /// than the track duration will start the track at the beginning.
37    pub seek_to: u32,
38    /// Options that decide how the context starts playing
39    pub context_options: Option<LoadContextOptions>,
40    /// Decides the starting position in the given context.
41    ///
42    /// If the provided item doesn't exist or is out of range,
43    /// the playback starts at the beginning of the context.
44    ///
45    /// If `None` is provided and `shuffle` is `true`, a random track is played, otherwise the first
46    pub playing_track: Option<PlayingTrack>,
47}
48
49/// The options which decide how the playback is started
50///
51/// Separated into an `enum` to exclude the other variants from being used
52/// simultaneously, as they are not compatible.
53#[derive(Debug, Clone)]
54pub enum LoadContextOptions {
55    /// Starts the context with options
56    Options(Options),
57    /// Starts the playback as the autoplay variant of the context
58    ///
59    /// This is the same as finishing a context and
60    /// automatically continuing playback of similar tracks
61    Autoplay,
62}
63
64/// The available options that indicate how to start the context
65#[derive(Debug, Default, Clone)]
66pub struct Options {
67    /// Start the context in shuffle mode
68    pub shuffle: bool,
69    /// Start the context in repeat mode
70    pub repeat: bool,
71    /// Start the context, repeating the first track until skipped or manually disabled
72    pub repeat_track: bool,
73}
74
75impl From<ContextPlayerOptionOverrides> for Options {
76    fn from(value: ContextPlayerOptionOverrides) -> Self {
77        Self {
78            shuffle: value.shuffling_context.unwrap_or_default(),
79            repeat: value.repeating_context.unwrap_or_default(),
80            repeat_track: value.repeating_track.unwrap_or_default(),
81        }
82    }
83}
84
85impl LoadRequest {
86    /// Create a load request from a `context_uri`
87    ///
88    /// For supported `context_uri` see [`SpClient::get_context`](librespot_core::spclient::SpClient::get_context)
89    ///
90    /// Equivalent to using [`/me/player/play`](https://developer.spotify.com/documentation/web-api/reference/start-a-users-playback)
91    /// and providing `context_uri`
92    pub fn from_context_uri(context_uri: String, options: LoadRequestOptions) -> Self {
93        Self {
94            context: PlayContext::Uri(context_uri),
95            options,
96        }
97    }
98
99    /// Create a load request from a set of `tracks`
100    ///
101    /// Equivalent to using [`/me/player/play`](https://developer.spotify.com/documentation/web-api/reference/start-a-users-playback)
102    /// and providing `uris`
103    pub fn from_tracks(tracks: Vec<String>, options: LoadRequestOptions) -> Self {
104        Self {
105            context: PlayContext::Tracks(tracks),
106            options,
107        }
108    }
109}
110
111/// An item that represent a track to play
112#[derive(Debug, Clone)]
113pub enum PlayingTrack {
114    /// Represent the track at a given index.
115    Index(u32),
116    /// Represent the uri of a track.
117    Uri(String),
118    #[doc(hidden)]
119    /// Represent an internal identifier from spotify.
120    ///
121    /// The internal identifier is not the id contained in the uri. And rather
122    /// an unrelated id probably unique in spotify's internal database. But that's
123    /// just speculation.
124    ///
125    /// This identifier is not available by any public api. It's used for varies in
126    /// any spotify client, like sorting, displaying which track is currently played
127    /// and skipping to a track. Mobile uses it pretty intensively but also web and
128    /// desktop seem to make use of it.
129    Uid(String),
130}
131
132impl TryFrom<SkipTo> for PlayingTrack {
133    type Error = ();
134
135    fn try_from(value: SkipTo) -> Result<Self, Self::Error> {
136        // order of checks is important, as the index can be 0, but still has an uid or uri provided,
137        // so we only use the index as last resort
138        if let Some(uri) = value.track_uri {
139            Ok(PlayingTrack::Uri(uri))
140        } else if let Some(uid) = value.track_uid {
141            Ok(PlayingTrack::Uid(uid))
142        } else if let Some(index) = value.track_index {
143            Ok(PlayingTrack::Index(index))
144        } else {
145            Err(())
146        }
147    }
148}
149
150#[derive(Debug)]
151pub(super) enum SpircPlayStatus {
152    Stopped,
153    LoadingPlay {
154        position_ms: u32,
155    },
156    LoadingPause {
157        position_ms: u32,
158    },
159    Playing {
160        nominal_start_time: i64,
161        preloading_of_next_track_triggered: bool,
162    },
163    Paused {
164        position_ms: u32,
165        preloading_of_next_track_triggered: bool,
166    },
167}