anni_provider/providers/
strict.rs1use 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}