Discovery lift (4/N): get_*_discovery_status -> shared helper
Fourth cluster: get_<source>_discovery_status (all eight sources, Beatport
included) -> core.discovery.endpoints.get_discovery_status(states, key, *,
not_found_message, error_label), wired via _get_source_discovery_status.
Unlike sync-status, the discovery-status response shape is byte-identical
across every source (phase/status/progress/spotify_matches/spotify_total/
results/complete), so Beatport folds in here too. Only the 404 string
("... discovery not found" vs "... playlist not found" vs "Beatport chart
not found") and the except-log label vary. ListenBrainz key via _lb_state_key.
NOT touched this cluster: get_*_playlist_state (the sibling endpoints).
Those genuinely diverge per source — different id-key name (playlist_id /
url_hash / playlist_mbid), presence of url / created_at / download_process_id,
Tidal's playlist.__dict__ serialization, and YouTube's strict (non-.get)
field access. Folding them would need a flag pile that wouldn't be a clean
1:1, so they keep their own bodies.
Tests: +4 (404, full response + last_accessed bump, complete=False when not
'discovered', missing-field -> 500). Full discovery suite: 175 passed.
web_server.py: -155 lines.
This commit is contained in:
parent
aad1d2b8f0
commit
8a9ed677ab
3 changed files with 107 additions and 172 deletions
|
|
@ -249,3 +249,42 @@ def get_sync_status(
|
|||
except Exception as e:
|
||||
logger.error(f"Error getting {error_label} sync status: {e}")
|
||||
return {"error": str(e)}, 500
|
||||
|
||||
|
||||
def get_discovery_status(
|
||||
states: Dict[str, Any],
|
||||
key: str,
|
||||
*,
|
||||
not_found_message: str,
|
||||
error_label: str,
|
||||
) -> Tuple[Dict[str, Any], int]:
|
||||
"""Report real-time discovery progress/results for one playlist.
|
||||
|
||||
1:1 lift of the byte-identical ``get_<source>_discovery_status`` bodies.
|
||||
Unlike sync-status, this shape is identical for ALL eight sources —
|
||||
Beatport included — so it folds in too. Only the 404 message
|
||||
(".../discovery not found" vs ".../playlist not found" vs "Beatport chart
|
||||
not found") and the except-log label vary, both passed in. The caller
|
||||
resolves the key (ListenBrainz via ``_lb_state_key``).
|
||||
|
||||
Returns ``(payload, status_code)``.
|
||||
"""
|
||||
try:
|
||||
if key not in states:
|
||||
return {"error": not_found_message}, 404
|
||||
|
||||
state = states[key]
|
||||
state['last_accessed'] = time.time()
|
||||
|
||||
return {
|
||||
'phase': state['phase'],
|
||||
'status': state['status'],
|
||||
'progress': state['discovery_progress'],
|
||||
'spotify_matches': state['spotify_matches'],
|
||||
'spotify_total': state['spotify_total'],
|
||||
'results': state['discovery_results'],
|
||||
'complete': state['phase'] == 'discovered',
|
||||
}, 200
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting {error_label} discovery status: {e}")
|
||||
return {"error": str(e)}, 500
|
||||
|
|
|
|||
|
|
@ -394,3 +394,54 @@ def test_status_strict_getter_missing_playlist_raises_500_after_partial_mutation
|
|||
# phase was set to sync_complete BEFORE the strict getter raised (1:1).
|
||||
assert states['pl']['phase'] == 'sync_complete'
|
||||
assert calls == [] # activity never posted
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# get_discovery_status
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def test_discovery_status_not_found():
|
||||
from core.discovery.endpoints import get_discovery_status
|
||||
body, code = get_discovery_status({}, 'missing',
|
||||
not_found_message='Tidal discovery not found',
|
||||
error_label='Tidal')
|
||||
assert code == 404 and body == {"error": "Tidal discovery not found"}
|
||||
|
||||
|
||||
def test_discovery_status_builds_response_and_bumps_access():
|
||||
from core.discovery.endpoints import get_discovery_status
|
||||
state = {
|
||||
'phase': 'discovered', 'status': 'done', 'discovery_progress': 100,
|
||||
'spotify_matches': 8, 'spotify_total': 10,
|
||||
'discovery_results': [{'x': 1}], 'last_accessed': 0,
|
||||
}
|
||||
states = {'pl': state}
|
||||
body, code = get_discovery_status(states, 'pl',
|
||||
not_found_message='nf', error_label='Tidal')
|
||||
assert code == 200
|
||||
assert body == {
|
||||
'phase': 'discovered', 'status': 'done', 'progress': 100,
|
||||
'spotify_matches': 8, 'spotify_total': 10,
|
||||
'results': [{'x': 1}], 'complete': True,
|
||||
}
|
||||
assert state['last_accessed'] != 0
|
||||
|
||||
|
||||
def test_discovery_status_complete_false_when_not_discovered():
|
||||
from core.discovery.endpoints import get_discovery_status
|
||||
state = {
|
||||
'phase': 'discovering', 'status': 'running', 'discovery_progress': 40,
|
||||
'spotify_matches': 2, 'spotify_total': 10, 'discovery_results': [],
|
||||
}
|
||||
body, code = get_discovery_status({'pl': state}, 'pl',
|
||||
not_found_message='nf', error_label='Deezer')
|
||||
assert code == 200 and body['complete'] is False
|
||||
|
||||
|
||||
def test_discovery_status_missing_field_raises_500():
|
||||
from core.discovery.endpoints import get_discovery_status
|
||||
# state missing 'status' -> strict access raises -> 500 (matches original)
|
||||
states = {'pl': {'phase': 'x'}}
|
||||
body, code = get_discovery_status(states, 'pl',
|
||||
not_found_message='nf', error_label='Beatport')
|
||||
assert code == 500 and "error" in body
|
||||
|
|
|
|||
189
web_server.py
189
web_server.py
|
|
@ -20560,28 +20560,7 @@ def start_tidal_discovery(playlist_id):
|
|||
@app.route('/api/tidal/discovery/status/<playlist_id>', methods=['GET'])
|
||||
def get_tidal_discovery_status(playlist_id):
|
||||
"""Get real-time discovery status for a Tidal playlist"""
|
||||
try:
|
||||
if playlist_id not in tidal_discovery_states:
|
||||
return jsonify({"error": "Tidal discovery not found"}), 404
|
||||
|
||||
state = tidal_discovery_states[playlist_id]
|
||||
state['last_accessed'] = time.time() # Update access time
|
||||
|
||||
response = {
|
||||
'phase': state['phase'],
|
||||
'status': state['status'],
|
||||
'progress': state['discovery_progress'],
|
||||
'spotify_matches': state['spotify_matches'],
|
||||
'spotify_total': state['spotify_total'],
|
||||
'results': state['discovery_results'],
|
||||
'complete': state['phase'] == 'discovered'
|
||||
}
|
||||
|
||||
return jsonify(response)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting Tidal discovery status: {e}")
|
||||
return jsonify({"error": str(e)}), 500
|
||||
return _get_source_discovery_status(tidal_discovery_states, playlist_id, "Tidal discovery not found", "Tidal")
|
||||
|
||||
|
||||
@app.route('/api/tidal/discovery/update_match', methods=['POST'])
|
||||
|
|
@ -21061,6 +21040,7 @@ from core.discovery.endpoints import (
|
|||
cancel_sync as _cancel_sync_core,
|
||||
delete_playlist_state as _delete_playlist_state_core,
|
||||
get_sync_status as _get_sync_status_core,
|
||||
get_discovery_status as _get_discovery_status_core,
|
||||
playlist_name_attr_or_unknown as _pl_name_attr_or_unknown,
|
||||
playlist_name_strict as _pl_name_strict,
|
||||
playlist_name_safe as _pl_name_safe,
|
||||
|
|
@ -21099,6 +21079,14 @@ def _get_source_sync_status(states, key, not_found_message, error_label,
|
|||
return jsonify(body), code
|
||||
|
||||
|
||||
def _get_source_discovery_status(states, key, not_found_message, error_label):
|
||||
"""Thin glue for the per-source get_*_discovery_status routes."""
|
||||
body, code = _get_discovery_status_core(
|
||||
states, key, not_found_message=not_found_message, error_label=error_label,
|
||||
)
|
||||
return jsonify(body), code
|
||||
|
||||
|
||||
def _build_tidal_discovery_deps():
|
||||
"""Build the TidalDiscoveryDeps bundle from web_server.py globals on each call."""
|
||||
return _discovery_tidal.TidalDiscoveryDeps(
|
||||
|
|
@ -21418,28 +21406,7 @@ def start_deezer_discovery(playlist_id):
|
|||
@app.route('/api/deezer/discovery/status/<playlist_id>', methods=['GET'])
|
||||
def get_deezer_discovery_status(playlist_id):
|
||||
"""Get real-time discovery status for a Deezer playlist"""
|
||||
try:
|
||||
if playlist_id not in deezer_discovery_states:
|
||||
return jsonify({"error": "Deezer discovery not found"}), 404
|
||||
|
||||
state = deezer_discovery_states[playlist_id]
|
||||
state['last_accessed'] = time.time()
|
||||
|
||||
response = {
|
||||
'phase': state['phase'],
|
||||
'status': state['status'],
|
||||
'progress': state['discovery_progress'],
|
||||
'spotify_matches': state['spotify_matches'],
|
||||
'spotify_total': state['spotify_total'],
|
||||
'results': state['discovery_results'],
|
||||
'complete': state['phase'] == 'discovered'
|
||||
}
|
||||
|
||||
return jsonify(response)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting Deezer discovery status: {e}")
|
||||
return jsonify({"error": str(e)}), 500
|
||||
return _get_source_discovery_status(deezer_discovery_states, playlist_id, "Deezer discovery not found", "Deezer")
|
||||
|
||||
@app.route('/api/deezer/discovery/update_match', methods=['POST'])
|
||||
def update_deezer_discovery_match():
|
||||
|
|
@ -21985,28 +21952,7 @@ def start_qobuz_discovery(playlist_id):
|
|||
@app.route('/api/qobuz/discovery/status/<playlist_id>', methods=['GET'])
|
||||
def get_qobuz_discovery_status(playlist_id):
|
||||
"""Get real-time discovery status for a Qobuz playlist."""
|
||||
try:
|
||||
if playlist_id not in qobuz_discovery_states:
|
||||
return jsonify({"error": "Qobuz discovery not found"}), 404
|
||||
|
||||
state = qobuz_discovery_states[playlist_id]
|
||||
state['last_accessed'] = time.time()
|
||||
|
||||
response = {
|
||||
'phase': state['phase'],
|
||||
'status': state['status'],
|
||||
'progress': state['discovery_progress'],
|
||||
'spotify_matches': state['spotify_matches'],
|
||||
'spotify_total': state['spotify_total'],
|
||||
'results': state['discovery_results'],
|
||||
'complete': state['phase'] == 'discovered'
|
||||
}
|
||||
|
||||
return jsonify(response)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting Qobuz discovery status: {e}")
|
||||
return jsonify({"error": str(e)}), 500
|
||||
return _get_source_discovery_status(qobuz_discovery_states, playlist_id, "Qobuz discovery not found", "Qobuz")
|
||||
|
||||
|
||||
@app.route('/api/qobuz/discovery/update_match', methods=['POST'])
|
||||
|
|
@ -22803,28 +22749,7 @@ def start_spotify_public_discovery(url_hash):
|
|||
@app.route('/api/spotify-public/discovery/status/<url_hash>', methods=['GET'])
|
||||
def get_spotify_public_discovery_status(url_hash):
|
||||
"""Get real-time discovery status for a Spotify Public playlist"""
|
||||
try:
|
||||
if url_hash not in spotify_public_discovery_states:
|
||||
return jsonify({"error": "Spotify Public discovery not found"}), 404
|
||||
|
||||
state = spotify_public_discovery_states[url_hash]
|
||||
state['last_accessed'] = time.time()
|
||||
|
||||
response = {
|
||||
'phase': state['phase'],
|
||||
'status': state['status'],
|
||||
'progress': state['discovery_progress'],
|
||||
'spotify_matches': state['spotify_matches'],
|
||||
'spotify_total': state['spotify_total'],
|
||||
'results': state['discovery_results'],
|
||||
'complete': state['phase'] == 'discovered'
|
||||
}
|
||||
|
||||
return jsonify(response)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting Spotify Public discovery status: {e}")
|
||||
return jsonify({"error": str(e)}), 500
|
||||
return _get_source_discovery_status(spotify_public_discovery_states, url_hash, "Spotify Public discovery not found", "Spotify Public")
|
||||
|
||||
@app.route('/api/spotify-public/discovery/update_match', methods=['POST'])
|
||||
def update_spotify_public_discovery_match():
|
||||
|
|
@ -23311,23 +23236,7 @@ def start_itunes_link_discovery(url_hash):
|
|||
|
||||
@app.route('/api/itunes-link/discovery/status/<url_hash>', methods=['GET'])
|
||||
def get_itunes_link_discovery_status(url_hash):
|
||||
try:
|
||||
if url_hash not in itunes_link_discovery_states:
|
||||
return jsonify({"error": "iTunes Link discovery not found"}), 404
|
||||
state = itunes_link_discovery_states[url_hash]
|
||||
state['last_accessed'] = time.time()
|
||||
return jsonify({
|
||||
'phase': state['phase'],
|
||||
'status': state['status'],
|
||||
'progress': state['discovery_progress'],
|
||||
'spotify_matches': state['spotify_matches'],
|
||||
'spotify_total': state['spotify_total'],
|
||||
'results': state['discovery_results'],
|
||||
'complete': state['phase'] == 'discovered'
|
||||
})
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting iTunes Link discovery status: {e}")
|
||||
return jsonify({"error": str(e)}), 500
|
||||
return _get_source_discovery_status(itunes_link_discovery_states, url_hash, "iTunes Link discovery not found", "iTunes Link")
|
||||
|
||||
|
||||
def _update_itunes_link_discovery_result(identifier, track_index, spotify_track):
|
||||
|
|
@ -23727,28 +23636,7 @@ def start_youtube_discovery(url_hash):
|
|||
@app.route('/api/youtube/discovery/status/<url_hash>', methods=['GET'])
|
||||
def get_youtube_discovery_status(url_hash):
|
||||
"""Get real-time discovery status for a YouTube playlist"""
|
||||
try:
|
||||
if url_hash not in youtube_playlist_states:
|
||||
return jsonify({"error": "YouTube playlist not found"}), 404
|
||||
|
||||
state = youtube_playlist_states[url_hash]
|
||||
state['last_accessed'] = time.time() # Update access time
|
||||
|
||||
response = {
|
||||
'phase': state['phase'],
|
||||
'status': state['status'],
|
||||
'progress': state['discovery_progress'],
|
||||
'spotify_matches': state['spotify_matches'],
|
||||
'spotify_total': state['spotify_total'],
|
||||
'results': state['discovery_results'],
|
||||
'complete': state['phase'] == 'discovered'
|
||||
}
|
||||
|
||||
return jsonify(response)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting YouTube discovery status: {e}")
|
||||
return jsonify({"error": str(e)}), 500
|
||||
return _get_source_discovery_status(youtube_playlist_states, url_hash, "YouTube playlist not found", "YouTube")
|
||||
|
||||
|
||||
@app.route('/api/youtube/discovery/unmatch', methods=['POST'])
|
||||
|
|
@ -30002,29 +29890,7 @@ def start_listenbrainz_discovery(playlist_mbid):
|
|||
@app.route('/api/listenbrainz/discovery/status/<playlist_mbid>', methods=['GET'])
|
||||
def get_listenbrainz_discovery_status(playlist_mbid):
|
||||
"""Get real-time discovery status for a ListenBrainz playlist"""
|
||||
try:
|
||||
state_key = _lb_state_key(playlist_mbid)
|
||||
if state_key not in listenbrainz_playlist_states:
|
||||
return jsonify({"error": "ListenBrainz playlist not found"}), 404
|
||||
|
||||
state = listenbrainz_playlist_states[state_key]
|
||||
state['last_accessed'] = time.time()
|
||||
|
||||
response = {
|
||||
'phase': state['phase'],
|
||||
'status': state['status'],
|
||||
'progress': state['discovery_progress'],
|
||||
'spotify_matches': state['spotify_matches'],
|
||||
'spotify_total': state['spotify_total'],
|
||||
'results': state['discovery_results'],
|
||||
'complete': state['phase'] == 'discovered'
|
||||
}
|
||||
|
||||
return jsonify(response)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting ListenBrainz discovery status: {e}")
|
||||
return jsonify({"error": str(e)}), 500
|
||||
return _get_source_discovery_status(listenbrainz_playlist_states, _lb_state_key(playlist_mbid), "ListenBrainz playlist not found", "ListenBrainz")
|
||||
|
||||
@app.route('/api/listenbrainz/update-phase/<playlist_mbid>', methods=['POST'])
|
||||
def update_listenbrainz_phase(playlist_mbid):
|
||||
|
|
@ -32018,28 +31884,7 @@ def start_beatport_discovery(url_hash):
|
|||
@app.route('/api/beatport/discovery/status/<url_hash>', methods=['GET'])
|
||||
def get_beatport_discovery_status(url_hash):
|
||||
"""Get real-time discovery status for a Beatport chart"""
|
||||
try:
|
||||
if url_hash not in beatport_chart_states:
|
||||
return jsonify({"error": "Beatport chart not found"}), 404
|
||||
|
||||
state = beatport_chart_states[url_hash]
|
||||
state['last_accessed'] = time.time()
|
||||
|
||||
response = {
|
||||
'phase': state['phase'],
|
||||
'status': state['status'],
|
||||
'progress': state['discovery_progress'],
|
||||
'spotify_matches': state['spotify_matches'],
|
||||
'spotify_total': state['spotify_total'],
|
||||
'results': state['discovery_results'],
|
||||
'complete': state['phase'] == 'discovered'
|
||||
}
|
||||
|
||||
return jsonify(response)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting Beatport discovery status: {e}")
|
||||
return jsonify({"error": str(e)}), 500
|
||||
return _get_source_discovery_status(beatport_chart_states, url_hash, "Beatport chart not found", "Beatport")
|
||||
|
||||
|
||||
@app.route('/api/beatport/discovery/update_match', methods=['POST'])
|
||||
|
|
|
|||
Loading…
Reference in a new issue