use {
crate::{
getopt,
serve::media::{
filter::MediaFilter,
item::MediaItem,
scanner::{FsScanner, MediaScanner},
stats::FilterStats,
types::MediaType,
},
},
std::path::{Path, PathBuf},
};
pub struct MediaGallery {
scanner: Box<dyn MediaScanner>,
directory: PathBuf,
cached_items: Option<Vec<MediaItem>>,
}
impl MediaGallery {
pub fn new(directory: PathBuf, load_metadata: bool) -> Self {
Self {
scanner: Box::new(FsScanner::with_threads(
load_metadata,
getopt!(gallery.load_threads),
)),
directory,
cached_items: None,
}
}
pub fn with_scanner(directory: PathBuf, scanner: Box<dyn MediaScanner>) -> Self {
Self {
scanner,
directory,
cached_items: None,
}
}
pub async fn get_items(&mut self) -> Result<&[MediaItem], std::io::Error> {
if self.cached_items.is_none() {
let items = self.scanner.scan(&self.directory).await?;
self.cached_items = Some(items);
}
Ok(self.cached_items.as_ref().expect("cached_items is None"))
}
pub async fn get_filtered_items(
&mut self,
filter: &MediaFilter,
) -> Result<Vec<MediaItem>, std::io::Error> {
Ok(self
.get_items()
.await?
.iter()
.filter(|item| filter.matches(item))
.cloned()
.collect())
}
pub async fn refresh(&mut self) -> Result<&[MediaItem], std::io::Error> {
self.cached_items = Some(self.scanner.scan(&self.directory).await?);
Ok(self.cached_items.as_ref().expect("cached_items is None"))
}
pub fn filter_by_type(&self, media_type: &MediaType) -> Vec<MediaItem> {
self.cached_items
.as_ref()
.map(|items| {
items
.iter()
.filter(|item| item.media_type() == media_type)
.cloned()
.collect()
})
.unwrap_or_default()
}
pub fn search(&self, query: &str) -> Vec<MediaItem> {
self.cached_items
.as_ref()
.map(|items| {
items
.iter()
.filter(|item| item.matches_search(query))
.cloned()
.collect()
})
.unwrap_or_default()
}
pub fn directory(&self) -> &Path {
&self.directory
}
pub fn get_filter_stats(&self) -> FilterStats {
FilterStats::from_items(self.cached_items.as_deref().unwrap_or(&[]))
}
}