Add cancellation support to all discovery workers
This commit is contained in:
parent
41edb31e07
commit
5f94352b40
3 changed files with 45 additions and 4 deletions
|
|
@ -238,9 +238,20 @@ class PlaylistSyncService:
|
|||
logger.warning(f"❌ Track {i+1} invalid for playlist: {track} (type: {type(track)}, has ratingKey: {hasattr(track, 'ratingKey') if track else 'N/A'})")
|
||||
|
||||
logger.info(f"Playlist validation: {len(valid_tracks)}/{len(media_tracks)} tracks are valid {server_type.title()} objects with ratingKeys")
|
||||
|
||||
# Use the validated tracks for the sync
|
||||
plex_tracks = valid_tracks # Keep variable name for compatibility with the rest of the function
|
||||
|
||||
# Deduplicate by ratingKey — media servers silently drop duplicates,
|
||||
# so count should reflect what actually ends up in the playlist
|
||||
seen_keys = set()
|
||||
deduped_tracks = []
|
||||
for t in valid_tracks:
|
||||
if t.ratingKey not in seen_keys:
|
||||
seen_keys.add(t.ratingKey)
|
||||
deduped_tracks.append(t)
|
||||
if len(deduped_tracks) < len(valid_tracks):
|
||||
logger.info(f"Deduplicated {len(valid_tracks) - len(deduped_tracks)} duplicate ratingKeys ({len(valid_tracks)} → {len(deduped_tracks)} tracks)")
|
||||
|
||||
# Use the deduplicated tracks for the sync
|
||||
plex_tracks = deduped_tracks
|
||||
|
||||
# Use active media server for playlist sync
|
||||
media_client, server_type = self._get_active_media_client()
|
||||
|
|
|
|||
|
|
@ -20852,6 +20852,8 @@ def update_tidal_playlist_phase(playlist_id):
|
|||
return jsonify({"error": str(e)}), 500
|
||||
|
||||
|
||||
_playlist_discovery_cancelled = set() # Set of automation_ids that have been cancelled
|
||||
|
||||
def _run_playlist_discovery_worker(playlists, automation_id=None):
|
||||
"""Background worker that discovers Spotify/iTunes metadata for undiscovered
|
||||
mirrored playlist tracks. Stores results in extra_data for use by sync."""
|
||||
|
|
@ -20931,6 +20933,16 @@ def _run_playlist_discovery_worker(playlists, automation_id=None):
|
|||
log_line=f'{len(undiscovered_tracks)} tracks to discover', log_type='info')
|
||||
|
||||
for i, track in enumerate(undiscovered_tracks):
|
||||
# Check for cancellation
|
||||
if automation_id and automation_id in _playlist_discovery_cancelled:
|
||||
_playlist_discovery_cancelled.discard(automation_id)
|
||||
print(f"🛑 Playlist discovery cancelled (automation {automation_id})")
|
||||
_update_automation_progress(automation_id, status='finished', progress=100,
|
||||
phase='Discovery cancelled',
|
||||
log_line=f'Cancelled: {total_discovered} discovered, {total_failed} failed',
|
||||
log_type='info')
|
||||
return
|
||||
|
||||
total_tracks += 1
|
||||
track_id = track['id']
|
||||
track_name = track.get('track_name', '')
|
||||
|
|
@ -21995,6 +22007,11 @@ def _run_youtube_discovery_worker(url_hash):
|
|||
# Process each track for discovery
|
||||
for i, track in enumerate(tracks):
|
||||
try:
|
||||
# Check for cancellation (phase changed by reset/delete/close)
|
||||
if state.get('phase') != 'discovering':
|
||||
print(f"🛑 Discovery cancelled for {url_hash} (phase changed to '{state.get('phase')}')")
|
||||
return
|
||||
|
||||
# Update progress
|
||||
state['discovery_progress'] = int((i / len(tracks)) * 100)
|
||||
|
||||
|
|
@ -22297,6 +22314,11 @@ def _run_listenbrainz_discovery_worker(playlist_mbid):
|
|||
# Process each track for discovery
|
||||
for i, track in enumerate(tracks):
|
||||
try:
|
||||
# Check for cancellation
|
||||
if state.get('phase') != 'discovering':
|
||||
print(f"🛑 ListenBrainz discovery cancelled (phase changed to '{state.get('phase')}')")
|
||||
return
|
||||
|
||||
# Update progress
|
||||
state['discovery_progress'] = int((i / len(tracks)) * 100)
|
||||
|
||||
|
|
@ -29417,6 +29439,11 @@ def _run_beatport_discovery_worker(url_hash):
|
|||
# Process each track for discovery
|
||||
for i, track in enumerate(tracks):
|
||||
try:
|
||||
# Check for cancellation
|
||||
if state.get('phase') != 'discovering':
|
||||
print(f"🛑 Beatport discovery cancelled (phase changed to '{state.get('phase')}')")
|
||||
return
|
||||
|
||||
# Update progress
|
||||
state['discovery_progress'] = int((i / len(tracks)) * 100)
|
||||
|
||||
|
|
|
|||
|
|
@ -43112,8 +43112,11 @@ async function clearMirroredDiscovery(playlistId, name) {
|
|||
const data = await res.json();
|
||||
if (data.success) {
|
||||
showToast(`Cleared discovery for ${name} (${data.cleared} tracks)`, 'success');
|
||||
// Also clear the discovery state and remove stale modal DOM
|
||||
// Signal cancellation to any running worker, then clear state
|
||||
const hash = `mirrored_${playlistId}`;
|
||||
if (youtubePlaylistStates[hash]) {
|
||||
youtubePlaylistStates[hash].phase = 'cancelled';
|
||||
}
|
||||
delete youtubePlaylistStates[hash];
|
||||
const staleModal = document.getElementById(`youtube-discovery-modal-${hash}`);
|
||||
if (staleModal) staleModal.remove();
|
||||
|
|
|
|||
Loading…
Reference in a new issue