From a9e8dc2b9550ba0390c8f28b216fba457bd2beeb Mon Sep 17 00:00:00 2001 From: Wenbin Fang Date: Fri, 25 Sep 2026 12:56:54 -0700 Subject: [PATCH] Add deletePlaylist support for JavaScript SDK 3.1.0 --- README.md | 26 ++++++++++++++++++++++++++ package.json | 2 +- src/PodcastApiMethods.js | 3 +++ src/api-contract.json | 21 ++++++++++++++++++++- tests/TestsLib.js | 9 +++++++-- tests/integration/MockApiTests.js | 8 ++++++++ 6 files changed, 65 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 38d5d57..73d67eb 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,7 @@ If you have any questions, please contact [hello@listennotes.com](hello@listenno - [`fetchPodcastsByDomain`](#fetchpodcastsbydomain) — `GET /podcasts/domains/{domain_name}` - [`createPlaylist`](#createplaylist) — `POST /playlists` - [`updatePlaylist`](#updateplaylist) — `PUT /playlists/{id}` +- [`deletePlaylist`](#deleteplaylist) — `DELETE /playlists/{id}` - [`addPlaylistItem`](#addplaylistitem) — `POST /playlists/{id}/items` - [`deletePlaylistItem`](#deleteplaylistitem) — `DELETE /playlists/{id}/items/{item_id}` - [`updatePlaylistItemNotes`](#updateplaylistitemnotes) — `PUT /playlists/{id}/items/{item_id}` @@ -783,6 +784,31 @@ client.updatePlaylist({ [Full API documentation](https://www.listennotes.com/api/docs/#put-api-v2-playlists-id) +### deletePlaylist + +Delete a playlist. + +`DELETE /playlists/{id}` + +Permanently delete a playlist, including all episode and podcast references saved in this specific playlist and their notes. The actual episodes and podcasts remain in the Listen Notes podcast database. + +**Warning: Deletion cannot be undone. Once deleted, the playlist is gone, regardless of how many episodes or podcasts it contains. You, the developer, are responsible for adding a confirmation step in your app's UI before calling this endpoint to prevent accidental deletion.** + +Only playlists owned by your admin API account can be modified; contributor membership does not grant write access. + +```javascript +const { Client } = require('podcast-api'); +const client = Client({ apiKey: process.env.LISTEN_API_KEY || null }); + +client.deletePlaylist({ + "id": "m1pe7z60bsw" +}) + .then((response) => console.log(response.data)) + .catch((error) => console.error(error)); +``` + +[Full API documentation](https://www.listennotes.com/api/docs/#delete-api-v2-playlists-id) + ### addPlaylistItem Add an episode or podcast to a playlist. diff --git a/package.json b/package.json index edaabbc..0316407 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "podcast-api", - "version": "3.0.2", + "version": "3.1.0", "description": "JavaScript bindings for the Listen Notes Podcast API", "main": "src/PodcastApiClient.js", "scripts": { diff --git a/src/PodcastApiMethods.js b/src/PodcastApiMethods.js index 5b61aad..6c4a332 100644 --- a/src/PodcastApiMethods.js +++ b/src/PodcastApiMethods.js @@ -83,6 +83,9 @@ const addApiMethodsToClient = (client) => { client.updatePlaylist = (params = {}) => request( client.httpClient, "PUT", "/playlists/{id}", {"path": ["id"], "query": [], "body": ["name", "description", "visibility", "type"]}, params); + client.deletePlaylist = (params = {}) => request( + client.httpClient, "DELETE", "/playlists/{id}", + {"path": ["id"], "query": [], "body": []}, params); client.addPlaylistItem = (params = {}) => request( client.httpClient, "POST", "/playlists/{id}/items", {"path": ["id"], "query": [], "body": ["episode_id", "podcast_id", "notes"]}, params); diff --git a/src/api-contract.json b/src/api-contract.json index a4e1ac9..3866deb 100644 --- a/src/api-contract.json +++ b/src/api-contract.json @@ -1,6 +1,6 @@ { "schema_version": 1, - "version": "3.0.2", + "version": "3.1.0", "operations": [ { "operationId": "search", @@ -815,6 +815,25 @@ "summary": "Update playlist metadata.", "description": "Update any subset of name, description, visibility, and type. Omitted fields remain unchanged; at least one field is required. Switching to private rotates the playlist RSS secret. Type selects the saved default view (episode_list or podcast_list) and the returned listennotes_url; changing it preserves all existing episodes and podcasts.\n\nOnly playlists owned by your admin API account can be modified; contributor membership does not grant write access." }, + { + "operationId": "deletePlaylist", + "func": "deletePlaylist", + "available_from": "3.1.0", + "method": "DELETE", + "path": "/playlists/{id}", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true + } + ], + "example_params": { + "id": "m1pe7z60bsw" + }, + "summary": "Delete a playlist.", + "description": "Permanently delete a playlist, including all episode and podcast references saved in this specific playlist and their notes. The actual episodes and podcasts remain in the Listen Notes podcast database.\n\n**Warning: Deletion cannot be undone. Once deleted, the playlist is gone, regardless of how many episodes or podcasts it contains. You, the developer, are responsible for adding a confirmation step in your app's UI before calling this endpoint to prevent accidental deletion.**\n\nOnly playlists owned by your admin API account can be modified; contributor membership does not grant write access." + }, { "operationId": "addPlaylistItem", "func": "addPlaylistItem", diff --git a/tests/TestsLib.js b/tests/TestsLib.js index f994c0c..5c2075b 100644 --- a/tests/TestsLib.js +++ b/tests/TestsLib.js @@ -91,9 +91,14 @@ const runTests = ({ createClient, reset, calls, setStatus }) => { expect(calls()[0].body).toBe('description='); }); - test('missing nested identifiers fail before any request', () => { + test.each([ + ['deletePlaylist', undefined, 'id'], + ['deletePlaylist', { id: '' }, 'id'], + ['deletePlaylist', { id: null }, 'id'], + ['deletePlaylistItem', { id: 'abc' }, 'item_id'], + ])('%s rejects missing path identifiers in %p before any request', (method, params, name) => { const client = createClient(); - expect(() => client.deletePlaylistItem({ id: 'abc' })).toThrow('Missing path parameter: item_id'); + expect(() => client[method](params)).toThrow(`Missing path parameter: ${name}`); expect(calls()).toHaveLength(0); }); diff --git a/tests/integration/MockApiTests.js b/tests/integration/MockApiTests.js index 5fdeea4..abe4efe 100644 --- a/tests/integration/MockApiTests.js +++ b/tests/integration/MockApiTests.js @@ -124,6 +124,14 @@ const runIntegrationTests = ({ createClient, calls, reset }) => { expect(calls()[0].body).toBeFalsy(); }); + test('delete a playlist', async () => { + const response = await client.deletePlaylist({ id: PLAYLIST_ID }); + const payload = responseData(response, 'DELETE', `/playlists/${PLAYLIST_ID}`); + expect(payload).toEqual({ id: PLAYLIST_ID, deleted: true }); + expect(new URL(calls()[0].url).search).toBe(''); + expect(calls()[0].body).toBeFalsy(); + }); + test('missing routes preserve the HTTP 404 error', async () => { await expect(client.httpClient._get('/sdk-integration-missing-route', {})).rejects.toMatchObject({ response: { status: 404 },