feat: support cover art + resizing

This commit is contained in:
Lys 2023-10-14 16:43:37 +03:00
parent 07197a6703
commit dc5fdf4b90
Signed by: lyssieth
GPG key ID: C9CF3D614FAA3940
11 changed files with 566 additions and 15 deletions

254
Cargo.lock generated
View file

@ -163,6 +163,18 @@ dependencies = [
"windows-sys",
]
[[package]]
name = "arrayref"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545"
[[package]]
name = "arrayvec"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
[[package]]
name = "async-attributes"
version = "1.1.2"
@ -391,6 +403,12 @@ version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
[[package]]
name = "bit_field"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61"
[[package]]
name = "bitflags"
version = "1.3.2"
@ -406,6 +424,19 @@ dependencies = [
"serde",
]
[[package]]
name = "blake3"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0231f06152bf547e9c2b5194f247cd97aacf6dcd8b15d8e5ec0663f64580da87"
dependencies = [
"arrayref",
"arrayvec",
"cc",
"cfg-if",
"constant_time_eq",
]
[[package]]
name = "block-buffer"
version = "0.10.4"
@ -458,6 +489,12 @@ version = "3.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
[[package]]
name = "bytemuck"
version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6"
[[package]]
name = "byteorder"
version = "1.5.0"
@ -574,6 +611,12 @@ dependencies = [
"tracing-error",
]
[[package]]
name = "color_quant"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
[[package]]
name = "colorchoice"
version = "1.0.0"
@ -595,6 +638,12 @@ version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f"
[[package]]
name = "constant_time_eq"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2"
[[package]]
name = "convert_case"
version = "0.4.0"
@ -678,6 +727,30 @@ dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-deque"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
dependencies = [
"cfg-if",
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7"
dependencies = [
"autocfg",
"cfg-if",
"crossbeam-utils",
"memoffset",
"scopeguard",
]
[[package]]
name = "crossbeam-queue"
version = "0.3.8"
@ -697,6 +770,12 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "crunchy"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
[[package]]
name = "crypto-common"
version = "0.1.6"
@ -884,6 +963,22 @@ version = "2.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
[[package]]
name = "exr"
version = "1.71.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "832a761f35ab3e6664babfbdc6cef35a4860e816ec3916dcfd0882954e98a8a8"
dependencies = [
"bit_field",
"flume",
"half",
"lebe",
"miniz_oxide",
"rayon-core",
"smallvec",
"zune-inflate",
]
[[package]]
name = "eyre"
version = "0.6.8"
@ -909,6 +1004,15 @@ version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
[[package]]
name = "fdeflate"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d329bdeac514ee06249dabc27877490f17f5d371ec693360768b838e19f3ae10"
dependencies = [
"simd-adler32",
]
[[package]]
name = "findshlibs"
version = "0.10.2"
@ -1124,6 +1228,16 @@ dependencies = [
"polyval",
]
[[package]]
name = "gif"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80792593675e051cf94a4b111980da2ba60d4a83e43e0048c5693baab3977045"
dependencies = [
"color_quant",
"weezl",
]
[[package]]
name = "gimli"
version = "0.28.0"
@ -1167,6 +1281,15 @@ dependencies = [
"tracing",
]
[[package]]
name = "half"
version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02b4af3693f1b705df946e9fe5631932443781d0aabb423b62fcd4d73f6d2fd0"
dependencies = [
"crunchy",
]
[[package]]
name = "hashbrown"
version = "0.12.3"
@ -1410,6 +1533,25 @@ dependencies = [
"unicode-normalization",
]
[[package]]
name = "image"
version = "0.24.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f3dfdbdd72063086ff443e297b61695500514b1e41095b6fb9a5ab48a70a711"
dependencies = [
"bytemuck",
"byteorder",
"color_quant",
"exr",
"gif",
"jpeg-decoder",
"num-rational",
"num-traits",
"png",
"qoi",
"tiff",
]
[[package]]
name = "indenter"
version = "0.3.3"
@ -1497,6 +1639,15 @@ version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
[[package]]
name = "jpeg-decoder"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e"
dependencies = [
"rayon",
]
[[package]]
name = "js-sys"
version = "0.3.64"
@ -1524,6 +1675,12 @@ dependencies = [
"spin 0.5.2",
]
[[package]]
name = "lebe"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
[[package]]
name = "libc"
version = "0.2.149"
@ -1615,6 +1772,15 @@ version = "2.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
[[package]]
name = "memoffset"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
dependencies = [
"autocfg",
]
[[package]]
name = "metaflac"
version = "0.2.5"
@ -1663,6 +1829,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
dependencies = [
"adler",
"simd-adler32",
]
[[package]]
@ -1787,6 +1954,17 @@ dependencies = [
"num-traits",
]
[[package]]
name = "num-rational"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.17"
@ -2060,6 +2238,19 @@ version = "0.3.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
[[package]]
name = "png"
version = "0.17.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd75bf2d8dd3702b9707cdbc56a5b9ef42cec752eb8b3bafc01234558442aa64"
dependencies = [
"bitflags 1.3.2",
"crc32fast",
"fdeflate",
"flate2",
"miniz_oxide",
]
[[package]]
name = "poem"
version = "1.3.58"
@ -2262,6 +2453,15 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "qoi"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001"
dependencies = [
"bytemuck",
]
[[package]]
name = "quick-xml"
version = "0.30.0"
@ -2316,10 +2516,12 @@ name = "rave"
version = "0.1.0"
dependencies = [
"audiotags",
"blake3",
"cfg-if",
"color-eyre",
"entities",
"futures",
"image",
"md5",
"migration",
"once_cell",
@ -2340,6 +2542,26 @@ dependencies = [
"url-escape",
]
[[package]]
name = "rayon"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1"
dependencies = [
"either",
"rayon-core",
]
[[package]]
name = "rayon-core"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed"
dependencies = [
"crossbeam-deque",
"crossbeam-utils",
]
[[package]]
name = "readme-rustdocifier"
version = "0.1.1"
@ -2993,6 +3215,12 @@ dependencies = [
"rand_core",
]
[[package]]
name = "simd-adler32"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
[[package]]
name = "slab"
version = "0.4.9"
@ -3387,6 +3615,17 @@ dependencies = [
"once_cell",
]
[[package]]
name = "tiff"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d172b0f4d3fba17ba89811858b9d3d97f928aece846475bbda076ca46736211"
dependencies = [
"flate2",
"jpeg-decoder",
"weezl",
]
[[package]]
name = "time"
version = "0.3.29"
@ -3944,6 +4183,12 @@ version = "0.25.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc"
[[package]]
name = "weezl"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb"
[[package]]
name = "whoami"
version = "1.4.1"
@ -4071,3 +4316,12 @@ name = "zeroize"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9"
[[package]]
name = "zune-inflate"
version = "0.2.54"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02"
dependencies = [
"simd-adler32",
]

View file

@ -10,7 +10,7 @@ unmount:
bash ./mount-tool.sh unmount
run: mount
RAVE_STORAGE_DIR=/tmp/media-for-rave cargo r
RAVE_STORAGE_DIR=/tmp/media-for-rave RAVE_CACHE_DIR=/tmp/cache-for-rave cargo r
refresh:
sea migrate fresh

View file

@ -7,7 +7,8 @@ use serde::{Deserialize, Serialize};
#[sea_orm(table_name = "cover_art")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
pub id: i64,
pub hash: String,
pub path: String,
}

View file

@ -13,12 +13,13 @@ impl MigrationTrait for Migration {
.if_not_exists()
.col(
ColumnDef::new(CoverArt::Id)
.integer()
.big_integer()
.not_null()
.auto_increment()
.primary_key()
.unique_key(),
)
.col(ColumnDef::new(CoverArt::Hash).string().not_null())
.col(ColumnDef::new(CoverArt::Path).string().not_null())
.to_owned(),
)
@ -36,5 +37,6 @@ impl MigrationTrait for Migration {
pub enum CoverArt {
Table,
Id,
Hash,
Path,
}

View file

@ -54,3 +54,5 @@ sentry = { version = "0.31.7", default-features = false, features = [
"tracing",
] }
sentry-tracing = { version = "0.31.7", features = ["backtrace"] }
blake3 = "1.5.0"
image = "0.24.7"

View file

@ -0,0 +1,162 @@
use std::{io::Cursor, path::PathBuf};
use entities::prelude::CoverArt;
use poem::{
http::StatusCode,
web::{Data, Query},
IntoResponse, Response,
};
use poem_ext::db::DbTxn;
use sea_orm::EntityTrait;
use serde::Deserialize;
use tracing::{error, instrument};
use crate::{
authentication::Authentication,
subsonic::{Error, SubsonicResponse},
utils::{self},
};
#[poem::handler]
#[instrument(skip(txn, auth))]
pub async fn get_cover_art(
Data(txn): Data<&DbTxn>,
auth: Authentication,
Query(params): Query<GetCoverArtParams>,
) -> Response {
let u = utils::verify_user(txn.clone(), auth).await;
match u {
Ok(_) => {}
Err(e) => return e.into_response(),
}
let cover_art_id = match params.id.strip_prefix("ca-").ok_or_else(|| {
Error::RequiredParameterMissing(Some("Album IDs must be formatted as `ca-{}`".to_string()))
}) {
Ok(id) => id,
Err(e) => return SubsonicResponse::new_error(e).into_response(),
};
let cover_art_id = match cover_art_id.parse::<i64>() {
Ok(id) => id,
Err(e) => {
error!(
error = &e as &dyn std::error::Error,
"Error parsing cover art ID: {e}"
);
return SubsonicResponse::new_error(Error::Generic(None)).into_response();
}
};
let cover_art = CoverArt::find_by_id(cover_art_id).one(&**txn).await;
let Ok(Some(cover_art)) = cover_art else {
match cover_art {
Ok(Some(_)) => unreachable!("Ok(Some(_)) covered by `let .. else`"),
Ok(None) => {
return SubsonicResponse::new_error(Error::RequestedDataWasNotFound(None))
.into_response()
}
Err(e) => {
error!(
error = &e as &dyn std::error::Error,
"Error getting album: {e}"
);
return SubsonicResponse::new_error(Error::Generic(None)).into_response();
}
}
};
let path = cover_art.path;
let path: PathBuf = path.into();
if !path.exists() {
return SubsonicResponse::new_error(Error::RequestedDataWasNotFound(None)).into_response();
}
let data = match tokio::fs::read(&path).await {
Ok(v) => v,
Err(e) => {
error!(
error = &e as &dyn std::error::Error,
"Error reading cover art file: {e}"
);
return SubsonicResponse::new_error(Error::Generic(None)).into_response();
}
};
let ext = path
.extension()
.expect("File had no extension")
.to_str()
.expect("PathBuf extension is not valid UTF-8");
if params.size.is_none() {
return Response::builder()
.status(StatusCode::OK)
.content_type(format!("image/{ext}"))
.body(data);
}
let size = params.size.expect("params.size.is_none() was false");
match size {
0.. => {}
..=-1 => {
return SubsonicResponse::new_error(Error::Generic(None)).into_response();
}
}
let image = match image::load_from_memory(&data) {
Ok(v) => v,
Err(e) => {
error!(
error = &e as &dyn std::error::Error,
"Error loading image from memory: {e}"
);
return SubsonicResponse::new_error(Error::Generic(None)).into_response();
}
};
#[allow(clippy::cast_sign_loss)]
let resize = image.resize(
size as u32,
size as u32,
image::imageops::FilterType::Lanczos3,
);
let mut buf = Cursor::new(Vec::new());
let res = resize.write_to(
&mut buf,
match ext {
"jpg" => image::ImageOutputFormat::Jpeg(100),
"png" => image::ImageOutputFormat::Png,
"gif" => image::ImageOutputFormat::Gif,
_ => {
return SubsonicResponse::new_error(Error::Generic(None)).into_response();
}
},
);
let () = match res {
Ok(v) => v,
Err(e) => {
error!(
error = &e as &dyn std::error::Error,
"Error writing resized image to buffer: {e}"
);
return SubsonicResponse::new_error(Error::Generic(None)).into_response();
}
};
Response::builder()
.status(StatusCode::OK)
.content_type(format!("image/{ext}"))
.body(buf.into_inner())
}
#[derive(Debug, Clone, Deserialize)]
pub struct GetCoverArtParams {
id: String,
size: Option<i32>,
}

View file

@ -18,6 +18,8 @@ mod start_scan;
mod get_scan_status;
// rest/search3
mod search3;
// rest/getCoverArt
mod get_cover_art;
pub fn build() -> Box<dyn Endpoint<Output = poem::Response>> {
Route::new()
@ -31,5 +33,6 @@ pub fn build() -> Box<dyn Endpoint<Output = poem::Response>> {
.at("/startScan", start_scan::start_scan)
.at("/getScanStatus", get_scan_status::get_scan_status)
.at("/search3", search3::search3)
.at("/getCoverArt", get_cover_art::get_cover_art)
.boxed()
}

View file

@ -1,7 +1,8 @@
use audiotags::MimeType;
use color_eyre::{Report, Result};
use entities::{
genre, music_folder,
prelude::{Genre, MusicFolder},
cover_art, genre, music_folder,
prelude::{CoverArt, Genre, MusicFolder},
};
use futures::StreamExt;
use once_cell::sync::Lazy;
@ -14,7 +15,7 @@ use std::{
path::{Path, PathBuf},
sync::Arc,
};
use tokio::sync::RwLock;
use tokio::{io::AsyncWriteExt, sync::RwLock};
use tracing::{debug, error, info, instrument};
mod walk;
@ -390,7 +391,60 @@ fn get_root_dir() -> PathBuf {
PathBuf::from(root_dir)
}
// fn get_cache_dir() -> PathBuf {
// let cache_dir = std::env::var("RAVE_CACHE_DIR").expect("RAVE_CACHE_DIR not set");
// PathBuf::from(cache_dir)
// }
fn get_cache_dir() -> PathBuf {
let cache_dir = std::env::var("RAVE_CACHE_DIR").expect("RAVE_CACHE_DIR not set");
PathBuf::from(cache_dir)
}
async fn find_or_create_cover_art(
tx: &DatabaseTransaction,
data: &[u8],
mime_type: MimeType,
) -> Result<cover_art::Model, Report> {
let hash = blake3::hash(data).to_string();
debug!("Finding cover art with hash {hash}");
let res = CoverArt::find()
.filter(cover_art::Column::Hash.eq(&hash))
.one(tx)
.await?;
if let Some(cover_art) = res {
return Ok(cover_art);
}
debug!("Trying to create cover art");
let path = get_cache_dir();
let path = path
.join(hash.clone())
.with_extension(mime_type_to_ext(mime_type));
{
let mut file = tokio::fs::File::create(&path).await?;
file.write_all(data).await?;
}
let path = path.to_string_lossy().to_string();
let am = cover_art::ActiveModel {
hash: Set(hash.clone()),
path: Set(path),
..Default::default()
};
let model = CoverArt::insert(am).exec_with_returning(tx).await;
Ok(model?)
}
const fn mime_type_to_ext(mime: MimeType) -> &'static str {
match mime {
MimeType::Png => "png",
MimeType::Jpeg => "jpg",
MimeType::Tiff => "tif",
MimeType::Bmp => "bmp",
MimeType::Gif => "gif",
}
}

View file

@ -2,7 +2,7 @@ use std::borrow::Cow;
use std::path::PathBuf;
use std::sync::Arc;
use audiotags::{AudioTagEdit, FlacTag, Tag};
use audiotags::{AudioTagEdit, FlacTag, MimeType, Tag};
use color_eyre::Report;
use entities::{
album, artist,
@ -14,7 +14,7 @@ use sea_orm::{
};
use time::OffsetDateTime;
use tokio::{fs::File, sync::RwLock};
use tracing::{debug, error, instrument};
use tracing::{debug, error, instrument, warn};
use super::ScanState;
@ -67,6 +67,24 @@ pub async fn handle(
Set(duration)
};
let cover_art = tag.album_cover();
if let Some(cover_art) = cover_art {
let data = match cover_art.mime_type {
MimeType::Png | MimeType::Jpeg | MimeType::Gif => Some(cover_art.data),
_ => {
warn!(
"Unknown cover art mime type: {mime_type:?}",
mime_type = cover_art.mime_type
);
None
}
};
if let Some(data) = data {
let cover_art = super::find_or_create_cover_art(tx, data, cover_art.mime_type).await?;
am.cover_art_id = Set(Some(cover_art.id));
}
}
am.created = Set(OffsetDateTime::now_utc());
am.size = Set(meta
.len()
@ -122,6 +140,24 @@ async fn find_album(
.map(|c| c.round().rem_euclid(2f64.powi(32)) as i64) // TODO: figure out how to do this properly
.unwrap_or_default());
am.created = Set(OffsetDateTime::now_utc());
let cover_art = tag.album_cover();
if let Some(cover_art) = cover_art {
let data = match cover_art.mime_type {
MimeType::Png | MimeType::Jpeg | MimeType::Gif => Some(cover_art.data),
_ => {
warn!(
"Unknown cover art mime type: {mime_type:?}",
mime_type = cover_art.mime_type
);
None
}
};
if let Some(data) = data {
let cover_art = super::find_or_create_cover_art(tx, data, cover_art.mime_type).await?;
am.cover_art_id = Set(Some(cover_art.id));
}
}
let model = Album::insert(am).exec_with_returning(tx).await;

View file

@ -2,7 +2,7 @@ use std::borrow::Cow;
use std::path::PathBuf;
use std::sync::Arc;
use audiotags::{AudioTagEdit, Id3v2Tag, Tag};
use audiotags::{AudioTagEdit, Id3v2Tag, MimeType, Tag};
use color_eyre::Report;
use entities::{
album, artist,
@ -14,7 +14,7 @@ use sea_orm::{
};
use time::OffsetDateTime;
use tokio::{fs::File, sync::RwLock};
use tracing::{debug, error, instrument};
use tracing::{debug, error, instrument, warn};
use super::ScanState;
@ -67,6 +67,24 @@ pub async fn handle(
Set(duration)
};
let cover_art = tag.album_cover();
if let Some(cover_art) = cover_art {
let data = match cover_art.mime_type {
MimeType::Png | MimeType::Jpeg | MimeType::Gif => Some(cover_art.data),
_ => {
warn!(
"Unknown cover art mime type: {mime_type:?}",
mime_type = cover_art.mime_type
);
None
}
};
if let Some(data) = data {
let cover_art = super::find_or_create_cover_art(tx, data, cover_art.mime_type).await?;
am.cover_art_id = Set(Some(cover_art.id));
}
}
am.created = Set(OffsetDateTime::now_utc());
am.size = Set(meta
.len()
@ -122,6 +140,24 @@ async fn find_album(
.map(|c| c.round().rem_euclid(2f64.powi(32)) as i64) // TODO: figure out how to do this properly
.unwrap_or_default());
am.created = Set(OffsetDateTime::now_utc());
let cover_art = tag.album_cover();
if let Some(cover_art) = cover_art {
let data = match cover_art.mime_type {
MimeType::Png | MimeType::Jpeg | MimeType::Gif => Some(cover_art.data),
_ => {
warn!(
"Unknown cover art mime type: {mime_type:?}",
mime_type = cover_art.mime_type
);
None
}
};
if let Some(data) = data {
let cover_art = super::find_or_create_cover_art(tx, data, cover_art.mime_type).await?;
am.cover_art_id = Set(Some(cover_art.id));
}
}
let model = Album::insert(am).exec_with_returning(tx).await;

View file

@ -18,12 +18,13 @@ pub struct Artist {
pub starred: Option<String>,
}
#[allow(clippy::cast_sign_loss)]
impl From<artist::Model> for Artist {
fn from(artist: artist::Model) -> Self {
Self {
id: format!("ar-{}", artist.id),
name: artist.name,
cover_art: artist.cover_art_id.map(|v| format!("ca-{}", v)),
cover_art: artist.cover_art_id.map(|v| format!("ca-{v}")),
artist_image_url: artist.artist_image_url,
album_count: artist.album_count as u64,
starred: artist