chilen_backend/music_lib/state/
playlist.rs1use std::{collections::HashSet, hash::Hash, sync::Arc, time::Duration};
2
3use log::warn;
4use serde::{Deserialize, Serialize};
5
6#[cfg(test)]
7use crate::music_lib::indexer::CacheMode;
8use crate::{
9 Error,
10 music_lib::{
11 indexer::{
12 self,
13 covers::{Cover, get_playlist_cover},
14 },
15 state::{MusicLibrary, Track},
16 },
17};
18
19#[derive(Clone, Debug, PartialEq, Eq, Hash)]
20pub struct Playlist {
21 pub name: String,
22 pub tracks: Vec<Arc<Track>>,
23 pub duration: Duration,
24 pub cover: Cover,
25 pub id: u64,
30 pub(crate) unmatched: Vec<u64>,
31}
32
33impl Playlist {
34 pub(super) fn load(lib: &MusicLibrary, loaded: ConfPlaylist, config: indexer::Config) -> Self {
35 let result = lib.tracks_from_hashes(loaded.track_hashes);
36 if !result.unmatched.is_empty() {
37 warn!(
38 "{} missing tracks in playlist {}",
39 result.unmatched.len(),
40 loaded.name
41 );
42 }
43 let duration = result.matched.iter().map(|t| t.duration).sum();
44
45 #[cfg(not(test))]
46 let cover =
47 get_playlist_cover(&loaded.name, config, &result.matched).unwrap_or(Cover::none());
48 #[cfg(test)]
49 let cover = if config.cache_mode != CacheMode::Disabled {
50 get_playlist_cover(&loaded.name, config, &result.matched).unwrap_or(Cover::none())
51 } else {
52 Cover::none()
53 };
54
55 Self {
56 name: loaded.name,
57 tracks: result.matched,
58 duration,
59 unmatched: result.unmatched,
60 id: loaded.id,
61 cover,
62 }
63 }
64
65 pub(crate) fn remove_tracks(&mut self, mut ids: Vec<usize>) -> Result<(), Error> {
66 ids.sort();
67 let mut unique = ids.clone();
68 unique.dedup();
69 if unique != ids {
70 return Err(Error::DuplicateItems);
71 }
72 if ids.iter().find(|i| i >= &&self.tracks.len()).is_some() {
73 return Err(Error::IndexOutOfBounds);
74 }
75 let remove_set: HashSet<usize> = ids.into_iter().collect();
76 self.tracks = self
77 .tracks
78 .drain(..)
79 .enumerate()
80 .filter_map(|(i, t)| {
81 if remove_set.contains(&i) {
82 None
83 } else {
84 Some(t)
85 }
86 })
87 .collect();
88 Ok(())
89 }
90}
91
92#[derive(Clone, Debug, Serialize, Deserialize)]
93pub(crate) struct ConfPlaylist {
94 name: String,
95 id: u64,
96 track_hashes: Vec<u64>,
97}
98
99impl From<Playlist> for ConfPlaylist {
100 fn from(value: Playlist) -> Self {
101 let mut track_hashes: Vec<_> = value.tracks.iter().map(|t| t.hash_self()).collect();
102 track_hashes.extend(value.unmatched);
103 Self {
104 name: value.name,
105 id: value.id,
106 track_hashes,
107 }
108 }
109}