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}