Tried this python script:
#!/usr/bin/env python3
import sqlite3
import zlib
import struct
import sys
from pathlib import Path
from tinytag import TinyTag
def get_db_sample_rate(db_path, track_id):
"""Extract sample rate from beatData in PerformanceData table."""
assert db_path.exists(), f"Database file does not exist: {db_path}"
assert isinstance(track_id, int), f"track_id must be int, got {type(track_id)}"
assert track_id > 0, f"track_id must be positive, got {track_id}"
conn = sqlite3.connect(db_path)
cursor = conn.cursor()
cursor.execute("SELECT beatData FROM PerformanceData WHERE trackId = ?", (track_id,))
row = cursor.fetchone()
conn.close()
assert row is not None, f"No PerformanceData found for track_id {track_id}"
assert row[0] is not None, f"beatData is NULL for track_id {track_id} (track not analyzed)"
compressed = bytes(row[0])
assert len(compressed) > 4, f"beatData too short for track_id {track_id}: {len(compressed)} bytes"
# Skip 4-byte length prefix, decompress
uncompressed = zlib.decompress(compressed[4:])
assert len(uncompressed) >= 8, f"Uncompressed beatData too short for track_id {track_id}: {len(uncompressed)} bytes"
# First 8 bytes is sample rate as big-endian double
sample_rate = struct.unpack('>d', uncompressed[0:8])[0]
assert sample_rate > 0, f"Invalid sample rate for track_id {track_id}: {sample_rate}"
assert sample_rate < 1000000, f"Unrealistic sample rate for track_id {track_id}: {sample_rate}"
return int(sample_rate)
def check_sample_rates(db_path):
"""Check all MP3 tracks for sample rate mismatches."""
db_path = Path(db_path)
assert db_path.exists(), f"Database file does not exist: {db_path}"
assert db_path.is_file(), f"Path is not a file: {db_path}"
base_dir = db_path.parent.parent
assert base_dir.exists(), f"Database parent directory does not exist: {base_dir}"
conn = sqlite3.connect(db_path)
cursor = conn.cursor()
# Get all MP3 tracks that have been analyzed and are available
cursor.execute("""
SELECT t.id, t.path, t.title, t.artist
FROM Track t
JOIN PerformanceData p ON t.id = p.trackId
WHERE t.fileType = 'mp3'
AND t.path IS NOT NULL
AND p.beatData IS NOT NULL
AND t.isAvailable = 1
""")
rows = cursor.fetchall()
assert len(rows) > 0, "No MP3 tracks found in database"
mismatches = []
matches = 0
missing_files = 0
for track_id, path, title, artist in rows:
assert isinstance(track_id, int), f"Invalid track_id type: {type(track_id)}"
assert path, f"Empty path for track_id {track_id}"
# Get actual file sample rate
file_path = base_dir / path
if not file_path.exists():
missing_files += 1
continue
# Get DB sample rate
db_sample_rate = get_db_sample_rate(db_path, track_id)
tag = TinyTag.get(str(file_path))
assert tag.samplerate is not None, f"No sample rate in file for track_id {track_id}: {file_path}"
actual_sample_rate = int(tag.samplerate)
assert actual_sample_rate > 0, f"Invalid sample rate in file for track_id {track_id}: {actual_sample_rate}"
if db_sample_rate != actual_sample_rate:
mismatches.append({
'id': track_id,
'title': title,
'artist': artist,
'path': path,
'db_sample_rate': db_sample_rate,
'actual_sample_rate': actual_sample_rate
})
else:
matches += 1
conn.close()
return mismatches, matches, missing_files
if __name__ == '__main__':
assert len(sys.argv) == 2, f"Usage: {sys.argv[0]} <database.db>"
db_path = sys.argv[1]
assert db_path, "Database path cannot be empty"
mismatches, matches, missing_files = check_sample_rates(db_path)
print(f"Matches: {matches}")
print(f"Mismatches: {len(mismatches)}")
print(f"Missing files: {missing_files}\n")
if mismatches:
for m in mismatches:
print(f" ID {m['id']}: {m['artist']} - {m['title']}")
print(f" DB: {m['db_sample_rate']} Hz, File: {m['actual_sample_rate']} Hz")
print(f" Path: {m['path']}\n")
But found that 100% of the tracks on the exported USB have correct sample rate. So lexicon is exporting correct sample rate info but something else is wrong