feat(verification): persist status into library_history, badge on Downloads completed list

The persistent Completed list is built from library_history (not live tasks),
so the badge never showed after a session ended. Column added (additive),
written at import, passed through _build_history_download_item, rendered by
_adlVerifBadge next to the status label.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
dev 2026-06-10 19:03:55 +02:00
parent 9d1d09a571
commit 2a11dc961a
4 changed files with 30 additions and 6 deletions

View file

@ -657,6 +657,7 @@ def _build_history_download_item(entry: dict) -> dict:
'priority': _STATUS_PRIORITY['completed'],
'quality': entry.get('quality') or '',
'file_path': entry.get('file_path') or '',
'verification_status': entry.get('verification_status'),
'is_persistent_history': True,
}

View file

@ -268,6 +268,7 @@ def record_library_history_download(context: Dict[str, Any]) -> None:
source_artist=source_artist,
origin=origin,
origin_context=origin_context,
verification_status=context.get("_verification_status"),
)
except Exception as e:
logger.debug("library history record failed: %s", e)

View file

@ -636,7 +636,7 @@ class MusicDatabase:
if 'download_source' not in lh_cols:
cursor.execute("ALTER TABLE library_history ADD COLUMN download_source TEXT")
logger.info("Added download_source column to library_history")
for _col in ['source_track_id', 'source_track_title', 'source_filename', 'acoustid_result', 'source_artist']:
for _col in ['source_track_id', 'source_track_title', 'source_filename', 'acoustid_result', 'source_artist', 'verification_status']:
if _col not in lh_cols:
cursor.execute(f"ALTER TABLE library_history ADD COLUMN {_col} TEXT")
logger.info(f"Added {_col} column to library_history")
@ -12811,7 +12811,7 @@ class MusicDatabase:
quality=None, server_source=None, file_path=None, thumb_url=None,
download_source=None, source_track_id=None, source_track_title=None,
source_filename=None, acoustid_result=None, source_artist=None,
origin=None, origin_context=None):
origin=None, origin_context=None, verification_status=None):
"""Record a download or import event to the library history table.
``origin``/``origin_context`` record what TRIGGERED the download
@ -12824,11 +12824,12 @@ class MusicDatabase:
INSERT INTO library_history (event_type, title, artist_name, album_name,
quality, server_source, file_path, thumb_url, download_source,
source_track_id, source_track_title, source_filename,
acoustid_result, source_artist, origin, origin_context)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
acoustid_result, source_artist, origin, origin_context,
verification_status)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""", (event_type, title, artist_name, album_name, quality, server_source, file_path, thumb_url,
download_source, source_track_id, source_track_title, source_filename,
acoustid_result, source_artist, origin, origin_context))
acoustid_result, source_artist, origin, origin_context, verification_status))
conn.commit()
return True
except Exception as e:

View file

@ -2342,6 +2342,26 @@ function _updateDlNavBadge(count) {
}
}
function _adlVerifBadge(dl) {
// Verification badge for completed downloads — how this file passed
// verification (status comes from library_history / the live task):
// verified = clean AcoustID pass; unverified = imported but not
// hard-confirmed (cross-script/ambiguous/no fingerprint match);
// force_imported = accepted as best candidate after the retry budget was
// exhausted (version-mismatch fallback).
if (dl.status !== 'completed') return '';
if (dl.verification_status === 'force_imported') {
return ' <span class="verif-badge verif-force" title="Force-imported: accepted as best available candidate after repeated mismatches (version-mismatch fallback). Library AcoustID scans report these as informational.">⚑</span>';
}
if (dl.verification_status === 'unverified') {
return ' <span class="verif-badge verif-unverified" title="Imported but not hard-verified (AcoustID could not confirm — e.g. cross-script metadata or no fingerprint match).">⚠</span>';
}
if (dl.verification_status === 'verified') {
return ' <span class="verif-badge verif-ok" title="AcoustID verified: audio fingerprint matches the expected track.">✔</span>';
}
return '';
}
function _adlRender() {
const list = document.getElementById('adl-list');
const empty = document.getElementById('adl-empty');
@ -2449,6 +2469,7 @@ function _adlRender() {
for (const dl of section.items) {
const statusClass = _adlStatusClass(dl.status);
const statusLabel = _adlStatusLabel(dl.status);
// (verification badge appended next to the label via _adlVerifBadge)
const title = _adlEsc(dl.title || 'Unknown Track');
const artist = _adlEsc(dl.artist || '');
const album = _adlEsc(dl.album || '');
@ -2488,7 +2509,7 @@ function _adlRender() {
</div>
<div class="adl-row-status ${statusClass}">
<span class="adl-status-dot ${statusClass}"></span>
${statusLabel}
${statusLabel}${_adlVerifBadge(dl)}
</div>
${cancelBtnHtml}
</div>`;