From cc834961911a4b32454a661ce18abb06e8bde33c Mon Sep 17 00:00:00 2001 From: Raymond Jacobson Date: Wed, 13 May 2026 16:12:06 -0700 Subject: [PATCH] fix(playlist): allow removing the last track from a playlist MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The trash icon on each row in the edit-collection form was disabled whenever only one track remained. That guard makes sense for the upload flow (which reuses the same component) but blocked edits to existing playlists from ever emptying out. Thread `isUpload` through `CollectionTrackFieldArray` so the guard only applies during upload. Even via the kebab menu's "Remove from playlist" path, the removal of the final track confirmed client-side but reappeared on reload. In the discovery-provider entity manager, `populate_playlist_record_metadata` was using a truthiness check on `playlist_metadata["playlist_contents"]` — an empty list (the SDK's serialization for "no tracks") is falsy in Python, so the field was silently treated as omitted. Compare with `is None` instead so an explicit empty list is applied. `update_playlist_tracks` already handles an empty `track_ids` correctly (marks all existing rows removed). Co-Authored-By: Claude Opus 4.7 --- .../src/tasks/entity_manager/entities/playlist.py | 5 ++++- .../components/edit-collection/EditCollectionForm.tsx | 2 +- .../edit/fields/CollectionTrackFieldArray.tsx | 10 ++++++++-- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/packages/discovery-provider/src/tasks/entity_manager/entities/playlist.py b/packages/discovery-provider/src/tasks/entity_manager/entities/playlist.py index 6a9ae67d2d9..34e8b934c32 100644 --- a/packages/discovery-provider/src/tasks/entity_manager/entities/playlist.py +++ b/packages/discovery-provider/src/tasks/entity_manager/entities/playlist.py @@ -677,7 +677,10 @@ def populate_playlist_record_metadata( # Update the playlist_record when the corresponding field exists # in playlist_metadata if key == "playlist_contents": - if not playlist_metadata.get(key): + # Use `is None` rather than a truthiness check so that an explicit + # empty list (the user removing the last track) is applied instead + # of being silently treated as "field omitted". + if playlist_metadata.get(key) is None: continue playlist_record.playlist_contents = process_playlist_contents( playlist_record, diff --git a/packages/web/src/components/edit-collection/EditCollectionForm.tsx b/packages/web/src/components/edit-collection/EditCollectionForm.tsx index e1d9f4a0461..932eb3dbb3c 100644 --- a/packages/web/src/components/edit-collection/EditCollectionForm.tsx +++ b/packages/web/src/components/edit-collection/EditCollectionForm.tsx @@ -195,7 +195,7 @@ export const EditCollectionForm = (props: EditCollectionFormProps) => { ) : null} - + {isUpload ? : } {playlist_id ? ( <> diff --git a/packages/web/src/components/edit/fields/CollectionTrackFieldArray.tsx b/packages/web/src/components/edit/fields/CollectionTrackFieldArray.tsx index dc187dbee54..248bfcc1673 100644 --- a/packages/web/src/components/edit/fields/CollectionTrackFieldArray.tsx +++ b/packages/web/src/components/edit/fields/CollectionTrackFieldArray.tsx @@ -66,7 +66,13 @@ const makeTrackKey = (track: TrackForUpload | TrackForEdit, index: number) => { return `${track.metadata.track_id}${suffix}` } -export const CollectionTrackFieldArray = () => { +type CollectionTrackFieldArrayProps = { + isUpload?: boolean +} + +export const CollectionTrackFieldArray = ({ + isUpload = false +}: CollectionTrackFieldArrayProps = {}) => { const [{ value: tracks }] = useField<(TrackForUpload | TrackForEdit)[]>('tracks') @@ -104,7 +110,7 @@ export const CollectionTrackFieldArray = () => { )}