Store original audio details in track provenance (#245)

- Add bit_depth, sample_rate, bitrate columns to track_downloads table
- Read audio info from file via Mutagen when recording provenance
- Source Info popover shows "Audio: 24-bit · 96.0kHz · 2304kbps"
- These values are captured from the original file before transcoding,
  so users can see the original specs even after Blasphemy Mode converts
  FLAC to lossy format
This commit is contained in:
Broque Thomas 2026-04-02 15:16:49 -07:00
parent c7d6cc21e8
commit 5d8f3bcaec
3 changed files with 38 additions and 4 deletions

View file

@ -1156,6 +1156,15 @@ class MusicDatabase:
cursor.execute("CREATE INDEX IF NOT EXISTS idx_td_file_path ON track_downloads (file_path)")
cursor.execute("CREATE INDEX IF NOT EXISTS idx_td_source ON track_downloads (source_username, source_filename)")
# Migration: Add audio detail columns to track_downloads
cursor.execute("PRAGMA table_info(track_downloads)")
td_columns = [c[1] for c in cursor.fetchall()]
if 'bit_depth' not in td_columns:
cursor.execute("ALTER TABLE track_downloads ADD COLUMN bit_depth INTEGER")
cursor.execute("ALTER TABLE track_downloads ADD COLUMN sample_rate INTEGER")
cursor.execute("ALTER TABLE track_downloads ADD COLUMN bitrate INTEGER")
logger.info("Added audio detail columns (bit_depth, sample_rate, bitrate) to track_downloads")
# Discovery artist blacklist — artists users never want to see in discovery
cursor.execute("""
CREATE TABLE IF NOT EXISTS discovery_artist_blacklist (
@ -8626,7 +8635,8 @@ class MusicDatabase:
def record_track_download(self, file_path: str, source_service: str, source_username: str,
source_filename: str, source_size: int = 0, audio_quality: str = '',
track_title: str = '', track_artist: str = '', track_album: str = '',
status: str = 'completed', track_id: str = None) -> Optional[int]:
status: str = 'completed', track_id: str = None,
bit_depth: int = None, sample_rate: int = None, bitrate: int = None) -> Optional[int]:
"""Record a download with full source provenance. Returns the record ID."""
try:
conn = self._get_connection()
@ -8642,10 +8652,12 @@ class MusicDatabase:
cursor.execute("""
INSERT INTO track_downloads
(track_id, file_path, source_service, source_username, source_filename,
source_size, audio_quality, track_title, track_artist, track_album, status)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
source_size, audio_quality, track_title, track_artist, track_album, status,
bit_depth, sample_rate, bitrate)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""", (track_id, file_path, source_service, source_username, source_filename,
source_size, audio_quality, track_title, track_artist, track_album, status))
source_size, audio_quality, track_title, track_artist, track_album, status,
bit_depth, sample_rate, bitrate))
conn.commit()
return cursor.lastrowid
except Exception as e:

View file

@ -1728,6 +1728,21 @@ def _record_download_provenance(context):
quality = context.get('_audio_quality', '')
size = search_result.get('size', 0)
# Read audio details from the file for provenance (survives transcoding)
bit_depth = None
sample_rate = None
bitrate = None
try:
if file_path and os.path.isfile(file_path):
from mutagen import File as MutagenFile
audio = MutagenFile(file_path)
if audio and audio.info:
sample_rate = getattr(audio.info, 'sample_rate', None)
bitrate = getattr(audio.info, 'bitrate', None)
bit_depth = getattr(audio.info, 'bits_per_sample', None)
except Exception:
pass
db = get_database()
db.record_track_download(
file_path=file_path,
@ -1739,6 +1754,9 @@ def _record_download_provenance(context):
track_title=title,
track_artist=artist_name,
track_album=album_name,
bit_depth=bit_depth,
sample_rate=sample_rate,
bitrate=bitrate,
)
except Exception:
pass # Non-critical, never block download flow

View file

@ -44658,6 +44658,10 @@ async function showTrackSourceInfo(track, anchorEl) {
<span class="source-info-label">Quality</span>
<span class="source-info-value">${_esc(dl.audio_quality)}</span>
</div>` : ''}
${dl.bit_depth || dl.sample_rate || dl.bitrate ? `<div class="source-info-row">
<span class="source-info-label">Audio</span>
<span class="source-info-value">${[dl.bit_depth ? `${dl.bit_depth}-bit` : '', dl.sample_rate ? `${(dl.sample_rate / 1000).toFixed(1)}kHz` : '', dl.bitrate ? `${Math.round(dl.bitrate / 1000)}kbps` : ''].filter(Boolean).join(' · ')}</span>
</div>` : ''}
${dateStr ? `<div class="source-info-row">
<span class="source-info-label">Downloaded</span>
<span class="source-info-value">${dateStr}</span>