use super::images::{
generate_album_art, generate_album_art_group, generate_full_album_art,
generate_full_album_art_group,
};
use super::track::{get_path_from_track, get_tracks_by_release_id};
use crate::app::settings::{get_db_connection, read_config, ReportType, Settings};
use crate::entity::{
album::{self, ActiveModel as AlbumAModel, Model as AlbumModel},
media::{self},
music::{self, Model as MusicModel},
report::{self},
track::{self, ActiveModel as TrackAModel},
};
use crate::error::error::{CustomError, TagError};
use crate::media_tags::tags::{
get_tags_from_path, EitherAlbum, EitherFileTags, EitherTrack, FileTags,
};
use crate::media_validator::reports::{delete_reports, MusicValidator};
use lofty::file::TaggedFile;
#[allow(unused_imports)]
use log::info;
use log::warn;
use sea_orm::{entity::*, query::*, DatabaseConnection, Iden};
use sea_orm::{
ActiveValue,
EntityTrait::{self},
Iterable, ModelTrait, QueryFilter, Set,
};
use std::path::Path;
use std::time::Instant;
#[allow(dead_code)]
pub async fn get_albums() -> Result<Vec<AlbumModel>, CustomError> {
let db = get_db_connection().await?;
let albums = album::Entity::find()
.order_by_asc(album::Column::Title)
.all(&db)
.await?;
Ok(albums)
}
fn generate_album_art_path(release_id: String, art_subdir: String) -> String {
let config = read_config().expect("Failed to set up application settings");
let settings = config.try_deserialize::<Settings>().unwrap();
String::from(format!(
"{}/{}/{}.{}",
settings.cache_dir, art_subdir, release_id, "jpg"
))
}
pub async fn generate_album_art_by_release_id(release_id: String) -> Result<String, CustomError> {
let tracks = get_tracks_by_release_id(release_id.clone()).await?;
let art_path = generate_album_art_path(release_id.clone(), String::from("album_art"));
if Path::new(&String::from(format!("{}", art_path))).exists() {
return Ok("Ok".to_string());
}
for t in tracks.iter() {
let path = get_path_from_track(t.clone()).await?;
let tag = get_tags_from_path(path, true).unwrap();
match generate_album_art(&tag) {
Ok(_) => return Ok("Ok".to_string()),
Err(_) => continue,
}
}
Err(CustomError::TagErr(TagError {
tag_name: String::from("Cover"),
}))
}
pub async fn generate_album_art_group_by_release_id(
mb_release_group_id: String,
) -> Result<String, CustomError> {
let album = get_earliest_album_by_release_group(mb_release_group_id).await?;
let tracks = get_tracks_by_release_id(album.mb_release_id.clone()).await?;
let art_path =
generate_album_art_path(album.mb_release_id.clone(), String::from("album_art_group"));
if Path::new(&String::from(format!("{}", art_path))).exists() {
return Ok("Ok".to_string());
}
for t in tracks.iter() {
let path = get_path_from_track(t.clone()).await?;
let tag = get_tags_from_path(path, true).unwrap();
match generate_album_art_group(&tag) {
Ok(_) => return Ok("Ok".to_string()),
Err(_) => continue,
}
}
Err(CustomError::TagErr(TagError {
tag_name: String::from("Cover"),
}))
}
pub async fn generate_album_art_group_full_by_release_id(
mb_release_group_id: String,
) -> Result<String, CustomError> {
let album = get_earliest_album_by_release_group(mb_release_group_id).await?;
let tracks = get_tracks_by_release_id(album.mb_release_id.clone()).await?;
let art_path = generate_album_art_path(
album.mb_release_id.clone(),
String::from("album_art_group_full"),
);
if Path::new(&String::from(format!("{}", art_path))).exists() {
return Ok("Ok".to_string());
}
for t in tracks.iter() {
let path = get_path_from_track(t.clone()).await?;
let tag = get_tags_from_path(path, true).unwrap();
match generate_full_album_art_group(&tag) {
Ok(_) => return Ok("Ok".to_string()),
Err(_) => continue,
}
}
Err(CustomError::TagErr(TagError {
tag_name: String::from("Cover"),
}))
}
pub async fn generate_album_art_full_by_release_id(
release_id: String,
) -> Result<String, CustomError> {
let tracks = get_tracks_by_release_id(release_id.clone()).await?;
let art_path = generate_album_art_path(release_id.clone(), String::from("album_art_full"));
if Path::new(&String::from(format!("{}", art_path))).exists() {
return Ok("Ok".to_string());
}
for t in tracks.iter() {
let path = get_path_from_track(t.clone()).await?;
let tag = get_tags_from_path(path, true).unwrap();
match generate_full_album_art(&tag) {
Ok(_) => return Ok("Ok".to_string()),
Err(_) => continue,
}
}
Err(CustomError::TagErr(TagError {
tag_name: String::from("Cover"),
}))
}
#[allow(dead_code)]
#[deprecated]
pub async fn check_albums() -> Result<String, CustomError> {
warn!("Running check_albums");
let before = Instant::now();
let db = get_db_connection().await?;
let mut music_pages = music::Entity::find()
.order_by_asc(music::Column::Id)
.paginate(&db, 50);
let mut i = 0;
delete_reports().await?;
while let Some(music) = music_pages.fetch_and_next().await? {
i = i + music.len();
check_albums_paginate(music, &db).await?;
}
println!("Timing Complete check_albums in {:.2?}", before.elapsed());
Ok("Ok".to_string())
}
#[allow(dead_code)]
pub async fn check_albums_paginate(
music: Vec<MusicModel>,
db: &DatabaseConnection,
) -> Result<String, CustomError> {
warn!("check_albums_paginate");
let _before = Instant::now();
let mut inserted_tracks: Vec<track::ActiveModel> = Vec::new();
let mut inserted_albums: Vec<album::ActiveModel> = Vec::new();
let mut inserted_reports: Vec<report::ActiveModel> = Vec::new();
let mut updated_music: Vec<music::ActiveModel> = Vec::new();
let _tot = music.len();
let mut album_ids: Vec<String> = Vec::new();
for m in music.iter() {
warn!("\tComparing mtimes current: {} previous: {}", m.mtime, m.processed_mtime);
if m.mtime.as_str() != m.processed_mtime.as_str() {
warn!("\tmtimes differ");
let new_mtime = m.mtime.as_str();
let media = m
.find_related(media::Entity)
.one(db)
.await?
.expect("Could not find parent media record");
let mut update_music = false;
if media.file_type != "MPEG" {
continue;
}
let retrieved_tags = get_tags_from_path(media.path.clone(), false);
warn!("\tFound tags {}", retrieved_tags.is_ok());
let tags = match retrieved_tags {
Ok(x) => x,
Err(e) => {
warn!("{}", e);
inserted_reports.push(report::ActiveModel {
id: ActiveValue::not_set(),
music_id: ActiveValue::set(Some(m.id)),
media_id: ActiveValue::not_set(),
report_type: ActiveValue::set(ReportType::NoTag.to_string()),
});
continue;
}
};
let _track_model = get_track_details(media.clone(), &tags).await;
match _track_model {
Ok(EitherTrack::Left(mut x)) => {
x.music_id = Set(m.id);
inserted_tracks.push(x);
update_music = true;
}
Ok(EitherTrack::Right(mut x)) => {
warn!("\tTrack retrieval missing a mandatory tag");
inserted_reports.append(&mut x);
}
Err(_) => inserted_reports.push(report::ActiveModel {
id: ActiveValue::not_set(),
music_id: ActiveValue::set(Some(m.id)),
media_id: ActiveValue::not_set(),
report_type: ActiveValue::set(ReportType::NoAlbum.to_string()),
}),
}
match get_album_details(media.clone(), &tags).await {
Ok(EitherAlbum::Left(x)) => {
if !album_ids.contains(&x.mb_release_id.clone().unwrap().clone()) {
album_ids.push(x.mb_release_id.clone().unwrap().clone());
inserted_albums.push(x);
}
update_music = true;
}
Ok(EitherAlbum::Right(mut x)) => {
inserted_reports.append(&mut x);
}
Err(_) => inserted_reports.push(report::ActiveModel {
id: ActiveValue::not_set(),
music_id: ActiveValue::set(Some(m.id)),
media_id: ActiveValue::not_set(),
report_type: ActiveValue::set(ReportType::NoAlbum.to_string()),
}),
}
if update_music {
let mut m: music::ActiveModel = m.clone().into_active_model();
m.processed_mtime = Set(new_mtime.to_string());
updated_music.push(m);
}
}
}
for chunk in updated_music.chunks(
(u16::MAX / (<music::Entity as EntityTrait>::Column::iter().count() as u16 * 10)) as usize,
) {
music::Entity::insert_many(chunk.to_vec())
.on_empty_do_nothing()
.on_conflict(
sea_query::OnConflict::column(music::Column::MediaId)
.update_column(music::Column::ProcessedMtime)
.to_owned(),
)
.exec(db)
.await?;
}
for chunk in inserted_albums.chunks(
(u16::MAX / (<album::Entity as EntityTrait>::Column::iter().count() as u16 * 10)) as usize,
) {
album::Entity::insert_many(chunk.to_vec())
.on_empty_do_nothing()
.on_conflict(
sea_query::OnConflict::column(album::Column::MbReleaseId)
.do_nothing()
.to_owned(),
)
.exec(db)
.await?;
}
let _ = MusicValidator::insert_report(inserted_reports, db).await?;
for chunk in inserted_tracks.chunks(
(u16::MAX / (<track::Entity as EntityTrait>::Column::iter().count() as u16 * 10)) as usize,
) {
track::Entity::insert_many(chunk.to_vec())
.on_empty_do_nothing()
.on_conflict(
sea_query::OnConflict::column(track::Column::MbTrackId)
.do_nothing()
.to_owned(),
)
.exec(db)
.await?;
}
warn!(
"Timing Complete check_albums_paginate in {:.2?}",
_before.elapsed()
);
Ok("Ok".to_string())
}
#[allow(dead_code)]
async fn get_track_details(
media: media::Model,
tag: &TaggedFile,
) -> Result<EitherTrack, CustomError> {
match FileTags::build(&media, tag).await? {
EitherFileTags::Right(x) => return Ok(EitherTrack::Right(x)),
EitherFileTags::Left(x) => match insert_track(x).await {
Ok(x) => {
return Ok(EitherTrack::Left(x));
}
Err(e) => Err(e),
},
}
}
#[allow(dead_code)]
async fn get_album_details(
media: media::Model,
tag: &TaggedFile,
) -> Result<EitherAlbum, CustomError> {
match FileTags::build(&media, tag).await? {
EitherFileTags::Right(x) => return Ok(EitherAlbum::Right(x)),
EitherFileTags::Left(x) => match insert_album(x).await {
Ok(x) => return Ok(EitherAlbum::Left(x)),
Err(e) => Err(e),
},
}
}
#[allow(dead_code)]
async fn insert_album(tags: FileTags) -> Result<AlbumAModel, CustomError> {
Ok(album::ActiveModel {
id: ActiveValue::not_set(),
mb_release_group_id: ActiveValue::set(tags.album_group_id),
mb_release_id: ActiveValue::set(tags.album_id),
mb_album_artist_id: ActiveValue::set(tags.album_artist_id),
album_artist: ActiveValue::set(tags.album_artist),
title: ActiveValue::set(tags.album_title),
original_year: ActiveValue::set(tags.original_year),
})
}
#[allow(dead_code)]
async fn insert_track(tags: FileTags) -> Result<TrackAModel, CustomError> {
Ok(track::ActiveModel {
id: ActiveValue::not_set(),
mb_recording_id: ActiveValue::set(tags.recording_id),
mb_release_id: ActiveValue::set(tags.album_id),
mb_track_id: ActiveValue::set(tags.track_id),
title: ActiveValue::set(tags.track_title),
track_num: ActiveValue::set(tags.track_num),
music_id: ActiveValue::not_set(),
album_id: ActiveValue::not_set(),
mb_release_group_id: ActiveValue::set(tags.album_group_id),
disc_num: ActiveValue::Set(tags.disc_num),
length_ms: ActiveValue::set(tags.length_ms),
})
}
#[allow(dead_code)]
pub async fn get_music() -> Result<Vec<MusicModel>, CustomError> {
let db = get_db_connection().await?;
let music = music::Entity::find().all(&db).await?;
Ok(music)
}
pub async fn validate_albums() -> Result<String, CustomError> {
let db = get_db_connection().await?;
let albums: Vec<AlbumModel> = album::Entity::find().all(&db).await?;
for a in albums.iter() {
match track::Entity::find()
.filter(track::Column::MbReleaseGroupId.contains(a.mb_release_group_id.as_str()))
.one(&db)
.await
.expect("Failed to query Track DB")
{
None => {
a.clone().delete(&db).await?;
}
Some(_) => continue,
}
}
Ok("Ok".to_string())
}
pub async fn get_albums_by_release_group(
mb_release_group_id: String,
) -> Result<Vec<AlbumModel>, CustomError> {
let db = get_db_connection().await?;
let albums = album::Entity::find()
.filter(album::Column::MbReleaseGroupId.eq(mb_release_group_id))
.all(&db)
.await?;
Ok(albums)
}
pub async fn get_earliest_album_by_release_group(
mb_release_group_id: String,
) -> Result<AlbumModel, CustomError> {
info!(
"get_earliest_album_by_release_group - {}",
mb_release_group_id
);
let db = get_db_connection().await?;
let albums = album::Entity::find()
.filter(album::Column::MbReleaseGroupId.eq(mb_release_group_id))
.order_by_asc(album::Column::OriginalYear)
.all(&db)
.await?;
info!("{:?}", albums);
Ok(albums[0].clone())
}