Commit graph

1 commit

Author SHA1 Message Date
BoulderBadgeDad
465a77f01d playlist export: Spotify/Deezer export job + endpoint (#945, increment 4)
Ties the resolver + write clients into a working backend, reusing the ListenBrainz export's
resolve→push→store-target shape:

- _run_service_export(job, db, playlist_id, title, service, client, resolve_fn): resolves the
  mirrored playlist's tracks to their stored service track IDs (id_key='service_track_id'),
  guards "nothing matched", pushes via the injected write client, and stores the returned
  playlist id as the export target so a re-export updates in place (idempotent, like LB #903).
  Deps injected → unit-testable without a DB or live service.
- _run_playlist_export dispatches mode in {spotify, deezer} to it (builds the real client +
  service resolver); the existing download/push (ListenBrainz/JSPF) flow is untouched.
- POST /api/playlists/<id>/export/service/<service> — distinct path so it can't collide with
  the existing /export/listenbrainz route; validates the target, starts the background job,
  returns {job_id} polled via the shared status endpoint.

5 orchestration tests (fake db/client/resolve_fn): success stores target + passes ids in order,
no-match → error with no push, client None → not-connected error, push failure surfaces the
client's error and stores nothing, re-export passes the existing target id. ruff clean.

Last piece: the modal options (Sync to Spotify / Deezer, gated on auth, unmatched count surfaced).
2026-06-28 21:20:06 -07:00