mirror of
https://github.com/advplyr/audiobookshelf-app.git
synced 2025-07-13 15:34:50 +02:00
Add:Playlists management
This commit is contained in:
parent
b62ce27487
commit
ab9f7fed64
12 changed files with 376 additions and 64 deletions
|
@ -319,9 +319,6 @@ export default {
|
||||||
userIsRoot() {
|
userIsRoot() {
|
||||||
return this.store.getters['user/getIsRoot']
|
return this.store.getters['user/getIsRoot']
|
||||||
},
|
},
|
||||||
_socket() {
|
|
||||||
return this.$root.socket || this.$nuxt.$root.socket
|
|
||||||
},
|
|
||||||
titleFontSize() {
|
titleFontSize() {
|
||||||
return 0.75 * this.sizeMultiplier
|
return 0.75 * this.sizeMultiplier
|
||||||
},
|
},
|
||||||
|
|
|
@ -278,9 +278,6 @@ export default {
|
||||||
userIsRoot() {
|
userIsRoot() {
|
||||||
return this.store.getters['user/getIsRoot']
|
return this.store.getters['user/getIsRoot']
|
||||||
},
|
},
|
||||||
_socket() {
|
|
||||||
return this.$root.socket || this.$nuxt.$root.socket
|
|
||||||
},
|
|
||||||
titleFontSize() {
|
titleFontSize() {
|
||||||
return 0.75 * this.sizeMultiplier
|
return 0.75 * this.sizeMultiplier
|
||||||
},
|
},
|
||||||
|
|
|
@ -22,9 +22,13 @@ export default {
|
||||||
currentLibraryIcon() {
|
currentLibraryIcon() {
|
||||||
return this.currentLibrary ? this.currentLibrary.icon : 'database'
|
return this.currentLibrary ? this.currentLibrary.icon : 'database'
|
||||||
},
|
},
|
||||||
|
userHasPlaylists() {
|
||||||
|
return this.$store.state.libraries.numUserPlaylists
|
||||||
|
},
|
||||||
items() {
|
items() {
|
||||||
|
let items = []
|
||||||
if (this.isPodcast) {
|
if (this.isPodcast) {
|
||||||
return [
|
items = [
|
||||||
{
|
{
|
||||||
to: '/bookshelf',
|
to: '/bookshelf',
|
||||||
routeName: 'bookshelf',
|
routeName: 'bookshelf',
|
||||||
|
@ -50,56 +54,62 @@ export default {
|
||||||
text: 'Library'
|
text: 'Library'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
} else {
|
||||||
|
items = [
|
||||||
|
{
|
||||||
|
to: '/bookshelf',
|
||||||
|
routeName: 'bookshelf',
|
||||||
|
iconPack: 'abs-icons',
|
||||||
|
icon: 'home',
|
||||||
|
iconClass: 'text-xl',
|
||||||
|
text: 'Home'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
to: '/bookshelf/library',
|
||||||
|
routeName: 'bookshelf-library',
|
||||||
|
iconPack: 'abs-icons',
|
||||||
|
icon: this.currentLibraryIcon,
|
||||||
|
iconClass: 'text-lg',
|
||||||
|
text: 'Library'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
to: '/bookshelf/series',
|
||||||
|
routeName: 'bookshelf-series',
|
||||||
|
iconPack: 'abs-icons',
|
||||||
|
icon: 'columns',
|
||||||
|
iconClass: 'text-lg pt-px',
|
||||||
|
text: 'Series'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
to: '/bookshelf/collections',
|
||||||
|
routeName: 'bookshelf-collections',
|
||||||
|
iconPack: 'material-icons-outlined',
|
||||||
|
icon: 'collections_bookmark',
|
||||||
|
iconClass: 'text-xl',
|
||||||
|
text: 'Collections'
|
||||||
|
}
|
||||||
|
// {
|
||||||
|
// to: '/bookshelf/authors',
|
||||||
|
// routeName: 'bookshelf-authors',
|
||||||
|
// iconPack: 'abs-icons',
|
||||||
|
// icon: 'authors',
|
||||||
|
// iconClass: 'text-2xl pb-px',
|
||||||
|
// text: 'Authors'
|
||||||
|
// }
|
||||||
|
]
|
||||||
}
|
}
|
||||||
return [
|
|
||||||
{
|
if (this.userHasPlaylists) {
|
||||||
to: '/bookshelf',
|
items.push({
|
||||||
routeName: 'bookshelf',
|
|
||||||
iconPack: 'abs-icons',
|
|
||||||
icon: 'home',
|
|
||||||
iconClass: 'text-xl',
|
|
||||||
text: 'Home'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
to: '/bookshelf/library',
|
|
||||||
routeName: 'bookshelf-library',
|
|
||||||
iconPack: 'abs-icons',
|
|
||||||
icon: this.currentLibraryIcon,
|
|
||||||
iconClass: 'text-lg',
|
|
||||||
text: 'Library'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
to: '/bookshelf/series',
|
|
||||||
routeName: 'bookshelf-series',
|
|
||||||
iconPack: 'abs-icons',
|
|
||||||
icon: 'columns',
|
|
||||||
iconClass: 'text-lg pt-px',
|
|
||||||
text: 'Series'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
to: '/bookshelf/collections',
|
|
||||||
routeName: 'bookshelf-collections',
|
|
||||||
iconPack: 'material-icons-outlined',
|
|
||||||
icon: 'collections_bookmark',
|
|
||||||
iconClass: 'text-xl',
|
|
||||||
text: 'Collections'
|
|
||||||
},
|
|
||||||
// {
|
|
||||||
// to: '/bookshelf/authors',
|
|
||||||
// routeName: 'bookshelf-authors',
|
|
||||||
// iconPack: 'abs-icons',
|
|
||||||
// icon: 'authors',
|
|
||||||
// iconClass: 'text-2xl pb-px',
|
|
||||||
// text: 'Authors'
|
|
||||||
// },
|
|
||||||
{
|
|
||||||
to: '/bookshelf/playlists',
|
to: '/bookshelf/playlists',
|
||||||
routeName: 'bookshelf-playlists',
|
routeName: 'bookshelf-playlists',
|
||||||
iconPack: 'material-icons',
|
iconPack: 'material-icons',
|
||||||
icon: 'queue_music',
|
icon: 'queue_music',
|
||||||
text: 'Playlists'
|
text: 'Playlists'
|
||||||
}
|
})
|
||||||
]
|
}
|
||||||
|
|
||||||
|
return items
|
||||||
},
|
},
|
||||||
routeName() {
|
routeName() {
|
||||||
return this.$route.name
|
return this.$route.name
|
||||||
|
|
225
components/modals/playlists/AddCreateModal.vue
Normal file
225
components/modals/playlists/AddCreateModal.vue
Normal file
|
@ -0,0 +1,225 @@
|
||||||
|
<template>
|
||||||
|
<modals-modal v-model="show" :width="360" height="100%" :processing="processing">
|
||||||
|
<template #outer>
|
||||||
|
<div class="absolute top-5 left-4 z-40">
|
||||||
|
<p class="text-white text-2xl truncate">Add to Playlist</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div class="w-full h-full overflow-hidden absolute top-0 left-0 flex items-center justify-center" @click="show = false">
|
||||||
|
<div ref="container" class="w-full rounded-lg bg-primary border border-white border-opacity-20 overflow-y-auto overflow-x-hidden" style="max-height: 80vh" @click.stop.prevent>
|
||||||
|
<div class="w-full h-full p-4" v-show="showPlaylistNameInput">
|
||||||
|
<div class="flex mb-4 items-center">
|
||||||
|
<div class="w-9 h-9 flex items-center justify-center rounded-full hover:bg-white hover:bg-opacity-10 cursor-pointer" @click.stop="showPlaylistNameInput = false">
|
||||||
|
<span class="material-icons text-3xl">arrow_back</span>
|
||||||
|
</div>
|
||||||
|
<p class="text-xl pl-2">New Playlist</p>
|
||||||
|
<div class="flex-grow" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ui-text-input-with-label v-model="newPlaylistName" label="Name" />
|
||||||
|
<div class="flex justify-end mt-6">
|
||||||
|
<ui-btn color="success" :loading="processing" class="w-full" @click.stop="submitCreatePlaylist">Create</ui-btn>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="w-full h-full" v-show="!showPlaylistNameInput">
|
||||||
|
<template v-for="playlist in sortedPlaylists">
|
||||||
|
<modals-playlists-playlist-row :key="playlist.id" :in-playlist="playlist.isItemIncluded" :playlist="playlist" @click="clickPlaylist" />
|
||||||
|
</template>
|
||||||
|
<div v-if="!playlists.length" class="flex h-32 items-center justify-center">
|
||||||
|
<p class="text-xl">{{ loading ? 'Loading..' : 'No Playlists' }}</p>
|
||||||
|
</div>
|
||||||
|
<ui-btn :loading="processing" color="success" class="w-full flex items-center justify-center" @click.stop="createPlaylist">
|
||||||
|
<span class="material-icons text-xl">add</span>
|
||||||
|
<p class="text-base pl-2">New Playlist</p>
|
||||||
|
</ui-btn>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</modals-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
libraryItemId: String
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
showPlaylistNameInput: false,
|
||||||
|
newPlaylistName: '',
|
||||||
|
playlists: [],
|
||||||
|
processing: false,
|
||||||
|
loading: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
show(newVal) {
|
||||||
|
if (newVal) {
|
||||||
|
this.setListeners()
|
||||||
|
this.showPlaylistNameInput = false
|
||||||
|
this.newPlaylistName = ''
|
||||||
|
this.loadPlaylists()
|
||||||
|
} else {
|
||||||
|
this.unsetListeners()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
show: {
|
||||||
|
get() {
|
||||||
|
return this.$store.state.globals.showPlaylistsAddCreateModal
|
||||||
|
},
|
||||||
|
set(val) {
|
||||||
|
this.$store.commit('globals/setShowPlaylistsAddCreateModal', val)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
currentLibraryId() {
|
||||||
|
return this.$store.state.libraries.currentLibraryId
|
||||||
|
},
|
||||||
|
selectedPlaylistItems() {
|
||||||
|
return this.$store.state.globals.selectedPlaylistItems || []
|
||||||
|
},
|
||||||
|
sortedPlaylists() {
|
||||||
|
return this.playlists
|
||||||
|
.map((playlist) => {
|
||||||
|
const includesItem = !this.selectedPlaylistItems.some((item) => !this.checkIsItemInPlaylist(playlist, item))
|
||||||
|
|
||||||
|
return {
|
||||||
|
isItemIncluded: includesItem,
|
||||||
|
...playlist
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.sort((a, b) => (a.isItemIncluded ? -1 : 1))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
checkIsItemInPlaylist(playlist, item) {
|
||||||
|
if (item.episode) {
|
||||||
|
return playlist.items.some((i) => i.libraryItemId === item.libraryItem.id && i.episodeId === item.episode.id)
|
||||||
|
}
|
||||||
|
return playlist.items.some((i) => i.libraryItemId === item.libraryItem.id)
|
||||||
|
},
|
||||||
|
loadPlaylists() {
|
||||||
|
this.loading = true
|
||||||
|
this.$axios
|
||||||
|
.$get(`/api/libraries/${this.currentLibraryId}/playlists`)
|
||||||
|
.then((data) => {
|
||||||
|
this.playlists = data.results || []
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('Failed', error)
|
||||||
|
this.$toast.error('Failed to load playlists')
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
clickPlaylist(playlist) {
|
||||||
|
if (playlist.isItemIncluded) {
|
||||||
|
this.removeFromPlaylist(playlist)
|
||||||
|
} else {
|
||||||
|
this.addToPlaylist(playlist)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
removeFromPlaylist(playlist) {
|
||||||
|
if (!this.selectedPlaylistItems.length) return
|
||||||
|
this.processing = true
|
||||||
|
|
||||||
|
const itemObjects = this.selectedPlaylistItems.map((pi) => ({ libraryItemId: pi.libraryItem.id, episodeId: pi.episode ? pi.episode.id : null }))
|
||||||
|
this.$axios
|
||||||
|
.$post(`/api/playlists/${playlist.id}/batch/remove`, { items: itemObjects })
|
||||||
|
.then((updatedPlaylist) => {
|
||||||
|
console.log(`Items removed from playlist`, updatedPlaylist)
|
||||||
|
this.$toast.success('Playlist item(s) removed')
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('Failed to remove items from playlist', error)
|
||||||
|
this.$toast.error('Failed to remove playlist item(s)')
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.processing = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
addToPlaylist(playlist) {
|
||||||
|
if (!this.selectedPlaylistItems.length) return
|
||||||
|
this.processing = true
|
||||||
|
|
||||||
|
const itemObjects = this.selectedPlaylistItems.map((pi) => ({ libraryItemId: pi.libraryItem.id, episodeId: pi.episode ? pi.episode.id : null }))
|
||||||
|
this.$axios
|
||||||
|
.$post(`/api/playlists/${playlist.id}/batch/add`, { items: itemObjects })
|
||||||
|
.then((updatedPlaylist) => {
|
||||||
|
console.log(`Items added to playlist`, updatedPlaylist)
|
||||||
|
this.$toast.success('Items added to playlist')
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('Failed to add items to playlist', error)
|
||||||
|
this.$toast.error('Failed to add items to playlist')
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.processing = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
createPlaylist() {
|
||||||
|
this.newPlaylistName = ''
|
||||||
|
this.showPlaylistNameInput = true
|
||||||
|
},
|
||||||
|
submitCreatePlaylist() {
|
||||||
|
if (!this.newPlaylistName || !this.selectedPlaylistItems.length) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.processing = true
|
||||||
|
|
||||||
|
const itemObjects = this.selectedPlaylistItems.map((pi) => ({ libraryItemId: pi.libraryItem.id, episodeId: pi.episode ? pi.episode.id : null }))
|
||||||
|
const newPlaylist = {
|
||||||
|
items: itemObjects,
|
||||||
|
libraryId: this.currentLibraryId,
|
||||||
|
name: this.newPlaylistName
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$axios
|
||||||
|
.$post('/api/playlists', newPlaylist)
|
||||||
|
.then((data) => {
|
||||||
|
console.log('New playlist created', data)
|
||||||
|
this.$toast.success(`Playlist "${data.name}" created`)
|
||||||
|
this.newPlaylistName = ''
|
||||||
|
this.showPlaylistNameInput = false
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('Failed to create playlist', error)
|
||||||
|
var errMsg = error.response ? error.response.data || '' : ''
|
||||||
|
this.$toast.error(`Failed to create playlist: ${errMsg}`)
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.processing = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
playlistAdded(playlist) {
|
||||||
|
if (!this.playlists.some((p) => p.id === playlist.id)) {
|
||||||
|
this.playlists.push(playlist)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
playlistUpdated(playlist) {
|
||||||
|
const index = this.playlists.findIndex((p) => p.id === playlist.id)
|
||||||
|
if (index >= 0) {
|
||||||
|
this.playlists.splice(index, 1, playlist)
|
||||||
|
} else {
|
||||||
|
this.playlists.push(playlist)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
playlistRemoved(playlist) {
|
||||||
|
this.playlists = this.playlists.filter((p) => p.id !== playlist.id)
|
||||||
|
},
|
||||||
|
setListeners() {
|
||||||
|
this.$socket.$on('playlist_added', this.playlistAdded)
|
||||||
|
this.$socket.$on('playlist_updated', this.playlistUpdated)
|
||||||
|
this.$socket.$on('playlist_removed', this.playlistRemoved)
|
||||||
|
},
|
||||||
|
unsetListeners() {
|
||||||
|
this.$socket.$off('playlist_added', this.playlistAdded)
|
||||||
|
this.$socket.$off('playlist_updated', this.playlistUpdated)
|
||||||
|
this.$socket.$off('playlist_removed', this.playlistRemoved)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {}
|
||||||
|
}
|
||||||
|
</script>
|
42
components/modals/playlists/PlaylistRow.vue
Normal file
42
components/modals/playlists/PlaylistRow.vue
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
<template>
|
||||||
|
<div :key="playlist.id" :id="`playlist-row-${playlist.id}`" class="flex items-center px-3 py-2 justify-start cursor-pointer relative" :class="inPlaylist ? 'bg-bg bg-opacity-60' : 'bg-opacity-20'">
|
||||||
|
<div v-if="inPlaylist" class="absolute top-0 left-0 h-full w-1 bg-success z-10" />
|
||||||
|
<div class="w-16 max-w-16 text-center">
|
||||||
|
<covers-playlist-cover :items="items" :width="52" :height="52" />
|
||||||
|
</div>
|
||||||
|
<div class="flex-grow overflow-hidden">
|
||||||
|
<p class="pl-1 pr-2 truncate text-sm">{{ playlist.name }}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="absolute top-0 right-0 h-full flex items-center px-3" @click.stop="click">
|
||||||
|
<span v-if="inPlaylist" class="material-icons text-error">remove</span>
|
||||||
|
<span v-else class="material-icons text-success">add</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
playlist: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
|
},
|
||||||
|
inPlaylist: Boolean
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
items() {
|
||||||
|
return this.playlist.items || []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
click() {
|
||||||
|
this.$emit('click', this.playlist)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -26,6 +26,10 @@
|
||||||
|
|
||||||
<ui-read-icon-btn :disabled="isProcessingReadUpdate" :is-read="userIsFinished" borderless class="mx-1 mt-0.5" @click="toggleFinished" />
|
<ui-read-icon-btn :disabled="isProcessingReadUpdate" :is-read="userIsFinished" borderless class="mx-1 mt-0.5" @click="toggleFinished" />
|
||||||
|
|
||||||
|
<button v-if="!isLocal" class="mx-1.5 mt-1.5" @click="addToPlaylist">
|
||||||
|
<span class="material-icons text-2xl">playlist_add</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
<div v-if="userCanDownload">
|
<div v-if="userCanDownload">
|
||||||
<span v-if="isLocal" class="material-icons-outlined px-2 text-success text-lg">audio_file</span>
|
<span v-if="isLocal" class="material-icons-outlined px-2 text-success text-lg">audio_file</span>
|
||||||
<span v-else-if="!localEpisode" class="material-icons mx-1 mt-2" :class="downloadItem ? 'animate-bounce text-warning text-opacity-75 text-xl' : 'text-gray-300 text-xl'" @click="downloadClick">{{ downloadItem ? 'downloading' : 'download' }}</span>
|
<span v-else-if="!localEpisode" class="material-icons mx-1 mt-2" :class="downloadItem ? 'animate-bounce text-warning text-opacity-75 text-xl' : 'text-gray-300 text-xl'" @click="downloadClick">{{ downloadItem ? 'downloading' : 'download' }}</span>
|
||||||
|
@ -134,6 +138,9 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
addToPlaylist() {
|
||||||
|
this.$emit('addToPlaylist', this.episode)
|
||||||
|
},
|
||||||
async selectFolder() {
|
async selectFolder() {
|
||||||
var folderObj = await AbsFileSystem.selectFolder({ mediaType: this.mediaType })
|
var folderObj = await AbsFileSystem.selectFolder({ mediaType: this.mediaType })
|
||||||
if (folderObj.error) {
|
if (folderObj.error) {
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<template v-for="episode in episodesSorted">
|
<template v-for="episode in episodesSorted">
|
||||||
<tables-podcast-episode-row :episode="episode" :local-episode="localEpisodeMap[episode.id]" :library-item-id="libraryItemId" :local-library-item-id="localLibraryItemId" :is-local="isLocal" :key="episode.id" />
|
<tables-podcast-episode-row :episode="episode" :local-episode="localEpisodeMap[episode.id]" :library-item-id="libraryItemId" :local-library-item-id="localLibraryItemId" :is-local="isLocal" :key="episode.id" @addToPlaylist="addEpisodeToPlaylist" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- What in tarnation is going on here?
|
<!-- What in tarnation is going on here?
|
||||||
|
@ -29,7 +29,10 @@
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
libraryItemId: String,
|
libraryItem: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
|
},
|
||||||
episodes: {
|
episodes: {
|
||||||
type: Array,
|
type: Array,
|
||||||
default: () => []
|
default: () => []
|
||||||
|
@ -95,6 +98,9 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
libraryItemId() {
|
||||||
|
return this.libraryItem ? this.libraryItem.id : null
|
||||||
|
},
|
||||||
episodesAreFiltered() {
|
episodesAreFiltered() {
|
||||||
return this.episodesFiltered.length !== this.episodesCopy.length
|
return this.episodesFiltered.length !== this.episodesCopy.length
|
||||||
},
|
},
|
||||||
|
@ -140,6 +146,10 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
addEpisodeToPlaylist(episode) {
|
||||||
|
this.$store.commit('globals/setSelectedPlaylistItems', [{ libraryItem: this.libraryItem, episode }])
|
||||||
|
this.$store.commit('globals/setShowPlaylistsAddCreateModal', true)
|
||||||
|
},
|
||||||
setFilter(filter) {
|
setFilter(filter) {
|
||||||
this.filterKey = filter
|
this.filterKey = filter
|
||||||
console.log('Set filter', this.filterKey)
|
console.log('Set filter', this.filterKey)
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
</div>
|
</div>
|
||||||
<app-audio-player-container ref="streamContainer" />
|
<app-audio-player-container ref="streamContainer" />
|
||||||
<modals-libraries-modal />
|
<modals-libraries-modal />
|
||||||
|
<modals-playlists-add-create-modal />
|
||||||
<app-side-drawer />
|
<app-side-drawer />
|
||||||
<readers-reader />
|
<readers-reader />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -106,7 +106,7 @@
|
||||||
<p class="text-sm">{{ description }}</p>
|
<p class="text-sm">{{ description }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<tables-podcast-episodes-table v-if="isPodcast" :library-item-id="libraryItemId" :local-library-item-id="localLibraryItemId" :episodes="episodes" :local-episodes="localLibraryItemEpisodes" :is-local="isLocal" />
|
<tables-podcast-episodes-table v-if="isPodcast" :library-item="libraryItem" :local-library-item-id="localLibraryItemId" :episodes="episodes" :local-episodes="localLibraryItemEpisodes" :is-local="isLocal" />
|
||||||
|
|
||||||
<modals-select-local-folder-modal v-model="showSelectLocalFolder" :media-type="mediaType" @select="selectedLocalFolder" />
|
<modals-select-local-folder-modal v-model="showSelectLocalFolder" :media-type="mediaType" @select="selectedLocalFolder" />
|
||||||
|
|
||||||
|
@ -343,12 +343,21 @@ export default {
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
} else {
|
} else {
|
||||||
return [
|
const items = [
|
||||||
{
|
{
|
||||||
text: 'View Details',
|
text: 'View Details',
|
||||||
value: 'details'
|
value: 'details'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
if (!this.isPodcast) {
|
||||||
|
items.push({
|
||||||
|
text: 'Add to Playlist',
|
||||||
|
value: 'playlist'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return items
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -359,6 +368,9 @@ export default {
|
||||||
this.$router.push(`/localMedia/item/${this.libraryItemId}`)
|
this.$router.push(`/localMedia/item/${this.libraryItemId}`)
|
||||||
} else if (action === 'details') {
|
} else if (action === 'details') {
|
||||||
this.showDetailsModal = true
|
this.showDetailsModal = true
|
||||||
|
} else if (action === 'playlist') {
|
||||||
|
this.$store.commit('globals/setSelectedPlaylistItems', [{ libraryItem: this.libraryItem, episode: null }])
|
||||||
|
this.$store.commit('globals/setShowPlaylistsAddCreateModal', true)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
moreButtonPress() {
|
moreButtonPress() {
|
||||||
|
|
|
@ -86,7 +86,7 @@ export default {
|
||||||
return !prog || !prog.isFinished
|
return !prog || !prog.isFinished
|
||||||
})
|
})
|
||||||
if (nextItem) {
|
if (nextItem) {
|
||||||
this.$eventBus.$emit('play-item', { libraryItemId: nextItem.libraryItemId })
|
this.$eventBus.$emit('play-item', { libraryItemId: nextItem.libraryItemId, episodeId: nextItem.episodeId })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -33,7 +33,9 @@ export const state = () => ({
|
||||||
value: 30
|
value: 30
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
libraryIcons: ['database', 'audiobookshelf', 'books-1', 'books-2', 'book-1', 'microphone-1', 'microphone-3', 'radio', 'podcast', 'rss', 'headphones', 'music', 'file-picture', 'rocket', 'power', 'star', 'heart']
|
libraryIcons: ['database', 'audiobookshelf', 'books-1', 'books-2', 'book-1', 'microphone-1', 'microphone-3', 'radio', 'podcast', 'rss', 'headphones', 'music', 'file-picture', 'rocket', 'power', 'star', 'heart'],
|
||||||
|
selectedPlaylistItems: [],
|
||||||
|
showPlaylistsAddCreateModal: false
|
||||||
})
|
})
|
||||||
|
|
||||||
export const getters = {
|
export const getters = {
|
||||||
|
@ -89,7 +91,6 @@ export const getters = {
|
||||||
export const actions = {
|
export const actions = {
|
||||||
async loadLocalMediaProgress({ state, commit }) {
|
async loadLocalMediaProgress({ state, commit }) {
|
||||||
var mediaProgress = await this.$db.getAllLocalMediaProgress()
|
var mediaProgress = await this.$db.getAllLocalMediaProgress()
|
||||||
console.log('Got all local media progress', JSON.stringify(mediaProgress))
|
|
||||||
commit('setLocalMediaProgress', mediaProgress)
|
commit('setLocalMediaProgress', mediaProgress)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -124,10 +125,8 @@ export const mutations = {
|
||||||
}
|
}
|
||||||
var index = state.localMediaProgress.findIndex(lmp => lmp.id == prog.id)
|
var index = state.localMediaProgress.findIndex(lmp => lmp.id == prog.id)
|
||||||
if (index >= 0) {
|
if (index >= 0) {
|
||||||
console.log('UpdateLocalMediaProgress updating', prog.id, prog.progress)
|
|
||||||
state.localMediaProgress.splice(index, 1, prog)
|
state.localMediaProgress.splice(index, 1, prog)
|
||||||
} else {
|
} else {
|
||||||
console.log('updateLocalMediaProgress inserting new progress', prog.id, prog.progress)
|
|
||||||
state.localMediaProgress.push(prog)
|
state.localMediaProgress.push(prog)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -139,5 +138,11 @@ export const mutations = {
|
||||||
},
|
},
|
||||||
setLastSearch(state, val) {
|
setLastSearch(state, val) {
|
||||||
state.lastSearch = val
|
state.lastSearch = val
|
||||||
|
},
|
||||||
|
setSelectedPlaylistItems(state, items) {
|
||||||
|
state.selectedPlaylistItems = items
|
||||||
|
},
|
||||||
|
setShowPlaylistsAddCreateModal(state, val) {
|
||||||
|
state.showPlaylistsAddCreateModal = val
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -6,7 +6,8 @@ export const state = () => ({
|
||||||
currentLibraryId: '',
|
currentLibraryId: '',
|
||||||
showModal: false,
|
showModal: false,
|
||||||
issues: 0,
|
issues: 0,
|
||||||
filterData: null
|
filterData: null,
|
||||||
|
numUserPlaylists: 0
|
||||||
})
|
})
|
||||||
|
|
||||||
export const getters = {
|
export const getters = {
|
||||||
|
@ -41,15 +42,17 @@ export const actions = {
|
||||||
return this.$axios
|
return this.$axios
|
||||||
.$get(`/api/libraries/${libraryId}?include=filterdata`)
|
.$get(`/api/libraries/${libraryId}?include=filterdata`)
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
var library = data.library
|
const library = data.library
|
||||||
var filterData = data.filterdata
|
const filterData = data.filterdata
|
||||||
var issues = data.issues || 0
|
const issues = data.issues || 0
|
||||||
|
const numUserPlaylists = data.numUserPlaylists || 0
|
||||||
|
|
||||||
dispatch('user/checkUpdateLibrarySortFilter', library.mediaType, { root: true })
|
dispatch('user/checkUpdateLibrarySortFilter', library.mediaType, { root: true })
|
||||||
|
|
||||||
commit('addUpdate', library)
|
commit('addUpdate', library)
|
||||||
commit('setLibraryIssues', issues)
|
commit('setLibraryIssues', issues)
|
||||||
commit('setLibraryFilterData', filterData)
|
commit('setLibraryFilterData', filterData)
|
||||||
|
commit('setNumUserPlaylists', numUserPlaylists)
|
||||||
commit('setCurrentLibrary', libraryId)
|
commit('setCurrentLibrary', libraryId)
|
||||||
return data
|
return data
|
||||||
})
|
})
|
||||||
|
@ -128,6 +131,9 @@ export const mutations = {
|
||||||
setLibraryIssues(state, val) {
|
setLibraryIssues(state, val) {
|
||||||
state.issues = val
|
state.issues = val
|
||||||
},
|
},
|
||||||
|
setNumUserPlaylists(state, numUserPlaylists) {
|
||||||
|
state.numUserPlaylists = numUserPlaylists
|
||||||
|
},
|
||||||
setLibraryFilterData(state, filterData) {
|
setLibraryFilterData(state, filterData) {
|
||||||
state.filterData = filterData
|
state.filterData = filterData
|
||||||
},
|
},
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue