use std::{
fs,
io::{self, Cursor},
path::PathBuf,
};
use blake3::Hash;
use lofty::{file::TaggedFileExt, picture::PictureType};
use serde::{Deserialize, Serialize};
use crate::{
cache_dir,
library::image_art::{CacheableArt, ImageArt, cache_image},
};
#[derive(Debug, Deserialize, Serialize, Clone)]
pub enum CoverArt {
Embedded { hash: Hash, source: PathBuf },
File(ImageArt),
}
impl CoverArt {
pub fn from_file(file: impl Into<PathBuf>) -> Result<Self, image::ImageError> {
let image = Self::File(ImageArt::from_file(file)?);
Ok(image)
}
}
impl CacheableArt for CoverArt {
fn cache_file(&self, x: u32, y: u32) -> io::Result<PathBuf> {
match self {
CoverArt::File(image_art) => image_art.cache_file(x, y),
CoverArt::Embedded { hash, source } => {
let mut file = fs::File::open(source)?;
let mut tagged_file = lofty::read_from(&mut file)
.expect("Embedded art already has its metadata read");
let tags = tagged_file.primary_tag_mut().unwrap();
let picture_pos = tags
.pictures()
.iter()
.position(|p| p.pic_type() == PictureType::CoverFront)
.unwrap_or(0);
let picture = tags.remove_picture(picture_pos);
let format = match picture.mime_type().unwrap() {
lofty::picture::MimeType::Png => image::ImageFormat::Png,
lofty::picture::MimeType::Jpeg => image::ImageFormat::Jpeg,
lofty::picture::MimeType::Tiff => image::ImageFormat::Tiff,
lofty::picture::MimeType::Bmp => image::ImageFormat::Bmp,
lofty::picture::MimeType::Gif => image::ImageFormat::Gif,
_ => unreachable!("Embedded cover art is pre-validated on extract"),
};
let reader = Cursor::new(picture.into_data());
let image = image::ImageReader::with_format(reader, format)
.decode()
.unwrap();
let cover_dir = cache_dir().join("covers");
let cache_file = cover_dir.join(format!("{id}_{x}_{x}.webp", id = hash));
if cache_file.exists() {
return Ok(cache_file);
}
cache_image(image, x, y, &cache_file)?;
Ok(cache_file)
}
}
}
}