fix(verification): persist status on ALL pipeline success exits + history backfill

The pipeline has three success exits (simple download, playlist folder mode,
main) but only the main one persisted the verification status — force-imported
playlist tracks got no tag, no history status, and never appeared in the
Unverified filter. Extracted _persist_verification_status() and call it at
every exit. One-time idempotent backfill derives status for existing history
rows from their recorded acoustid_result (pass->verified, skip->unverified).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
dev 2026-06-10 19:25:07 +02:00
parent 8dcad2be4e
commit 41536384c3
2 changed files with 41 additions and 13 deletions

View file

@ -182,6 +182,26 @@ def build_import_pipeline_runtime(
)
def _persist_verification_status(context, final_path):
"""Compute + persist the verification status (verified / unverified /
force_imported) for a finished import: embedded tag on the file, plus
context['_verification_status'] for the history row and the Downloads UI.
MUST be called on EVERY success exit of post-processing (main, playlist
folder mode, simple download) a missed exit means no badge and no tag.
Never raises."""
try:
from core.matching.verification_status import status_for_import
from core.tag_writer import write_verification_status
status = status_for_import(context)
if status:
context['_verification_status'] = status
if final_path:
write_verification_status(str(final_path), status)
except Exception as _vs_err:
logger.debug(f"verification-status persist skipped: {_vs_err}")
def post_process_matched_download(context_key, context, file_path, runtime, metadata_runtime=None):
on_download_completed = getattr(runtime, "on_download_completed", None)
automation_engine = getattr(runtime, "automation_engine", None)
@ -450,6 +470,7 @@ def post_process_matched_download(context_key, context, file_path, runtime, meta
logger.info(f"Simple download post-processing complete: {activity_target}")
context['_simple_download_completed'] = True
context['_final_path'] = str(destination)
_persist_verification_status(context, destination)
emit_track_downloaded(context, automation_engine)
record_library_history_download(context)
record_download_provenance(context)
@ -627,6 +648,8 @@ def post_process_matched_download(context_key, context, file_path, runtime, meta
final_path = downsampled_path
context['_final_processed_path'] = final_path
_persist_verification_status(context, final_path)
blasphemy_path = create_lossy_copy(final_path)
if blasphemy_path:
context['_final_processed_path'] = blasphemy_path
@ -944,19 +967,7 @@ def post_process_matched_download(context_key, context, file_path, runtime, meta
final_path = downsampled_path
context['_final_processed_path'] = final_path
# Persist the verification status (verified / unverified / force_imported)
# as an embedded tag so it travels with the file and survives DB resets.
# Written BEFORE the lossy copy so the copy inherits it. Also stashed on
# the context so the wrapper can surface it on the Downloads page.
try:
from core.matching.verification_status import status_for_import
from core.tag_writer import write_verification_status
_verif_status = status_for_import(context)
if _verif_status:
context['_verification_status'] = _verif_status
write_verification_status(final_path, _verif_status)
except Exception as _vs_err:
logger.debug(f"verification-status persist skipped: {_vs_err}")
_persist_verification_status(context, final_path)
blasphemy_path = create_lossy_copy(final_path)
if blasphemy_path:

View file

@ -641,6 +641,23 @@ class MusicDatabase:
cursor.execute(f"ALTER TABLE library_history ADD COLUMN {_col} TEXT")
logger.info(f"Added {_col} column to library_history")
# One-time backfill: derive verification_status for history rows
# written before the column existed (or by pipeline exits that
# missed it) from the acoustid_result those imports already
# recorded (pass->verified, skip->unverified). force_imported
# can't be derived retroactively. Idempotent: only fills NULLs.
cursor.execute("""
UPDATE library_history SET verification_status =
CASE acoustid_result
WHEN 'pass' THEN 'verified'
WHEN 'skip' THEN 'unverified'
END
WHERE verification_status IS NULL
AND acoustid_result IN ('pass', 'skip')
""")
if cursor.rowcount:
logger.info("Backfilled verification_status from acoustid_result (%d rows)", cursor.rowcount)
# Migration: download-origin provenance — what TRIGGERED a download
# ('watchlist' + artist / 'playlist' + playlist name). Read by the
# origin-history modal on the watchlist + sync pages.