Reworked agent UI. Artist management form.
Publish Metadata Agent Image (dev) / build-and-push-image (push) Successful in 1m8s
Publish Web Player Image (dev) / build-and-push-image (push) Successful in 1m9s
Publish Metadata Agent Image / build-and-push-image (push) Successful in 1m7s
Publish Web Player Image / build-and-push-image (push) Successful in 1m10s
Publish Server Image / build-and-push-image (push) Successful in 2m23s

This commit is contained in:
2026-03-19 13:24:48 +00:00
parent 7c2c7b0ce5
commit b1eaa1b6e9
11 changed files with 741 additions and 38 deletions
+29 -3
View File
@@ -132,8 +132,8 @@ async fn reprocess_pending(state: &Arc<AppState>) -> anyhow::Result<usize> {
Vec::new()
};
// LLM normalization
match normalize::normalize(state, &raw_meta, &hints, &similar_artists, &similar_albums).await {
// LLM normalization (no folder context available for reprocessing from DB)
match normalize::normalize(state, &raw_meta, &hints, &similar_artists, &similar_albums, None).await {
Ok(normalized) => {
let confidence = normalized.confidence.unwrap_or(0.0);
let status = if confidence >= state.config.confidence_threshold {
@@ -428,9 +428,35 @@ async fn process_file(state: &Arc<AppState>, file_path: &std::path::Path) -> any
tracing::info!(file = filename, matches = ?names, "Found similar albums in DB");
}
// Build folder context for the LLM
let audio_extensions = ["flac", "mp3", "ogg", "wav", "aac", "m4a", "opus", "wma", "ape", "alac"];
let folder_ctx = {
let folder = file_path.parent().unwrap_or(file_path);
let mut folder_files: Vec<String> = std::fs::read_dir(folder)
.ok()
.map(|rd| {
rd.filter_map(|e| e.ok())
.filter_map(|e| {
let name = e.file_name().to_string_lossy().into_owned();
let ext = name.rsplit('.').next().unwrap_or("").to_lowercase();
if audio_extensions.contains(&ext.as_str()) { Some(name) } else { None }
})
.collect()
})
.unwrap_or_default();
folder_files.sort();
let track_count = folder_files.len();
let folder_path = folder
.strip_prefix(&state.config.inbox_dir)
.unwrap_or(folder)
.to_string_lossy()
.into_owned();
normalize::FolderContext { folder_path, folder_files, track_count }
};
// Call LLM for normalization
tracing::info!(file = filename, model = %state.config.ollama_model, "Sending to LLM for normalization...");
match normalize::normalize(state, &raw_meta, &hints, &similar_artists, &similar_albums).await {
match normalize::normalize(state, &raw_meta, &hints, &similar_artists, &similar_albums, Some(&folder_ctx)).await {
Ok(normalized) => {
let confidence = normalized.confidence.unwrap_or(0.0);
let status = if confidence >= state.config.confidence_threshold {
+24 -1
View File
@@ -7,6 +7,13 @@ use crate::web::AppState;
use super::metadata::RawMetadata;
#[derive(Debug)]
pub struct FolderContext {
pub folder_path: String, // path relative to inbox_dir (e.g. "Kunteynir/Синглы/Пьюк")
pub folder_files: Vec<String>, // audio filenames in the same folder
pub track_count: usize, // number of audio files in folder
}
/// Build the user message with all context and call Ollama for normalization.
pub async fn normalize(
state: &Arc<AppState>,
@@ -14,8 +21,9 @@ pub async fn normalize(
hints: &crate::db::PathHints,
similar_artists: &[SimilarArtist],
similar_albums: &[SimilarAlbum],
folder_ctx: Option<&FolderContext>,
) -> anyhow::Result<NormalizedFields> {
let user_message = build_user_message(raw, hints, similar_artists, similar_albums);
let user_message = build_user_message(raw, hints, similar_artists, similar_albums, folder_ctx);
let response = call_ollama(
&state.config.ollama_url,
@@ -34,6 +42,7 @@ fn build_user_message(
hints: &crate::db::PathHints,
similar_artists: &[SimilarArtist],
similar_albums: &[SimilarAlbum],
folder_ctx: Option<&FolderContext>,
) -> String {
let mut msg = String::from("## Raw metadata from file tags\n");
@@ -88,6 +97,18 @@ fn build_user_message(
}
}
if let Some(ctx) = folder_ctx {
msg.push_str("\n## Folder context\n");
msg.push_str(&format!("Folder path: \"{}\"\n", ctx.folder_path));
msg.push_str(&format!("Track count in folder: {}\n", ctx.track_count));
if !ctx.folder_files.is_empty() {
msg.push_str("Files in folder:\n");
for f in &ctx.folder_files {
msg.push_str(&format!(" - {}\n", f));
}
}
}
msg
}
@@ -201,6 +222,7 @@ fn parse_response(response: &str) -> anyhow::Result<NormalizedFields> {
genre: Option<String>,
#[serde(default)]
featured_artists: Vec<String>,
release_type: Option<String>,
confidence: Option<f64>,
notes: Option<String>,
}
@@ -216,6 +238,7 @@ fn parse_response(response: &str) -> anyhow::Result<NormalizedFields> {
track_number: parsed.track_number,
genre: parsed.genre,
featured_artists: parsed.featured_artists,
release_type: parsed.release_type,
confidence: parsed.confidence,
notes: parsed.notes,
})