diff --git a/client/components/app/BookShelfToolbar.vue b/client/components/app/BookShelfToolbar.vue
index b2bd0330..9829fec0 100644
--- a/client/components/app/BookShelfToolbar.vue
+++ b/client/components/app/BookShelfToolbar.vue
@@ -165,6 +165,9 @@ export default {
isCollectionsPage() {
return this.page === 'collections'
},
+ isPlaylistsPage() {
+ return this.page === 'playlists'
+ },
isHomePage() {
return this.$route.name === 'library-library'
},
@@ -185,6 +188,7 @@ export default {
if (!this.page) return this.$strings.LabelBooks
if (this.isSeriesPage) return this.$strings.LabelSeries
if (this.isCollectionsPage) return this.$strings.LabelCollections
+ if (this.isPlaylistsPage) return this.$strings.LabelPlaylists
return ''
},
seriesId() {
diff --git a/client/components/app/LazyBookshelf.vue b/client/components/app/LazyBookshelf.vue
index 082d1096..fa486ee4 100644
--- a/client/components/app/LazyBookshelf.vue
+++ b/client/components/app/LazyBookshelf.vue
@@ -87,11 +87,11 @@ export default {
emptyMessage() {
if (this.page === 'series') return this.$strings.MessageBookshelfNoSeries
if (this.page === 'collections') return this.$strings.MessageBookshelfNoCollections
+ if (this.page === 'playlists') return this.$strings.MessageNoUserPlaylists
if (this.hasFilter) {
if (this.filterName === 'Issues') return this.$strings.MessageNoIssues
else if (this.filterName === 'Feed-open') return this.$strings.MessageBookshelfNoRSSFeeds
return this.$getString('MessageBookshelfNoResultsForFilter', [this.filterName, this.filterValue])
- // return `No Results for filter "${this.filterName}: ${this.filterValue}"`
}
return this.$strings.MessageNoResults
},
@@ -178,7 +178,7 @@ export default {
return this.shelfPadding * 2
},
entityWidth() {
- if (this.entityName === 'series' || this.entityName === 'collections') {
+ if (this.entityName === 'series' || this.entityName === 'collections' || this.entityName === 'playlists') {
if (this.bookWidth * 2 > this.bookshelfWidth - this.shelfPadding) return this.bookWidth * 1.6
return this.bookWidth * 2
}
@@ -302,11 +302,11 @@ export default {
this.currentSFQueryString = this.buildSearchParams()
}
- var entityPath = this.entityName === 'books' || this.entityName === 'series-books' ? `items` : this.entityName
- var sfQueryString = this.currentSFQueryString ? this.currentSFQueryString + '&' : ''
- var fullQueryString = `?${sfQueryString}limit=${this.booksPerFetch}&page=${page}&minified=1`
+ const entityPath = this.entityName === 'books' || this.entityName === 'series-books' ? 'items' : this.entityName
+ const sfQueryString = this.currentSFQueryString ? this.currentSFQueryString + '&' : ''
+ const fullQueryString = `?${sfQueryString}limit=${this.booksPerFetch}&page=${page}&minified=1`
- var payload = await this.$axios.$get(`/api/libraries/${this.currentLibraryId}/${entityPath}${fullQueryString}`).catch((error) => {
+ const payload = await this.$axios.$get(`/api/libraries/${this.currentLibraryId}/${entityPath}${fullQueryString}`).catch((error) => {
console.error('failed to fetch books', error)
return null
})
diff --git a/client/components/app/SideRail.vue b/client/components/app/SideRail.vue
index f42beda6..029d5095 100644
--- a/client/components/app/SideRail.vue
+++ b/client/components/app/SideRail.vue
@@ -71,6 +71,14 @@
+
+ playlist_play
+
+ {{ $strings.ButtonPlaylists }}
+
+
+
+
warning
@@ -143,6 +151,9 @@ export default {
isAuthorsPage() {
return this.$route.name === 'library-library-authors'
},
+ isPlaylistsPage() {
+ return this.paramId === 'playlists'
+ },
libraryBookshelfPage() {
return this.$route.name === 'library-library-bookshelf-id'
},
@@ -173,6 +184,9 @@ export default {
},
streamLibraryItem() {
return this.$store.state.streamLibraryItem
+ },
+ showPlaylists() {
+ return true
}
},
methods: {
diff --git a/client/components/cards/LazyPlaylistCard.vue b/client/components/cards/LazyPlaylistCard.vue
new file mode 100644
index 00000000..a08c7d38
--- /dev/null
+++ b/client/components/cards/LazyPlaylistCard.vue
@@ -0,0 +1,115 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/client/components/modals/playlists/AddCreateModal.vue b/client/components/modals/playlists/AddCreateModal.vue
index 68d06d68..88d257d9 100644
--- a/client/components/modals/playlists/AddCreateModal.vue
+++ b/client/components/modals/playlists/AddCreateModal.vue
@@ -75,7 +75,7 @@ export default {
return selectedPlaylistItem.libraryItem.media.metadata.title || ''
},
playlists() {
- return this.$store.state.user.playlists || []
+ return this.$store.state.libraries.userPlaylists || []
},
bookCoverAspectRatio() {
return this.$store.getters['libraries/getBookCoverAspectRatio']
@@ -113,9 +113,9 @@ export default {
if (!this.playlists.length) {
this.processing = true
this.$axios
- .$get(`/api/playlists`)
+ .$get(`/api/libraries/${this.currentLibraryId}/playlists`)
.then((data) => {
- this.$store.commit('user/setPlaylists', data.playlists || [])
+ this.$store.commit('libraries/setUserPlaylists', data.results || [])
})
.catch((error) => {
console.error('Failed to get playlists', error)
diff --git a/client/layouts/default.vue b/client/layouts/default.vue
index c3c2a86a..b9e334f1 100644
--- a/client/layouts/default.vue
+++ b/client/layouts/default.vue
@@ -54,9 +54,12 @@ export default {
isCasting() {
return this.$store.state.globals.isCasting
},
+ currentLibraryId() {
+ return this.$store.state.libraries.currentLibraryId
+ },
isShowingSideRail() {
if (!this.$route.name) return false
- return !this.$route.name.startsWith('config') && this.$store.state.libraries.currentLibraryId
+ return !this.$route.name.startsWith('config') && this.currentLibraryId
},
isShowingToolbar() {
return this.isShowingSideRail && this.$route.name !== 'upload' && this.$route.name !== 'account'
@@ -169,7 +172,7 @@ export default {
this.$store.commit('libraries/remove', library)
// When removed currently selected library then set next accessible library
- const currLibraryId = this.$store.state.libraries.currentLibraryId
+ const currLibraryId = this.currentLibraryId
if (currLibraryId === library.id) {
var nextLibrary = this.$store.getters['libraries/getNextAccessibleLibrary']
if (nextLibrary) {
@@ -208,7 +211,7 @@ export default {
libraryItemRemoved(item) {
if (this.$route.name.startsWith('item')) {
if (this.$route.params.id === item.id) {
- this.$router.replace(`/library/${this.$store.state.libraries.currentLibraryId}`)
+ this.$router.replace(`/library/${this.currentLibraryId}`)
}
}
},
@@ -293,35 +296,39 @@ export default {
this.$store.commit('user/updateMediaProgress', payload)
},
collectionAdded(collection) {
+ if (this.currentLibraryId !== collection.libraryId) return
this.$store.commit('libraries/addUpdateCollection', collection)
},
collectionUpdated(collection) {
+ if (this.currentLibraryId !== collection.libraryId) return
this.$store.commit('libraries/addUpdateCollection', collection)
},
collectionRemoved(collection) {
+ if (this.currentLibraryId !== collection.libraryId) return
if (this.$route.name.startsWith('collection')) {
if (this.$route.params.id === collection.id) {
- this.$router.replace(`/library/${this.$store.state.libraries.currentLibraryId}/bookshelf/collections`)
+ this.$router.replace(`/library/${this.currentLibraryId}/bookshelf/collections`)
}
}
this.$store.commit('libraries/removeCollection', collection)
},
playlistAdded(playlist) {
- if (playlist.userId !== this.user.id) return
- this.$store.commit('user/addUpdatePlaylist', playlist)
+ if (playlist.userId !== this.user.id || this.currentLibraryId !== playlist.libraryId) return
+ this.$store.commit('libraries/addUpdateUserPlaylist', playlist)
},
playlistUpdated(playlist) {
- if (playlist.userId !== this.user.id) return
- this.$store.commit('user/addUpdatePlaylist', playlist)
+ if (playlist.userId !== this.user.id || this.currentLibraryId !== playlist.libraryId) return
+ this.$store.commit('libraries/addUpdateUserPlaylist', playlist)
},
playlistRemoved(playlist) {
- if (playlist.userId !== this.user.id) return
+ if (playlist.userId !== this.user.id || this.currentLibraryId !== playlist.libraryId) return
+
if (this.$route.name.startsWith('playlist')) {
if (this.$route.params.id === playlist.id) {
- this.$router.replace(`/library/${this.$store.state.libraries.currentLibraryId}/bookshelf/playlists`)
+ this.$router.replace(`/library/${this.currentLibraryId}/bookshelf/playlists`)
}
}
- this.$store.commit('user/removePlaylist', playlist)
+ this.$store.commit('libraries/removeUserPlaylist', playlist)
},
rssFeedOpen(data) {
this.$store.commit('feeds/addFeed', data)
diff --git a/client/mixins/bookshelfCardsHelpers.js b/client/mixins/bookshelfCardsHelpers.js
index 4eec2eea..69b53647 100644
--- a/client/mixins/bookshelfCardsHelpers.js
+++ b/client/mixins/bookshelfCardsHelpers.js
@@ -2,6 +2,7 @@ import Vue from 'vue'
import LazyBookCard from '@/components/cards/LazyBookCard'
import LazySeriesCard from '@/components/cards/LazySeriesCard'
import LazyCollectionCard from '@/components/cards/LazyCollectionCard'
+import LazyPlaylistCard from '@/components/cards/LazyPlaylistCard'
export default {
data() {
@@ -15,6 +16,7 @@ export default {
getComponentClass() {
if (this.entityName === 'series') return Vue.extend(LazySeriesCard)
if (this.entityName === 'collections') return Vue.extend(LazyCollectionCard)
+ if (this.entityName === 'playlists') return Vue.extend(LazyPlaylistCard)
return Vue.extend(LazyBookCard)
},
async mountEntityCard(index) {
diff --git a/client/pages/library/_library/bookshelf/_id.vue b/client/pages/library/_library/bookshelf/_id.vue
index 8cc77e83..4447b899 100644
--- a/client/pages/library/_library/bookshelf/_id.vue
+++ b/client/pages/library/_library/bookshelf/_id.vue
@@ -16,7 +16,6 @@ export default {
// Set series sort by
if (params.id === 'series') {
- console.log('Series page', query)
if (query.sort) {
store.commit('libraries/setSeriesSortBy', query.sort)
store.commit('libraries/setSeriesSortDesc', !!query.desc)
diff --git a/client/store/libraries.js b/client/store/libraries.js
index 8e215ad7..829b27dc 100644
--- a/client/store/libraries.js
+++ b/client/store/libraries.js
@@ -12,7 +12,8 @@ export const state = () => ({
seriesSortBy: 'name',
seriesSortDesc: false,
seriesFilterBy: 'all',
- collections: []
+ collections: [],
+ userPlaylists: []
})
export const getters = {
@@ -102,6 +103,8 @@ export const actions = {
return false
}
+ const libraryChanging = state.currentLibraryId !== libraryId
+
return this.$axios
.$get(`/api/libraries/${libraryId}?include=filterdata`)
.then((data) => {
@@ -115,7 +118,10 @@ export const actions = {
commit('setLibraryIssues', issues)
commit('setLibraryFilterData', filterData)
commit('setCurrentLibrary', libraryId)
- commit('setCollections', [])
+ if (libraryChanging) {
+ commit('setCollections', [])
+ commit('setUserPlaylists', [])
+ }
return data
})
.catch((error) => {
@@ -320,5 +326,19 @@ export const mutations = {
},
removeCollection(state, collection) {
state.collections = state.collections.filter(c => c.id !== collection.id)
+ },
+ setUserPlaylists(state, playlists) {
+ state.userPlaylists = playlists
+ },
+ addUpdateUserPlaylist(state, playlist) {
+ const index = state.userPlaylists.findIndex(p => p.id === playlist.id)
+ if (index >= 0) {
+ state.userPlaylists.splice(index, 1, playlist)
+ } else {
+ state.userPlaylists.push(playlist)
+ }
+ },
+ removeUserPlaylist(state, playlist) {
+ state.userPlaylists = state.userPlaylists.filter(p => p.id !== playlist.id)
}
}
\ No newline at end of file
diff --git a/client/store/user.js b/client/store/user.js
index d6b82306..7b57b169 100644
--- a/client/store/user.js
+++ b/client/store/user.js
@@ -9,8 +9,7 @@ export const state = () => ({
collapseSeries: false,
collapseBookSeries: false
},
- settingsListeners: [],
- playlists: []
+ settingsListeners: []
})
export const getters = {
@@ -164,19 +163,5 @@ export const mutations = {
},
removeSettingsListener(state, listenerId) {
state.settingsListeners = state.settingsListeners.filter(l => l.id !== listenerId)
- },
- setPlaylists(state, playlists) {
- state.playlists = playlists
- },
- addUpdatePlaylist(state, playlist) {
- const indexOf = state.playlists.findIndex(p => p.id == playlist.id)
- if (indexOf >= 0) {
- state.playlists.splice(indexOf, 1, playlist)
- } else {
- state.playlists.push(playlist)
- }
- },
- removePlaylist(state, playlist) {
- state.playlists = state.playlists.filter(p => p.id !== playlist.id)
}
}
\ No newline at end of file
diff --git a/client/strings/en-us.json b/client/strings/en-us.json
index 8d5f5f0f..995eb84b 100644
--- a/client/strings/en-us.json
+++ b/client/strings/en-us.json
@@ -41,6 +41,7 @@
"ButtonOpenManager": "Open Manager",
"ButtonPlay": "Play",
"ButtonPlaying": "Playing",
+ "ButtonPlaylists": "Playlists",
"ButtonPurgeAllCache": "Purge All Cache",
"ButtonPurgeItemsCache": "Purge Items Cache",
"ButtonPurgeMediaProgress": "Purge Media Progress",
diff --git a/server/controllers/LibraryController.js b/server/controllers/LibraryController.js
index b724ed0c..aec012d7 100644
--- a/server/controllers/LibraryController.js
+++ b/server/controllers/LibraryController.js
@@ -422,6 +422,26 @@ class LibraryController {
res.json(payload)
}
+ // api/libraries/:id/playlists
+ async getUserPlaylistsForLibrary(req, res) {
+ let playlistsForUser = this.db.playlists.filter(p => p.userId === req.user.id && p.libraryId === req.library.id).map(p => p.toJSONExpanded(this.db.libraryItems))
+
+ const payload = {
+ results: [],
+ total: playlistsForUser.length,
+ limit: req.query.limit && !isNaN(req.query.limit) ? Number(req.query.limit) : 0,
+ page: req.query.page && !isNaN(req.query.page) ? Number(req.query.page) : 0
+ }
+
+ if (payload.limit) {
+ const startIndex = payload.page * payload.limit
+ playlistsForUser = playlistsForUser.slice(startIndex, startIndex + payload.limit)
+ }
+
+ payload.results = playlistsForUser
+ res.json(payload)
+ }
+
async getLibraryFilterData(req, res) {
res.json(libraryHelpers.getDistinctFilterDataNew(req.libraryItems))
}
diff --git a/server/routers/ApiRouter.js b/server/routers/ApiRouter.js
index f67bb768..df32468a 100644
--- a/server/routers/ApiRouter.js
+++ b/server/routers/ApiRouter.js
@@ -72,6 +72,7 @@ class ApiRouter {
this.router.delete('/libraries/:id/issues', LibraryController.middleware.bind(this), LibraryController.removeLibraryItemsWithIssues.bind(this))
this.router.get('/libraries/:id/series', LibraryController.middleware.bind(this), LibraryController.getAllSeriesForLibrary.bind(this))
this.router.get('/libraries/:id/collections', LibraryController.middleware.bind(this), LibraryController.getCollectionsForLibrary.bind(this))
+ this.router.get('/libraries/:id/playlists', LibraryController.middleware.bind(this), LibraryController.getUserPlaylistsForLibrary.bind(this))
this.router.get('/libraries/:id/personalized', LibraryController.middleware.bind(this), LibraryController.getLibraryUserPersonalizedOptimal.bind(this))
this.router.get('/libraries/:id/filterdata', LibraryController.middleware.bind(this), LibraryController.getLibraryFilterData.bind(this))
this.router.get('/libraries/:id/search', LibraryController.middleware.bind(this), LibraryController.search.bind(this))