211 lines
6.6 KiB
Rust
211 lines
6.6 KiB
Rust
use std::collections::{BTreeMap, HashMap};
|
|
|
|
use fuser::INodeNo;
|
|
use sea_orm::entity::prelude::*;
|
|
|
|
use crate::db::entities::Model;
|
|
use crate::item::Item;
|
|
use crate::music::metadata::MusicMetadata;
|
|
use crate::music::metadata::db::{
|
|
artists, music_metadata as music_metadata_entity, other_tags, pictures,
|
|
};
|
|
use tracing::{debug, error};
|
|
|
|
pub async fn save_music_metadata(
|
|
inode: i64,
|
|
music_metadata: &MusicMetadata,
|
|
client: &sea_orm::DatabaseConnection,
|
|
) -> Result<(), sea_orm::DbErr> {
|
|
use sea_orm::ActiveValue::Set;
|
|
|
|
music_metadata_entity::Entity::delete_by_id(inode)
|
|
.exec(client)
|
|
.await?;
|
|
|
|
music_metadata_entity::Entity::insert(music_metadata_entity::ActiveModel {
|
|
inode: Set(inode),
|
|
track_title: Set(music_metadata.track_title.clone()),
|
|
album: Set(music_metadata.album.clone()),
|
|
track_number: Set(music_metadata.track_number),
|
|
header: Set(music_metadata.header.clone()),
|
|
real_audio_start: Set(music_metadata.real_audio_start as i64),
|
|
})
|
|
.exec(client)
|
|
.await?;
|
|
|
|
if !music_metadata.artist.is_empty() {
|
|
artists::Entity::insert_many(music_metadata.artist.iter().map(|a| artists::ActiveModel {
|
|
inode: Set(inode),
|
|
artist: Set(a.clone()),
|
|
}))
|
|
.exec(client)
|
|
.await?;
|
|
}
|
|
|
|
if !music_metadata.other_tags.is_empty() {
|
|
other_tags::Entity::insert_many(music_metadata.other_tags.iter().enumerate().map(
|
|
|(pos, tag)| other_tags::ActiveModel {
|
|
inode: Set(inode),
|
|
position: Set(pos as i32),
|
|
tag: Set(tag.clone()),
|
|
},
|
|
))
|
|
.exec(client)
|
|
.await?;
|
|
}
|
|
|
|
if !music_metadata.picture_block_headers.is_empty() {
|
|
pictures::Entity::insert_many(
|
|
music_metadata
|
|
.picture_block_headers
|
|
.iter()
|
|
.zip(music_metadata.picture_data_ranges.iter())
|
|
.enumerate()
|
|
.map(|(pos, (hdr, (offset, len)))| pictures::ActiveModel {
|
|
inode: Set(inode),
|
|
position: Set(pos as i32),
|
|
block_header: Set(hdr.clone()),
|
|
data_offset: Set(*offset as i64),
|
|
data_length: Set(*len as i64),
|
|
}),
|
|
)
|
|
.exec(client)
|
|
.await?;
|
|
}
|
|
|
|
return Ok(());
|
|
}
|
|
|
|
pub async fn restore_music_metadata_from_db(
|
|
snapshot: &mut BTreeMap<INodeNo, Item>,
|
|
db_items: &HashMap<i64, Model>,
|
|
client: &sea_orm::DatabaseConnection,
|
|
) {
|
|
use artists::Column as ArtCol;
|
|
use music_metadata_entity::Column as MmCol;
|
|
use other_tags::Column as OtCol;
|
|
use pictures::Column as PicCol;
|
|
|
|
let unchanged_music_inodes: Vec<i64> = db_items
|
|
.values()
|
|
.filter_map(|db_item| {
|
|
let ino = INodeNo(db_item.inode as u64);
|
|
snapshot
|
|
.get(&ino)
|
|
.filter(|item| item.hash as i64 == db_item.hash && item.music_metadata.is_some())
|
|
.map(|_| db_item.inode)
|
|
})
|
|
.collect();
|
|
|
|
if unchanged_music_inodes.is_empty() {
|
|
return;
|
|
}
|
|
|
|
let mm_rows: HashMap<i64, music_metadata_entity::Model> =
|
|
match music_metadata_entity::Entity::find()
|
|
.filter(MmCol::Inode.is_in(unchanged_music_inodes.clone()))
|
|
.all(client)
|
|
.await
|
|
{
|
|
Ok(rows) => rows.into_iter().map(|m| (m.inode, m)).collect(),
|
|
Err(e) => {
|
|
error!(error = %e, "restore_music_metadata: mm rows query failed");
|
|
return;
|
|
}
|
|
};
|
|
|
|
let mut artists_by_inode: HashMap<i64, Vec<String>> = HashMap::new();
|
|
let artist_rows = match artists::Entity::find()
|
|
.filter(ArtCol::Inode.is_in(unchanged_music_inodes.clone()))
|
|
.all(client)
|
|
.await
|
|
{
|
|
Ok(r) => r,
|
|
Err(e) => {
|
|
error!(error = %e, "restore_music_metadata: artists query failed");
|
|
vec![]
|
|
}
|
|
};
|
|
for row in artist_rows {
|
|
artists_by_inode
|
|
.entry(row.inode)
|
|
.or_default()
|
|
.push(row.artist);
|
|
}
|
|
|
|
let mut other_tags_by_inode: HashMap<i64, Vec<(i32, String)>> = HashMap::new();
|
|
let other_tag_rows = match other_tags::Entity::find()
|
|
.filter(OtCol::Inode.is_in(unchanged_music_inodes.clone()))
|
|
.all(client)
|
|
.await
|
|
{
|
|
Ok(r) => r,
|
|
Err(e) => {
|
|
error!(error = %e, "restore_music_metadata: other_tags query failed");
|
|
vec![]
|
|
}
|
|
};
|
|
for row in other_tag_rows {
|
|
other_tags_by_inode
|
|
.entry(row.inode)
|
|
.or_default()
|
|
.push((row.position, row.tag));
|
|
}
|
|
|
|
let mut pictures_by_inode: HashMap<i64, Vec<pictures::Model>> = HashMap::new();
|
|
let picture_rows = match pictures::Entity::find()
|
|
.filter(PicCol::Inode.is_in(unchanged_music_inodes))
|
|
.all(client)
|
|
.await
|
|
{
|
|
Ok(r) => r,
|
|
Err(e) => {
|
|
error!(error = %e, "restore_music_metadata: pictures query failed");
|
|
vec![]
|
|
}
|
|
};
|
|
for row in picture_rows {
|
|
pictures_by_inode.entry(row.inode).or_default().push(row);
|
|
}
|
|
|
|
debug!(restored = mm_rows.len(), "restored music metadata from db");
|
|
|
|
for (inode, mm_row) in mm_rows {
|
|
let ino = INodeNo(inode as u64);
|
|
let Some(item) = snapshot.get_mut(&ino) else {
|
|
continue;
|
|
};
|
|
|
|
let mut sorted_tags = other_tags_by_inode.remove(&inode).unwrap_or_default();
|
|
sorted_tags.sort_by_key(|(pos, _)| *pos);
|
|
|
|
let mut sorted_pics = pictures_by_inode.remove(&inode).unwrap_or_default();
|
|
sorted_pics.sort_by_key(|p| p.position);
|
|
|
|
let picture_block_headers: Vec<Vec<u8>> =
|
|
sorted_pics.iter().map(|p| p.block_header.clone()).collect();
|
|
|
|
let picture_data_ranges: Vec<(u64, u64)> = sorted_pics
|
|
.iter()
|
|
.map(|p| (p.data_offset as u64, p.data_length as u64))
|
|
.collect();
|
|
|
|
let mut music_metadata = MusicMetadata {
|
|
artist: artists_by_inode.remove(&inode).unwrap_or_default(),
|
|
album_artist: None,
|
|
album: mm_row.album,
|
|
track_number: mm_row.track_number,
|
|
track_title: mm_row.track_title,
|
|
other_tags: sorted_tags.into_iter().map(|(_, tag)| tag).collect(),
|
|
header: mm_row.header,
|
|
picture_block_headers,
|
|
picture_data_ranges,
|
|
real_audio_start: mm_row.real_audio_start as u64,
|
|
vorbis_comment_offset: 0,
|
|
vorbis_comment_length: 0,
|
|
};
|
|
music_metadata.find_vorbis_offsets();
|
|
item.music_metadata = Some(music_metadata);
|
|
}
|
|
}
|