diff --git a/src/js/services/core/actions.js b/src/js/services/core/actions.js index e97e271d..914359a6 100755 --- a/src/js/services/core/actions.js +++ b/src/js/services/core/actions.js @@ -371,9 +371,7 @@ export function reorderPlaylistTracks(uri, indexes, insert_before, snapshot_id = insert_before, snapshot_id, }; - - case 'm3u': - case 'gmusic': + default: return { type: 'MOPIDY_REORDER_PLAYLIST_TRACKS', key: uri, @@ -381,40 +379,22 @@ export function reorderPlaylistTracks(uri, indexes, insert_before, snapshot_id = range_length: range.length, insert_before, }; - - default: - return { - type: 'UNSUPPORTED_ACTION', - name: 'reorderPlaylistTracks', - }; } } -export function savePlaylist(uri, name, description = '', is_public = false, is_collaborative = false, image = null) { +export function savePlaylist(uri, data) { switch (uriSource(uri)) { case 'spotify': return { type: 'SPOTIFY_SAVE_PLAYLIST', key: uri, - name, - description: (description == '' ? null : description), - image, - is_public, - is_collaborative, + data, }; - - case 'm3u': - case 'gmusic': + default: return { type: 'MOPIDY_SAVE_PLAYLIST', key: uri, - name, - }; - - default: - return { - type: 'UNSUPPORTED_ACTION', - name: 'savePlaylist', + data, }; } } @@ -432,7 +412,6 @@ export function deletePlaylist(uri) { switch (uriSource(uri)) { case 'spotify': return spotifyActions.following(uri, 'DELETE'); - default: return mopidyActions.deletePlaylist(uri); } @@ -446,20 +425,12 @@ export function removeTracksFromPlaylist(uri, tracks_indexes) { key: uri, tracks_indexes, }; - - case 'm3u': - case 'gmusic': + default: return { type: 'MOPIDY_REMOVE_PLAYLIST_TRACKS', key: uri, tracks_indexes, }; - - default: - return { - type: 'UNSUPPORTED_ACTION', - name: 'removeTracksFromPlaylist', - }; } } @@ -471,20 +442,12 @@ export function addTracksToPlaylist(uri, tracks_uris) { key: uri, tracks_uris, }; - - case 'm3u': - case 'gmusic': + default: return { type: 'MOPIDY_ADD_PLAYLIST_TRACKS', key: uri, tracks_uris, }; - - default: - return { - type: 'UNSUPPORTED_ACTION', - name: 'addTracksToPlaylist', - }; } } diff --git a/src/js/services/core/middleware.js b/src/js/services/core/middleware.js index be1a4b6b..2fe79c2f 100755 --- a/src/js/services/core/middleware.js +++ b/src/js/services/core/middleware.js @@ -275,10 +275,8 @@ const CoreMiddleware = (function () { case 'spotify': store.dispatch(spotifyActions.getPlaylist(key, {})); break; - case 'm3u': - store.dispatch(mopidyActions.getPlaylist(key, {})); - break; default: + store.dispatch(mopidyActions.getPlaylist(key, {})); break; } next(action); diff --git a/src/js/services/mopidy/middleware.js b/src/js/services/mopidy/middleware.js index 227f094c..621ed765 100755 --- a/src/js/services/mopidy/middleware.js +++ b/src/js/services/mopidy/middleware.js @@ -1423,7 +1423,7 @@ const MopidyMiddleware = (function () { // requires a Mopidy playlist object (with updates) request(store, 'playlists.lookup', { uri: action.key }) .then((response) => { - const mopidy_playlist = { ...response, name: action.name }; + const mopidy_playlist = { ...response, ...action.data }; request(store, 'playlists.save', { playlist: mopidy_playlist }) .then((response) => { @@ -1432,9 +1432,9 @@ const MopidyMiddleware = (function () { // Overwrite our playlist with the response to our save // This is essential to get the updated URI from Mopidy const playlist = { + ...action.data, ...store.getState().core.items[action.key], - uri: response.uri, - name: response.name, + ...response, }; // When we rename a playlist, the URI also changes to reflect the name change. @@ -2154,134 +2154,57 @@ const MopidyMiddleware = (function () { case 'MOPIDY_GET_LIBRARY_PLAYLISTS': { store.dispatch(uiActions.startProcess(action.type, { notification: false })); - // Built-in playlist support works differently to other providers - if (action.uri === 'm3u:playlists') { - request(store, 'playlists.asList') - .then((listResponse) => { - const libraryPlaylists = []; - const playlist_uris = arrayOf('uri', listResponse).filter( - (pUri) => (pUri.indexOf('m3u') > -1), - ); - store.dispatch( - uiActions.updateProcess( - action.type, - { - total: playlist_uris.length, - remaining: playlist_uris.length, - }, - ), - ); + request(store, 'playlists.asList').then((browseResponse) => { + const allUris = arrayOf('uri', browseResponse); + store.dispatch( + uiActions.updateProcess( + action.type, + { + total: allUris.length, + remaining: allUris.length, + }, + ), + ); - if (playlist_uris.length) { - playlist_uris.forEach((uri, index) => { - request(store, 'playlists.lookup', { uri }) - .then((response) => { - if (response) { - libraryPlaylists.push( - formatPlaylist({ - name: response.name, - uri: response.uri, - can_edit: uriSource(response.uri) === 'm3u', - last_modified: response.last_modified, - // By not including actual tracks they will be fetched when needed. We don't - // want these simple tracks because they don't contain duration, artist, etc. - tracks_total: response.tracks ? response.tracks.length : null, - }), - ); - } + const run = () => { + if (allUris.length) { + const uri = allUris.splice(0, 1)[0]; + const processor = store.getState().ui.processes[action.type]; - store.dispatch( - uiActions.updateProcess( - action.type, - { - remaining: playlist_uris.length - index - 1, - }, - ), - ); - - if (index === playlist_uris.length - 1) { - store.dispatch(coreActions.itemsLoaded(libraryPlaylists)); - store.dispatch(coreActions.libraryLoaded({ - uri: action.uri, - type: 'playlists', - items_uris: arrayOf('uri', libraryPlaylists), - })); - store.dispatch(uiActions.processFinished(action.type)); - } - }); - }); - } else { - store.dispatch(coreActions.libraryLoaded({ - uri: action.uri, - type: 'playlists', - items_uris: [], - })); - store.dispatch(uiActions.stopLoading('mopidy:library:playlists')); - store.dispatch(uiActions.processFinished(action.type)); + if (processor && processor.status === 'cancelling') { + store.dispatch(uiActions.processCancelled(action.type)); + store.dispatch(uiActions.stopLoading(action.uri)); + return; } - }); - } else { - request(store, 'library.browse', { uri: action.uri }) - .then((browseResponse) => { - const libraryPlaylists = []; + store.dispatch(uiActions.updateProcess(action.type, { remaining: allUris.length })); - store.dispatch( - uiActions.updateProcess( - action.type, - { - total: browseResponse.length, - remaining: browseResponse.length, - }, - ), - ); + request(store, 'playlists.lookup', { uri }).then((lookupResponse) => { + if (lookupResponse) { + const playlist = formatPlaylist({ + name: lookupResponse.name, + uri: lookupResponse.uri, + can_edit: true, // TODO: Confirm whether some are uneditable?? + last_modified: lookupResponse.last_modified, + // Don't include simple tracks; they don't contain duration, artist, etc. + tracks_total: lookupResponse?.tracks?.length || null, + }); - if (browseResponse.length) { - browseResponse.forEach((playlist, index) => { - request(store, 'library.lookup', { uris: [playlist.uri] }) - .then((response) => { - if (response) { - libraryPlaylists.push( - formatPlaylist({ - name: playlist.name, - uri: playlist.uri, - can_edit: uriSource(playlist.uri) === 'm3u', - last_modified: playlist.last_modified, - tracks: formatTracks(response[playlist.uri]), - }), - ); - } + store.dispatch(coreActions.itemLoaded(playlist)); + } + run(); + }); + } else { + store.dispatch(uiActions.processFinished(action.type)); + store.dispatch(coreActions.libraryLoaded({ + uri: action.uri, + type: 'playlists', + items_uris: arrayOf('uri', browseResponse), + })); + } + }; - store.dispatch( - uiActions.updateProcess( - action.type, - { - remaining: browseResponse.length - index - 1, - }, - ), - ); - - if (index === browseResponse.length - 1) { - store.dispatch(coreActions.itemsLoaded(libraryPlaylists)); - store.dispatch(coreActions.libraryLoaded({ - uri: action.uri, - type: 'playlists', - items_uris: arrayOf('uri', libraryPlaylists), - })); - store.dispatch(uiActions.processFinished(action.type)); - } - }); - }); - } else { - store.dispatch(coreActions.libraryLoaded({ - uri: action.uri, - type: 'playlists', - items_uris: [], - })); - store.dispatch(uiActions.stopLoading('mopidy:library:playlists')); - store.dispatch(uiActions.processFinished(action.type)); - } - }); - } + run(); + }); break; } diff --git a/src/js/services/spotify/actions.js b/src/js/services/spotify/actions.js index 474601fe..be9a269d 100755 --- a/src/js/services/spotify/actions.js +++ b/src/js/services/spotify/actions.js @@ -1237,14 +1237,8 @@ export function createPlaylist(playlist) { }; } -export function savePlaylist(uri, name, description, is_public, is_collaborative, image) { +export function savePlaylist(uri, { image, ...data }) { return (dispatch, getState) => { - const data = { - name, - description, - public: is_public, - collaborative: is_collaborative, - }; const { spotify: { me: { diff --git a/src/js/util/selectors.js b/src/js/util/selectors.js index 933cd9d2..03782f69 100755 --- a/src/js/util/selectors.js +++ b/src/js/util/selectors.js @@ -121,6 +121,11 @@ const providers = { uri: 'jellyfin:playlists', title: i18n('services.jellyfin.title'), }, + { + scheme: 'ytmusic:', + uri: 'ytmusic:playlists', + title: i18n('services.youtube.title'), + }, ], albums: [ { diff --git a/src/js/views/Modals/CreatePlaylist.js b/src/js/views/Modals/CreatePlaylist.js index 114f2365..109cc27b 100755 --- a/src/js/views/Modals/CreatePlaylist.js +++ b/src/js/views/Modals/CreatePlaylist.js @@ -154,6 +154,18 @@ const CreatePlaylist = () => { +