mirror of https://github.com/Nezreka/SoulSync.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
183 lines
5.5 KiB
183 lines
5.5 KiB
import sqlite3
|
|
import sys
|
|
import types
|
|
from types import SimpleNamespace
|
|
|
|
# Stub optional Spotify dependency so the metadata package can import in tests.
|
|
if 'spotipy' not in sys.modules:
|
|
spotipy = types.ModuleType('spotipy')
|
|
oauth2 = types.ModuleType('spotipy.oauth2')
|
|
|
|
class _DummySpotify:
|
|
pass
|
|
|
|
class _DummyOAuth:
|
|
pass
|
|
|
|
spotipy.Spotify = _DummySpotify
|
|
oauth2.SpotifyOAuth = _DummyOAuth
|
|
oauth2.SpotifyClientCredentials = _DummyOAuth
|
|
spotipy.oauth2 = oauth2
|
|
sys.modules['spotipy'] = spotipy
|
|
sys.modules['spotipy.oauth2'] = oauth2
|
|
|
|
if 'config.settings' not in sys.modules:
|
|
config_mod = types.ModuleType('config')
|
|
settings_mod = types.ModuleType('config.settings')
|
|
|
|
class _DummyConfigManager:
|
|
def get(self, key, default=None):
|
|
return default
|
|
|
|
def get_active_media_server(self):
|
|
return "plex"
|
|
|
|
settings_mod.config_manager = _DummyConfigManager()
|
|
config_mod.settings = settings_mod
|
|
sys.modules['config'] = config_mod
|
|
sys.modules['config.settings'] = settings_mod
|
|
|
|
from core.repair_jobs import metadata_gap_filler as mgf
|
|
|
|
|
|
class _FakeTrackClient:
|
|
def __init__(self, source_name, isrc=None):
|
|
self.source_name = source_name
|
|
self.isrc = isrc
|
|
self.calls = []
|
|
|
|
def get_track_details(self, track_id):
|
|
self.calls.append(track_id)
|
|
if self.isrc is None:
|
|
return None
|
|
return {
|
|
'id': track_id,
|
|
'external_ids': {'isrc': self.isrc},
|
|
}
|
|
|
|
|
|
class _FakeMBClient:
|
|
def __init__(self):
|
|
self.calls = []
|
|
|
|
def search_recording(self, title, artist_name=None, limit=1):
|
|
self.calls.append((title, artist_name, limit))
|
|
return [{'id': 'mb-recording'}]
|
|
|
|
|
|
def _make_db():
|
|
conn = sqlite3.connect(':memory:')
|
|
conn.row_factory = sqlite3.Row
|
|
cursor = conn.cursor()
|
|
cursor.execute(
|
|
"""
|
|
CREATE TABLE artists (
|
|
id INTEGER PRIMARY KEY,
|
|
name TEXT,
|
|
thumb_url TEXT
|
|
)
|
|
"""
|
|
)
|
|
cursor.execute(
|
|
"""
|
|
CREATE TABLE albums (
|
|
id INTEGER PRIMARY KEY,
|
|
title TEXT,
|
|
thumb_url TEXT
|
|
)
|
|
"""
|
|
)
|
|
cursor.execute(
|
|
"""
|
|
CREATE TABLE tracks (
|
|
id INTEGER PRIMARY KEY,
|
|
title TEXT,
|
|
artist_id INTEGER,
|
|
album_id INTEGER,
|
|
spotify_track_id TEXT,
|
|
itunes_track_id TEXT,
|
|
deezer_track_id TEXT,
|
|
isrc TEXT,
|
|
musicbrainz_recording_id TEXT
|
|
)
|
|
"""
|
|
)
|
|
cursor.execute("INSERT INTO artists (id, name, thumb_url) VALUES (1, 'Artist', '')")
|
|
cursor.execute("INSERT INTO albums (id, title, thumb_url) VALUES (1, 'Album', '')")
|
|
cursor.execute(
|
|
"""
|
|
INSERT INTO tracks
|
|
(id, title, artist_id, album_id, spotify_track_id, itunes_track_id, deezer_track_id, isrc, musicbrainz_recording_id)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
""",
|
|
(1, 'Track Title', 1, 1, 'sp-1', None, 'dz-1', '', ''),
|
|
)
|
|
conn.commit()
|
|
return conn
|
|
|
|
|
|
def _make_context(conn):
|
|
findings = []
|
|
return SimpleNamespace(
|
|
db=SimpleNamespace(_get_connection=lambda: conn),
|
|
config_manager=SimpleNamespace(get=lambda key, default=None: default),
|
|
check_stop=lambda: False,
|
|
wait_if_paused=lambda: False,
|
|
update_progress=lambda *args, **kwargs: None,
|
|
report_progress=lambda *args, **kwargs: None,
|
|
sleep_or_stop=lambda seconds: False,
|
|
mb_client=_FakeMBClient(),
|
|
create_finding=lambda **kwargs: (findings.append(kwargs) or True),
|
|
findings=findings,
|
|
)
|
|
|
|
|
|
def test_metadata_gap_filler_prefers_primary_track_source(monkeypatch):
|
|
conn = _make_db()
|
|
context = _make_context(conn)
|
|
|
|
spotify_client = _FakeTrackClient('spotify', isrc='SP-ISRC')
|
|
deezer_client = _FakeTrackClient('deezer', isrc='DZ-ISRC')
|
|
itunes_client = _FakeTrackClient('itunes', isrc=None)
|
|
|
|
monkeypatch.setattr(mgf, 'get_primary_source', lambda: 'deezer')
|
|
monkeypatch.setattr(
|
|
mgf,
|
|
'get_client_for_source',
|
|
lambda source: {'spotify': spotify_client, 'deezer': deezer_client, 'itunes': itunes_client}.get(source),
|
|
)
|
|
|
|
result = mgf.MetadataGapFillerJob().scan(context)
|
|
|
|
assert result.findings_created == 1
|
|
assert deezer_client.calls == ['dz-1']
|
|
assert spotify_client.calls == []
|
|
assert context.findings[0]['details']['found_fields']['isrc'] == 'DZ-ISRC'
|
|
assert context.findings[0]['details']['resolved_source'] == 'deezer'
|
|
assert context.findings[0]['details']['resolved_track_id'] == 'dz-1'
|
|
|
|
|
|
def test_metadata_gap_filler_skips_track_detail_lookup_when_isrc_disabled(monkeypatch):
|
|
conn = _make_db()
|
|
context = _make_context(conn)
|
|
|
|
spotify_client = _FakeTrackClient('spotify', isrc='SP-ISRC')
|
|
deezer_client = _FakeTrackClient('deezer', isrc='DZ-ISRC')
|
|
|
|
monkeypatch.setattr(mgf, 'get_primary_source', lambda: 'deezer')
|
|
monkeypatch.setattr(
|
|
mgf,
|
|
'get_client_for_source',
|
|
lambda source: {'spotify': spotify_client, 'deezer': deezer_client}.get(source),
|
|
)
|
|
|
|
job = mgf.MetadataGapFillerJob()
|
|
monkeypatch.setattr(job, '_get_settings', lambda context: {'fill_isrc': False, 'fill_musicbrainz_id': True})
|
|
|
|
result = job.scan(context)
|
|
|
|
assert result.findings_created == 1
|
|
assert spotify_client.calls == []
|
|
assert deezer_client.calls == []
|
|
assert context.findings[0]['details']['found_fields']['musicbrainz_recording_id'] == 'mb-recording'
|