Something went wrong. Try again.
An extensible music server written in Rust , single binary, zero dependency
scrobble audio atproto radio streaming jellyfin subsonic navidrome
Something went wrong. Try again.
16 kB · 429 lines
Rust
at master
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430#[macro_use]extern crate log;
#[cfg(test)]mod tests;
pub mod addons;pub mod analysis;pub mod analytics;pub mod atproto_sync;pub mod core;pub mod event;pub mod history;pub mod library;pub mod media_controls;pub mod mixer;pub mod play_stats;pub mod playback;pub mod playlist;pub mod remote;mod remote_waveform;pub mod scrobbler;pub mod server;pub mod servers;pub mod tracklist;pub mod api { #[path = ""] pub mod music { use music_player_entity::folder; use music_player_types::types::Playlist;
use self::v1alpha1::{GetFolderDetailsResponse, GetPlaylistDetailsResponse};
#[path = "music.v1alpha1.rs"] pub mod v1alpha1;
impl From<folder::Model> for GetFolderDetailsResponse { fn from(model: folder::Model) -> Self { Self { id: model.id, name: model.name, playlists: model.playlists.into_iter().map(Into::into).collect(), } } }
impl From<GetPlaylistDetailsResponse> for Playlist { fn from(val: GetPlaylistDetailsResponse) -> Self { Playlist { id: val.id, name: val.name, description: Some(val.description), tracks: val.tracks.into_iter().map(Into::into).collect(), track_count: Some(val.track_count).filter(|count| *count > 0), } } }
impl From<Playlist> for GetPlaylistDetailsResponse { fn from(playlist: Playlist) -> Self { Self { track_count: playlist.len(), id: playlist.id, name: playlist.name, description: playlist.description.unwrap_or_default(), tracks: playlist.tracks.into_iter().map(Into::into).collect(), } } } }
#[path = ""] pub mod objects { use self::v1alpha1::Playlist; use music_player_entity::playlist; use music_player_types::types;
#[path = "objects.v1alpha1.rs"] pub mod v1alpha1;
impl From<playlist::Model> for Playlist { fn from(model: playlist::Model) -> Self { Self { track_count: model.tracks.len() as u32, id: model.id, name: model.name, description: model.description.unwrap_or_default(), tracks: model.tracks.into_iter().map(Into::into).collect(), } } }
impl From<Playlist> for types::Playlist { fn from(val: Playlist) -> Self { types::Playlist { id: val.id, name: val.name, description: Some(val.description), tracks: val.tracks.into_iter().map(Into::into).collect(), track_count: Some(val.track_count).filter(|count| *count > 0), } } } }
#[path = ""] pub mod metadata { use music_player_entity::{album, artist, track}; use music_player_types::types;
use self::v1alpha1::{Album, Artist, ArtistSong, Song, SongArtist, Track};
#[path = "metadata.v1alpha1.rs"] pub mod v1alpha1;
impl From<artist::Model> for Artist { fn from(model: artist::Model) -> Self { Self { id: model.id, name: model.name, picture: model.picture.unwrap_or_default(), songs: model.tracks.into_iter().map(Into::into).collect(), albums: model.albums.into_iter().map(Into::into).collect(), ..Default::default() } } }
impl From<album::Model> for Album { fn from(model: album::Model) -> Self { Self { id: model.id, title: model.title, cover: model.cover.unwrap_or_default(), artist: model.artist, year: i32::try_from(model.year.unwrap_or_default()).unwrap_or_default(), tracks: model.tracks.into_iter().map(Into::into).collect(), ..Default::default() } } }
impl From<track::Model> for Track { fn from(model: track::Model) -> Self { Self { id: model.id, title: model.title, uri: model.uri, duration: model.duration.unwrap_or(0.0), track_number: i32::try_from(model.track.unwrap_or_default()).unwrap(), // Zero means "not part of a set", which is what the clients // read as "do not group by disc". disc_number: i32::try_from(model.disc.unwrap_or_default()).unwrap_or_default(), bitrate: model.bitrate.unwrap_or_default(), sample_rate: model.sample_rate.unwrap_or_default(), liked: model.liked, key: model.key, bpm: model.bpm, artists: model.artists.into_iter().map(Into::into).collect(), album: Some(model.album.into()), artist: model.artist, ..Default::default() } } }
impl From<track::Model> for Song { fn from(model: track::Model) -> Self { Self { id: model.id, title: model.title, duration: model.duration.unwrap_or_default(), track_number: i32::try_from(model.track.unwrap_or_default()).unwrap(), disc_number: i32::try_from(model.disc.unwrap_or_default()).unwrap_or_default(), uri: model.uri, album: model.album.title.clone(), artist: model.artist, key: model.key, bpm: model.bpm, artists: model.artists.into_iter().map(Into::into).collect(), } } }
impl From<types::Track> for Song { fn from(track: types::Track) -> Self { Self { id: track.id, title: track.title, duration: track.duration.unwrap_or_default(), track_number: track.track_number.unwrap_or_default() as i32, disc_number: track.disc_number as i32, uri: track.uri, album: track .album .as_ref() .map(|a| a.title.clone()) .unwrap_or_default(), artist: track.artist, // A remote provider's tracks are not analysed here, so // there is nothing to report rather than a zero to show. key: None, bpm: None, artists: track.artists.into_iter().map(Into::into).collect(), } } }
impl From<artist::Model> for SongArtist { fn from(model: artist::Model) -> Self { Self { id: model.id, name: model.name, } } }
impl From<types::Artist> for SongArtist { fn from(artist: types::Artist) -> Self { Self { id: artist.id, name: artist.name, } } }
impl From<track::Model> for ArtistSong { fn from(model: track::Model) -> Self { Self { id: model.id, title: model.title, duration: model.duration.unwrap_or_default(), track_number: i32::try_from(model.track.unwrap_or_default()).unwrap(), artists: model.artists.into_iter().map(Into::into).collect(), album: Some(model.album.into()), artist: model.artist, ..Default::default() } } }
impl From<ArtistSong> for types::Track { fn from(val: ArtistSong) -> Self { types::Track { id: val.id, title: val.title, duration: Some(val.duration), track_number: Some(u32::try_from(val.track_number).unwrap_or_default()), disc_number: u32::try_from(val.disc_number).unwrap_or_default(), artists: val.artists.into_iter().map(Into::into).collect(), album: val.album.map(|album| album.into()), artist: val.artist, ..Default::default() } } }
impl From<Track> for types::Track { fn from(val: Track) -> Self { types::Track { id: val.id, title: val.title, uri: val.uri, duration: Some(val.duration), track_number: Some(u32::try_from(val.track_number).unwrap_or_default()), disc_number: u32::try_from(val.disc_number).unwrap_or_default(), artists: val.artists.into_iter().map(Into::into).collect(), artist: val.artist, album: val.album.map(|album| album.into()), // Zero on the wire means unknown. bitrate: Some(val.bitrate).filter(|rate| *rate > 0), sample_rate: Some(val.sample_rate).filter(|rate| *rate > 0), liked: val.liked, } } }
impl From<Track> for track::Model { fn from(val: Track) -> Self { track::Model { id: val.id, title: val.title, uri: val.uri, duration: Some(val.duration), track: Some(u32::try_from(val.track_number).unwrap_or_default()), disc: u32::try_from(val.disc_number).ok().filter(|disc| *disc > 0), artists: val.artists.into_iter().map(Into::into).collect(), artist: val.artist, // The tracklist holds these, and the now-playing readout // reads them back — dropping them here is what made a // remote track show no bitrate and probe the stream for it. bitrate: Some(val.bitrate).filter(|rate| *rate > 0), sample_rate: Some(val.sample_rate).filter(|rate| *rate > 0), // Rides along so a queued track still knows whether the // source has it liked; without it the heart falls back to // a snapshot list, which can always be incomplete. liked: val.liked, // Same reason: the queue shows these columns too, and a // track that lost them on the way in would show blanks // where the library shows a key. key: val.key, bpm: val.bpm, album: val.album.map(Into::into).unwrap_or_default(), ..Default::default() } } }
impl From<types::Track> for Track { fn from(track: types::Track) -> Self { Self { id: track.id, title: track.title, uri: track.uri, duration: track.duration.unwrap_or_default(), track_number: i32::try_from(track.track_number.unwrap_or_default()).unwrap(), disc_number: i32::try_from(track.disc_number).unwrap(), artists: track.artists.into_iter().map(Into::into).collect(), artist: track.artist, album: track.album.map(|album| album.into()), bitrate: track.bitrate.unwrap_or_default(), sample_rate: track.sample_rate.unwrap_or_default(), liked: track.liked, ..Default::default() } } }
impl From<Artist> for types::Artist { fn from(val: Artist) -> Self { types::Artist { id: val.id, name: val.name, picture: Some(val.picture), albums: val.albums.into_iter().map(Into::into).collect(), songs: val.songs.into_iter().map(Into::into).collect(), } } }
impl From<Artist> for artist::Model { fn from(val: Artist) -> Self { artist::Model { id: val.id, name: val.name, albums: val.albums.into_iter().map(Into::into).collect(), ..Default::default() } } }
impl From<types::Artist> for Artist { fn from(artist: types::Artist) -> Self { Self { id: artist.id, name: artist.name, picture: artist.picture.unwrap_or_default(), ..Default::default() } } }
impl From<SongArtist> for types::Artist { fn from(val: SongArtist) -> Self { types::Artist { id: val.id, name: val.name, ..Default::default() } } }
impl From<Song> for types::Track { fn from(val: Song) -> Self { types::Track { id: val.id, title: val.title, duration: Some(val.duration), track_number: Some(u32::try_from(val.track_number).unwrap_or_default()), disc_number: u32::try_from(val.disc_number).unwrap_or_default(), artists: val.artists.into_iter().map(Into::into).collect(), ..Default::default() } } }
impl From<Album> for types::Album { fn from(val: Album) -> Self { types::Album { id: val.id, title: val.title, cover: Some(val.cover), artist: val.artist.clone(), year: Some(u32::try_from(val.year).unwrap_or_default()), artist_id: Some(format!("{:x}", md5::compute(val.artist.as_str()))), tracks: val.tracks.into_iter().map(Into::into).collect(), } } }
impl From<Album> for album::Model { fn from(val: Album) -> Self { album::Model { id: val.id, title: val.title, cover: Some(val.cover), artist: val.artist.clone(), year: Some(u32::try_from(val.year).unwrap_or_default()), artist_id: Some(format!("{:x}", md5::compute(val.artist.as_str()))), ..Default::default() } } }
impl From<types::Album> for Album { fn from(album: types::Album) -> Self { Self { id: album.id, title: album.title, cover: album.cover.unwrap_or_default(), artist: album.artist, year: i32::try_from(album.year.unwrap_or_default()).unwrap_or_default(), tracks: album.tracks.into_iter().map(Into::into).collect(), ..Default::default() } } } }}