Skip to main content

anni_provider/providers/
strict.rs

1use crate::{
2    AnniProvider, AudioResourceReader, FileEntry, FileSystemProvider, ProviderError, Range,
3    ResourceReader, Result,
4};
5use async_trait::async_trait;
6use futures::StreamExt;
7use std::borrow::Cow;
8use std::collections::{HashMap, HashSet, VecDeque};
9use std::num::NonZeroU8;
10use std::path::PathBuf;
11use uuid::Uuid;
12
13pub struct CommonStrictProvider {
14    root: PathBuf,
15    layer: usize,
16    fs: Box<dyn FileSystemProvider + Send + Sync>,
17    folders: HashMap<String, FileEntry>,
18}
19
20impl CommonStrictProvider {
21    pub async fn new(
22        root: PathBuf,
23        layer: usize,
24        fs: Box<dyn FileSystemProvider + Send + Sync>,
25    ) -> Result<Self> {
26        let mut me = Self {
27            root,
28            layer,
29            fs,
30            folders: HashMap::new(),
31        };
32        me.reload().await?;
33        Ok(me)
34    }
35}
36
37#[async_trait]
38impl AnniProvider for CommonStrictProvider {
39    async fn albums(&self) -> Result<HashSet<Cow<str>>> {
40        Ok(self
41            .folders
42            .keys()
43            .map(|c| Cow::Borrowed(c.as_str()))
44            .collect())
45    }
46
47    async fn get_audio(
48        &self,
49        album_id: &str,
50        disc_id: NonZeroU8,
51        track_id: NonZeroU8,
52        range: Range,
53    ) -> Result<AudioResourceReader> {
54        let disc = self.get_disc(album_id, disc_id).await?;
55        let file = self
56            .fs
57            .get_file_entry_by_prefix(&disc.path, &format!("{track_id}."))
58            .await?;
59        self.fs.get_audio_file(&file.path, range).await
60    }
61
62    async fn get_cover(
63        &self,
64        album_id: &str,
65        disc_id: Option<NonZeroU8>,
66    ) -> Result<ResourceReader> {
67        match disc_id {
68            Some(disc_id) => {
69                let disc = self.get_disc(album_id, disc_id).await?;
70                let cover = self
71                    .fs
72                    .get_file_entry_by_prefix(&disc.path, "cover.jpg")
73                    .await?;
74                self.fs.get_file(&cover.path, Range::FULL).await
75            }
76            None => {
77                let album = self
78                    .folders
79                    .get(album_id)
80                    .ok_or(ProviderError::FileNotFound)?;
81                let cover = self
82                    .fs
83                    .get_file_entry_by_prefix(&album.path, "cover.jpg")
84                    .await?;
85                self.fs.get_file(&cover.path, Range::FULL).await
86            }
87        }
88    }
89
90    async fn reload(&mut self) -> Result<()> {
91        self.fs.reload().await?;
92        self.reload_albums().await?;
93        Ok(())
94    }
95}
96
97impl CommonStrictProvider {
98    pub async fn get_disc(&self, album_id: &str, disc_id: NonZeroU8) -> Result<FileEntry> {
99        let folder = self
100            .folders
101            .get(album_id)
102            .ok_or(ProviderError::FileNotFound)?;
103        let mut folders = self.fs.children(&folder.path).await?;
104        while let Some(folder) = folders.next().await {
105            if folder.name == format!("{disc_id}") {
106                return Ok(folder);
107            }
108        }
109        Err(ProviderError::FileNotFound)
110    }
111
112    pub async fn reload_albums(&mut self) -> Result<()> {
113        self.folders.clear();
114
115        let mut vis = VecDeque::from([(self.root.clone(), 0)]);
116        while let Some((ref path, layer)) = vis.pop_front() {
117            log::debug!("Walking dir: {path:?}");
118            let mut reader = self.fs.children(path).await?;
119            if layer == self.layer {
120                while let Some(entry) = reader.next().await {
121                    match Uuid::parse_str(&entry.name) {
122                        Ok(album_id) => {
123                            self.folders.insert(album_id.to_string(), entry);
124                        }
125                        _ => log::warn!("Unexpected dir: {path:?}"),
126                    }
127                }
128            } else {
129                while let Some(entry) = reader.next().await {
130                    vis.push_back((entry.path, layer + 1));
131                }
132            }
133        }
134
135        Ok(())
136    }
137}