use std::sync::Arc;
use mineral_channel_core::MusicChannel;
use parking_lot::Mutex;
use rustc_hash::FxHashSet;
use super::assemble;
use super::queue::{TagJob, job_key};
use super::write;
pub(super) async fn run(
rx: Arc<tokio::sync::Mutex<tokio::sync::mpsc::UnboundedReceiver<TagJob>>>,
channels: Vec<Arc<dyn MusicChannel>>,
http: Option<reqwest::Client>,
inflight: Arc<Mutex<FxHashSet<String>>>,
album_cache: assemble::AlbumCache,
) {
loop {
let job = rx.lock().await.recv().await;
let Some(job) = job else { break };
process(&job, &channels, http.as_ref(), &album_cache).await;
inflight
.lock()
.remove(&job_key(job.song_id(), job.quality, &job.path));
}
}
async fn process(
job: &TagJob,
channels: &[Arc<dyn MusicChannel>],
http: Option<&reqwest::Client>,
album_cache: &assemble::AlbumCache,
) {
let Some(channel) = channels
.iter()
.find(|ch| ch.source() == job.song_id().namespace())
else {
mineral_log::warn!(target: "tagging", song_id = job.song_id().as_str(), "无对应 channel,跳过打标");
return;
};
let song = &job.song;
let tags = assemble::collect(channel.as_ref(), http, song, album_cache).await;
let path = job.path.clone();
let result = tokio::task::spawn_blocking(move || write::write_tags(&path, &tags)).await;
match result {
Ok(Ok(write::WriteOutcome::Tagged)) => {
mineral_log::info!(target: "tagging", song_id = job.song_id().as_str(), path = %job.path.display(), "已写入内嵌 tag");
}
Ok(Ok(write::WriteOutcome::SkippedUnsupported)) => {
mineral_log::warn!(target: "tagging", song_id = job.song_id().as_str(), path = %job.path.display(), "内容无法探测或容器不支持,跳过打标");
}
Ok(Err(e)) => {
mineral_log::warn!(target: "tagging", song_id = job.song_id().as_str(), error = mineral_log::chain(&e), "打标失败");
}
Err(e) => {
mineral_log::warn!(target: "tagging", song_id = job.song_id().as_str(), error = mineral_log::chain(&e), "打标任务被取消");
}
}
}