diff --git a/core/imports/pipeline.py b/core/imports/pipeline.py index 3995173a..ec027f06 100644 --- a/core/imports/pipeline.py +++ b/core/imports/pipeline.py @@ -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: diff --git a/database/music_database.py b/database/music_database.py index 6aed3f6b..2547d740 100644 --- a/database/music_database.py +++ b/database/music_database.py @@ -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.