use crate::app::settings::get_db_connection;
use crate::entity::{
album::{self, ActiveModel as AlbumAModel},
album_group::{self, ActiveModel as AlbumGroupAModel},
media::{self},
music::{self, ActiveModel as MusicAModel, Entity as MusicEntity, Model as MusicModel},
};
use crate::error::error::CustomError;
use crate::media_checker::albums::check_albums_paginate;
use crate::media_manager::MusicManager;
use crate::media_validator::reports::delete_reports;
#[allow(unused_imports)]
use log::info;
use sea_orm::{entity::*, DatabaseConnection, PaginatorTrait, QueryOrder};
use sea_orm::{ActiveValue, EntityTrait, Iterable, QueryFilter, Set};
use sea_query;
use std::sync::mpsc::Sender;
use std::time::Instant;
pub struct MusicChecker {
sender: Option<Sender<String>>,
}
impl MusicChecker {
pub fn new(tx: Option<Sender<String>>) -> Self {
Self { sender: tx }
}
pub async fn get_music() -> Result<Vec<MusicModel>, CustomError> {
info!("MusicChecker::get_music");
let db = get_db_connection().await?;
let music = MusicEntity::find().all(&db).await?;
Ok(music)
}
fn send(&self, message: String) {
if self.sender.is_some() {
let _ = self.sender.as_ref().unwrap().send(message);
}
}
pub async fn execute(&self) -> Result<(), CustomError> {
info!("MusicChecker.execute");
let _before = Instant::now();
let db = get_db_connection().await?;
let media = MusicManager::get_filtered_media().await?;
let mut inserted_music: Vec<music::ActiveModel> = Vec::new();
let mut updated_music: Vec<music::ActiveModel> = Vec::new();
let mut i = 0;
let tot_media = media.len();
info!("MusicChecker.execute: media to scan: {}", tot_media);
for m in media {
i = i + 1;
if i % 100 == 0 || i == tot_media {
let message = format!("Scanning media - {}/{}", i, tot_media);
info!("{}", message);
self.send(message);
}
let music = music::Entity::find()
.filter(music::Column::MediaId.eq(m.id.to_string()))
.one(&db)
.await?;
match music {
Some(x) => updated_music.push(MusicChecker::update_music(x, m)),
None => inserted_music.push(MusicChecker::new_music(m)),
};
}
for chunk in inserted_music.chunks(
(u16::MAX / (<music::Entity as EntityTrait>::Column::iter().count() as u16 * 10))
as usize,
) {
let _ = MusicChecker::insert_music(chunk.to_vec(), &db).await?;
}
for chunk in updated_music.chunks(
(u16::MAX / (<music::Entity as EntityTrait>::Column::iter().count() as u16 * 10))
as usize,
) {
let _ = MusicChecker::insert_music(chunk.to_vec(), &db).await?;
}
info!("MusicChecker.execute: Timing Complete in {:.2?}", _before.elapsed());
self.check_albums().await?;
Ok(())
}
#[allow(dead_code)]
async fn check_albums(&self) -> Result<(), CustomError> {
info!("MusicChecker.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();
let message = format!("Scanning Albums - {}", i);
info!("{}", message);
self.send(message);
check_albums_paginate(music, &db).await?;
}
info!("MusicChecker.check_albums: Timing Complete in {:.2?}", _before.elapsed());
Ok(())
}
pub async fn insert_album_group(
album_group: Vec<AlbumGroupAModel>,
db: &DatabaseConnection,
) -> Result<String, CustomError> {
let _ = album_group::Entity::insert_many(album_group)
.on_empty_do_nothing()
.on_conflict(
sea_query::OnConflict::column(album_group::Column::MbReleaseGroupId)
.update_column(album_group::Column::ReleaseYear)
.to_owned(),
)
.exec(db)
.await?;
Ok("Ok".to_string())
}
pub async fn insert_album(
album: Vec<AlbumAModel>,
db: &DatabaseConnection,
) -> Result<String, CustomError> {
let _ = album::Entity::insert_many(album)
.on_empty_do_nothing()
.on_conflict(
sea_query::OnConflict::column(album::Column::MbReleaseId)
.do_nothing()
.to_owned(),
)
.exec(db)
.await?;
Ok("Ok".to_string())
}
pub async fn insert_music(
music: Vec<MusicAModel>,
db: &DatabaseConnection,
) -> Result<String, CustomError> {
let _ = music::Entity::insert_many(music)
.on_empty_do_nothing()
.on_conflict(
sea_query::OnConflict::column(music::Column::MediaId)
.update_column(music::Column::Mtime)
.to_owned(),
)
.exec(db)
.await?;
Ok("Ok".to_string())
}
fn update_music(music: music::Model, media: media::Model) -> MusicAModel {
let media_mtime = media.mtime.to_owned();
let active_media: media::ActiveModel = media.into();
let mut active_music: music::ActiveModel = music.into();
if active_music.mtime != active_media.mtime {
active_music.mtime = Set(media_mtime);
}
active_music
}
fn new_music(m: media::Model) -> MusicAModel {
music::ActiveModel {
id: ActiveValue::not_set(),
media_id: ActiveValue::set(m.id),
mtime: ActiveValue::set(m.mtime),
processed_mtime: ActiveValue::set(String::from("0")),
}
}
}