anni_provider/providers/
no_cache.rs1use crate::{
2 strict_album_path, AnniProvider, AudioInfo, AudioResourceReader, Range, ResourceReader,
3};
4use std::borrow::Cow;
5use std::collections::{HashSet, VecDeque};
6use std::io::SeekFrom;
7use std::num::NonZeroU8;
8use std::path::PathBuf;
9use tokio::io::{AsyncReadExt, AsyncSeekExt};
10use uuid::Uuid;
11
12pub struct NoCacheStrictLocalProvider {
17 pub root: PathBuf,
19 pub layer: usize,
21}
22
23#[async_trait::async_trait]
24impl AnniProvider for NoCacheStrictLocalProvider {
25 async fn albums(&self) -> crate::Result<HashSet<Cow<str>>> {
30 let mut albums = HashSet::with_capacity(32);
33
34 let mut vis = VecDeque::from([(self.root.clone(), 0)]);
36 while let Some((ref path, layer)) = vis.pop_front() {
37 log::debug!("Walking dir: {path:?}");
38 let mut reader = path.read_dir()?;
39 if layer == self.layer {
40 while let Some(entry) = reader.next() {
41 let entry = entry?;
42 match Uuid::parse_str(&entry.file_name().to_string_lossy()) {
43 Ok(album_id) => {
44 albums.insert(album_id.to_string());
45 }
46 _ => log::warn!("Unexpected dir: {path:?}"),
47 }
48 }
49 } else {
50 while let Some(entry) = reader.next() {
51 let entry = entry?;
52 if entry.metadata()?.is_dir() {
53 vis.push_back((entry.path(), layer + 1));
54 }
55 }
56 }
57 }
58
59 Ok(albums.into_iter().map(Cow::Owned).collect())
60 }
61
62 async fn get_audio(
63 &self,
64 album_id: &str,
65 disc_id: NonZeroU8,
66 track_id: NonZeroU8,
67 range: Range,
68 ) -> crate::Result<AudioResourceReader> {
69 let mut audio = strict_album_path(&self.root, album_id, self.layer);
70 audio.push(disc_id.get().to_string());
71 audio.push(format!("{track_id}.flac"));
72
73 if !audio.exists() {
74 return Err(crate::ProviderError::FileNotFound);
75 }
76
77 let mut file = tokio::fs::File::open(audio).await?;
78 let metadata = file.metadata().await?;
79 let file_size = metadata.len();
80
81 file.seek(SeekFrom::Start(range.start)).await?;
82 let file = file.take(range.length_limit(file_size));
83 let reader = Box::pin(file);
84 let (duration, reader) = crate::utils::read_duration(reader, range).await?;
85
86 Ok(AudioResourceReader {
87 info: AudioInfo {
88 extension: "flac".to_string(),
89 size: file_size as usize,
90 duration,
91 },
92 range: Range {
93 start: range.start,
94 end: Some(range.end.unwrap_or(file_size - 1)),
95 total: Some(file_size),
96 },
97 reader,
98 })
99 }
100
101 async fn get_cover(
102 &self,
103 album_id: &str,
104 disc_id: Option<NonZeroU8>,
105 ) -> crate::Result<ResourceReader> {
106 let mut cover = strict_album_path(&self.root, album_id, self.layer);
107 if let Some(disc_id) = disc_id {
108 cover.push(disc_id.get().to_string());
109 }
110 cover.push(format!("cover.jpg"));
111
112 if !cover.exists() {
113 return Err(crate::ProviderError::FileNotFound);
114 }
115
116 let file = tokio::fs::File::open(cover).await?;
117 Ok(Box::pin(file))
118 }
119
120 async fn reload(&mut self) -> crate::Result<()> {
121 Ok(())
122 }
123}