Files
lidarr-mb-gap/src-cleanup/track_verification.py
Danilo Reyes af5a2bf825 Add audio verification and duplicate tracking features
- Integrated `plexapi` and `python-dotenv` as dependencies in `flake.nix` and `pyproject.toml` for enhanced functionality.
- Implemented new modules for audio verification and duplicate tracking, including `audio_verification.py`, `duplicate_finder.py`, and `track_verification.py`.
- Updated `main.py` to utilize the new modules for identifying and managing duplicate single tracks in Lidarr, with detailed logging and confidence scoring.
- Enhanced the `find_duplicate_singles` function to support audio verification results and metadata migration to Plex.
- Refactored existing code for improved structure and maintainability, ensuring better integration of new features.
2025-11-14 01:32:41 -06:00

86 lines
3.0 KiB
Python

"""Track verification using multiple methods"""
import logging
from typing import Dict, Optional, Tuple
from audio_verification import (
check_file_properties,
check_mb_recording_id,
check_quality_profile,
compare_fingerprints,
get_audio_fingerprint,
get_file_properties,
)
from lidarr_client import get_track_info, get_trackfile_info
logger = logging.getLogger(__name__)
def verify_audio_match(
base_url: str,
headers: Dict[str, str],
single_track_id: int,
single_track_file_id: int,
album_track_id: int,
album_track_file_id: int,
docker_mount: Optional[str] = None,
) -> Tuple[bool, Optional[str], int]:
"""Verify tracks using multiple methods. Returns (match, message, confidence_score)"""
logger.debug(
f"Verifying audio match: single trackFileId {single_track_file_id} vs album trackFileId {album_track_file_id}"
)
single_file_info = get_trackfile_info(base_url, single_track_file_id, headers)
album_file_info = get_trackfile_info(base_url, album_track_file_id, headers)
if not (single_file_info and album_file_info):
return False, "Could not fetch track file info", 0
single_path = single_file_info.get("path")
album_path = album_file_info.get("path")
if not (single_path and album_path):
return False, "Missing file paths", 0
single_track_info = get_track_info(base_url, single_track_id, headers)
album_track_info = get_track_info(base_url, album_track_id, headers)
mb_score, mb_msg = check_mb_recording_id(single_track_info, album_track_info)
quality_score, quality_msg = check_quality_profile(
single_file_info, album_file_info
)
single_props = get_file_properties(single_path, docker_mount)
album_props = get_file_properties(album_path, docker_mount)
prop_checks = check_file_properties(single_props, album_props)
single_fp = get_audio_fingerprint(single_path, docker_mount)
album_fp = get_audio_fingerprint(album_path, docker_mount)
log_context = f"single trackFileId {single_track_file_id} vs album trackFileId {album_track_file_id}"
if single_fp and album_fp:
fp_match, fp_message = compare_fingerprints(
single_fp, album_fp, log_context, return_message=True
)
fp_score = 20 if fp_match else 0
fp_msg = f"✓ Audio fingerprint match (+20)" if fp_match else f"{fp_message}"
else:
fp_score, fp_msg = 0, "⚠ Audio fingerprint unavailable"
all_checks = [
(mb_score, mb_msg),
(quality_score, quality_msg) if quality_msg else None,
*prop_checks,
(fp_score, fp_msg),
]
valid_checks = list(filter(lambda x: x is not None, all_checks))
confidence_score = sum(score for score, _ in valid_checks)
verification_results = [msg for _, msg in valid_checks]
match = confidence_score >= 70
result_message = f"Confidence: {confidence_score}/100 | " + " | ".join(
verification_results
)
return match, result_message, confidence_score