1#![cfg_attr(feature = "fail-on-warnings", deny(warnings))]
2#![warn(clippy::all, clippy::pedantic, clippy::nursery, clippy::cargo)]
3#![allow(clippy::multiple_crate_versions)]
4
5use moosicbox_audio_zone::{
6 db::audio_zone_try_from_db,
7 models::{AudioZone, Player},
8};
9use moosicbox_json_utils::database::DatabaseFetchError;
10use moosicbox_music_models::api::ApiTrack;
11use moosicbox_session_models::{
12 CreateSession, PlaybackTarget, Session, SessionPlaylist, SetSessionAudioZone, UpdateSession,
13};
14use switchy_database::{config::ConfigDatabase, profiles::LibraryDatabase};
15
16mod db;
17pub use moosicbox_session_models as models;
18use thiserror::Error;
19
20#[cfg(feature = "api")]
21pub mod api;
22
23#[cfg(feature = "events")]
24pub mod events;
25
26pub async fn get_session_playlist_tracks(
30 db: &LibraryDatabase,
31 session_playlist_id: u64,
32) -> Result<Vec<ApiTrack>, DatabaseFetchError> {
33 crate::db::get_session_playlist_tracks(db, session_playlist_id).await
34}
35
36pub async fn get_session_playlist(
40 db: &LibraryDatabase,
41 session_id: u64,
42) -> Result<Option<SessionPlaylist>, DatabaseFetchError> {
43 crate::db::get_session_playlist(db, session_id).await
44}
45
46pub async fn get_session_audio_zone(
50 db: &LibraryDatabase,
51 session_id: u64,
52) -> Result<Option<AudioZone>, DatabaseFetchError> {
53 Ok(
54 if let Some(zone) = crate::db::get_session_audio_zone(db, session_id).await? {
55 Some(audio_zone_try_from_db(zone, db.into()).await?)
56 } else {
57 None
58 },
59 )
60}
61
62pub async fn set_session_audio_zone(
66 db: &LibraryDatabase,
67 set_session_audio_zone: &SetSessionAudioZone,
68) -> Result<(), DatabaseFetchError> {
69 crate::db::set_session_audio_zone(db, set_session_audio_zone).await
70}
71
72pub async fn get_session_playing(
76 db: &LibraryDatabase,
77 id: u64,
78) -> Result<Option<bool>, DatabaseFetchError> {
79 crate::db::get_session_playing(db, id).await
80}
81
82pub async fn get_session(
86 db: &LibraryDatabase,
87 id: u64,
88) -> Result<Option<Session>, DatabaseFetchError> {
89 crate::db::get_session(db, id).await
90}
91
92pub async fn get_sessions(db: &LibraryDatabase) -> Result<Vec<Session>, DatabaseFetchError> {
96 crate::db::get_sessions(db).await
97}
98
99pub async fn create_session(
103 db: &LibraryDatabase,
104 session: &CreateSession,
105) -> Result<Session, DatabaseFetchError> {
106 crate::db::create_session(db, session).await
107}
108
109pub async fn update_session(
113 db: &LibraryDatabase,
114 session: &UpdateSession,
115) -> Result<(), DatabaseFetchError> {
116 crate::db::update_session(db, session).await
117}
118
119pub async fn delete_session(
123 db: &LibraryDatabase,
124 session_id: u64,
125) -> Result<(), DatabaseFetchError> {
126 crate::db::delete_session(db, session_id).await
127}
128
129pub async fn get_connections(
133 db: &ConfigDatabase,
134) -> Result<Vec<models::Connection>, DatabaseFetchError> {
135 crate::db::get_connections(db).await
136}
137
138pub async fn register_connection(
142 db: &ConfigDatabase,
143 connection: &models::RegisterConnection,
144) -> Result<models::Connection, DatabaseFetchError> {
145 let result = crate::db::register_connection(db, connection).await?;
146
147 for player in &connection.players {
148 create_player(db, &connection.connection_id, player).await?;
149 }
150
151 let players = get_players(db, &result.id).await?;
152
153 Ok(models::Connection {
154 id: result.id,
155 name: result.name,
156 created: result.created,
157 updated: result.updated,
158 players,
159 })
160}
161
162pub async fn delete_connection(
166 db: &ConfigDatabase,
167 connection_id: &str,
168) -> Result<(), DatabaseFetchError> {
169 crate::db::delete_connection(db, connection_id).await
170}
171
172pub async fn get_players(
176 db: &ConfigDatabase,
177 connection_id: &str,
178) -> Result<Vec<Player>, DatabaseFetchError> {
179 crate::db::get_players(db, connection_id).await
180}
181
182pub async fn create_player(
186 db: &ConfigDatabase,
187 connection_id: &str,
188 player: &models::RegisterPlayer,
189) -> Result<Player, DatabaseFetchError> {
190 let result = crate::db::create_player(db, connection_id, player).await?;
191
192 #[cfg(feature = "events")]
193 {
194 moosicbox_task::spawn("create_player updated_events", async move {
195 if let Err(e) = crate::events::trigger_players_updated_event().await {
196 moosicbox_assert::die_or_error!("Failed to trigger event: {e:?}");
197 }
198 });
199 }
200
201 Ok(result)
202}
203
204#[derive(Debug, Error)]
205pub enum CreatePlayersError {
206 #[error(transparent)]
207 Db(#[from] DatabaseFetchError),
208 #[error("Invalid connection")]
209 InvalidConnection,
210}
211
212pub async fn create_players(
216 db: &ConfigDatabase,
217 connection_id: &str,
218 players: &[models::RegisterPlayer],
219) -> Result<Vec<Player>, CreatePlayersError> {
220 let connections = crate::db::get_connections(db).await?;
221 if !connections.iter().any(|x| x.id == connection_id) {
222 return Err(CreatePlayersError::InvalidConnection);
223 }
224
225 let mut results = vec![];
226
227 for player in players {
228 results.push(crate::db::create_player(db, connection_id, player).await?);
229 }
230
231 #[cfg(feature = "events")]
232 {
233 moosicbox_task::spawn("create_players updated_events", async move {
234 if let Err(e) = crate::events::trigger_players_updated_event().await {
235 moosicbox_assert::die_or_error!("Failed to trigger event: {e:?}");
236 }
237 });
238 }
239
240 Ok(results)
241}
242
243pub async fn delete_player(db: &ConfigDatabase, player_id: u64) -> Result<(), DatabaseFetchError> {
247 crate::db::delete_player(db, player_id).await?;
248
249 #[cfg(feature = "events")]
250 {
251 moosicbox_task::spawn("delete_player updated_events", async move {
252 if let Err(e) = crate::events::trigger_players_updated_event().await {
253 moosicbox_assert::die_or_error!("Failed to trigger event: {e:?}");
254 }
255 });
256 }
257
258 Ok(())
259}
260
261pub async fn delete_session_playlist_track_by_track_id(
265 db: &LibraryDatabase,
266 id: u64,
267) -> Result<Option<ApiTrack>, DatabaseFetchError> {
268 crate::db::delete_session_playlist_track_by_track_id(db, id).await
269}
270
271pub async fn delete_session_playlist_tracks_by_track_id(
275 db: &LibraryDatabase,
276 ids: Option<&Vec<u64>>,
277) -> Result<Vec<ApiTrack>, DatabaseFetchError> {
278 crate::db::delete_session_playlist_tracks_by_track_id(db, ids).await
279}
280
281pub async fn update_session_audio_output_ids(
285 session: &UpdateSession,
286 db: &ConfigDatabase,
287) -> Result<Vec<String>, DatabaseFetchError> {
288 Ok(match &session.playback_target {
289 PlaybackTarget::AudioZone { audio_zone_id } => {
290 let Some(output) = moosicbox_audio_zone::get_zone(db, *audio_zone_id).await? else {
291 return Ok(vec![]);
292 };
293
294 output
295 .players
296 .into_iter()
297 .map(|x| x.audio_output_id)
298 .collect::<Vec<_>>()
299 }
300 PlaybackTarget::ConnectionOutput { output_id, .. } => vec![output_id.to_owned()],
301 })
302}