New data model play media entity, PlaybackSessionManager

This commit is contained in:
advplyr 2022-03-17 19:10:47 -05:00
parent 1cf9e85272
commit 099ae7c776
54 changed files with 841 additions and 902 deletions

View file

@ -175,8 +175,8 @@ export default {
})
},
batchDeleteClick() {
var audiobookText = this.numLibraryItemsSelected > 1 ? `these ${this.numLibraryItemsSelected} audiobooks` : 'this audiobook'
var confirmMsg = `Are you sure you want to remove ${audiobookText}?\n\n*Does not delete your files, only removes the audiobooks from AudioBookshelf`
var audiobookText = this.numLibraryItemsSelected > 1 ? `these ${this.numLibraryItemsSelected} items` : 'this item'
var confirmMsg = `Are you sure you want to remove ${audiobookText}?\n\n*Does not delete your files, only removes the items from Audiobookshelf`
if (confirm(confirmMsg)) {
this.processingBatchDelete = true
this.$store.commit('setProcessingBatch', true)

View file

@ -300,11 +300,11 @@ export default {
var firstBookPage = Math.floor(firstBookIndex / this.booksPerFetch)
var lastBookPage = Math.floor(lastBookIndex / this.booksPerFetch)
if (!this.pagesLoaded[firstBookPage]) {
console.log('Must load next batch', firstBookPage, 'book index', firstBookIndex)
// console.log('Must load next batch', firstBookPage, 'book index', firstBookIndex)
this.loadPage(firstBookPage)
}
if (!this.pagesLoaded[lastBookPage]) {
console.log('Must load last next batch', lastBookPage, 'book index', lastBookIndex)
// console.log('Must load last next batch', lastBookPage, 'book index', lastBookIndex)
this.loadPage(lastBookPage)
}

View file

@ -95,16 +95,17 @@ export default {
user() {
return this.$store.state.user.user
},
userAudiobook() {
userLibraryItemProgress() {
if (!this.libraryItemId) return
return this.$store.getters['user/getUserLibraryItemProgress'](this.libraryItemId)
},
userAudiobookCurrentTime() {
return this.userAudiobook ? this.userAudiobook.currentTime || 0 : 0
userItemCurrentTime() {
return this.userLibraryItemProgress ? this.userLibraryItemProgress.currentTime || 0 : 0
},
bookmarks() {
if (!this.userAudiobook) return []
return (this.userAudiobook.bookmarks || []).map((bm) => ({ ...bm })).sort((a, b) => a.time - b.time)
return []
// if (!this.userAudiobook) return []
// return (this.userAudiobook.bookmarks || []).map((bm) => ({ ...bm })).sort((a, b) => a.time - b.time)
},
streamLibraryItem() {
return this.$store.state.streamLibraryItem
@ -236,9 +237,9 @@ export default {
console.error('No Audio Ref')
}
},
streamOpen(stream) {
this.$store.commit('setLibraryItemStream', stream.libraryItem)
this.playerHandler.prepareStream(stream)
sessionOpen(session) {
this.$store.commit('setLibraryItemStream', session.libraryItem)
this.playerHandler.prepareOpenSession(session)
},
streamClosed(streamId) {
// Stream was closed from the server
@ -282,7 +283,7 @@ export default {
if (!libraryItem) return
this.$store.commit('setLibraryItemStream', libraryItem)
this.playerHandler.load(libraryItem, true, this.userAudiobookCurrentTime)
this.playerHandler.load(libraryItem, true)
}
},
mounted() {

View file

@ -161,10 +161,12 @@ export default {
return this._libraryItem.libraryId
},
hasEbook() {
return this.media.numEbooks
if (!this.media.ebooks) return 0
return this.media.ebooks.length
},
hasTracks() {
return this.media.numTracks
hasAudiobook() {
if (!this.media.audiobooks) return 0
return this.media.audiobooks.length
},
processingBatch() {
return this.store.state.processingBatch
@ -244,7 +246,7 @@ export default {
return !this.isSelectionMode && this.showExperimentalFeatures && !this.showPlayButton && this.hasEbook
},
showPlayButton() {
return !this.isSelectionMode && !this.isMissing && !this.isInvalid && this.hasTracks && !this.isStreaming
return !this.isSelectionMode && !this.isMissing && !this.isInvalid && this.hasAudiobook && !this.isStreaming
},
showSmallEBookIcon() {
return !this.isSelectionMode && this.showExperimentalFeatures && this.hasEbook
@ -310,7 +312,7 @@ export default {
}
]
if (this.userCanUpdate) {
if (this.hasTracks) {
if (this.hasAudiobook) {
items.push({
func: 'showEditModalTracks',
text: 'Tracks'

View file

@ -175,7 +175,7 @@ export default {
return this.filterData.languages || []
},
progress() {
return ['Read', 'Unread', 'In Progress']
return ['Finished', 'In Progress', 'Not Started']
},
sublistItems() {
return (this[this.sublist] || []).map((item) => {

View file

@ -168,7 +168,6 @@ export default {
},
async updateDetails(updatedDetails) {
this.isProcessing = true
console.log('Sending update', updatedDetails.updatePayload)
var updateResult = await this.$axios.$patch(`/api/items/${this.libraryItemId}/media`, updatedDetails.updatePayload).catch((error) => {
console.error('Failed to update', error)
return false

View file

@ -64,7 +64,6 @@ export default {
computed: {
_directories() {
return this.directories.map((d) => {
console.log('Directories', d)
var isUsed = !!this.paths.find((path) => path.endsWith(d.path))
var isSelected = d.path === this.selectedPath
var classes = []

View file

@ -1,105 +0,0 @@
<template>
<div class="w-full my-2">
<div class="w-full bg-primary px-4 py-2 flex items-center cursor-pointer">
<p class="pr-4">All Files</p>
<span class="bg-black-400 rounded-xl py-1 px-2 text-sm font-mono">{{ allFiles.length }}</span>
<div class="flex-grow" />
<ui-btn small :color="showFullPath ? 'gray-600' : 'primary'" @click.stop="showFullPath = !showFullPath">Full Path</ui-btn>
</div>
<div class="w-full">
<table class="text-sm tracksTable">
<tr class="font-book">
<th class="text-left px-4">Path</th>
<th class="text-left px-4 w-24">Filetype</th>
<th v-if="userCanDownload" class="text-center w-20">Download</th>
</tr>
<template v-for="file in allFiles">
<tr :key="file.path">
<td class="font-book pl-2">
{{ showFullPath ? file.fullPath : file.path }}
</td>
<td class="text-xs">
<p>{{ file.filetype }}</p>
</td>
<td v-if="userCanDownload" class="text-center">
<a :href="`/s/book/${audiobookId}/${file.relativePath}?token=${userToken}`" download><span class="material-icons icon-text">download</span></a>
</td>
</tr>
</template>
</table>
</div>
</div>
</template>
<script>
export default {
props: {
audiobook: {
type: Object,
default: () => {}
}
},
data() {
return {
showFullPath: false
}
},
computed: {
audiobookId() {
return this.audiobook.id
},
audiobookPath() {
return this.audiobook.path
},
userCanDownload() {
return this.$store.getters['user/getUserCanDownload']
},
userToken() {
return this.$store.getters['user/getToken']
},
isMissing() {
return this.audiobook.isMissing
},
showDownload() {
return this.userCanDownload && !this.isMissing
},
otherFiles() {
return this.audiobook.otherFiles || []
},
audioFiles() {
return this.audiobook.audioFiles || []
},
audioFilesCleaned() {
return this.audioFiles.map((af) => {
return {
path: af.path,
fullPath: af.fullPath,
relativePath: this.getRelativePath(af.path),
filetype: 'audio'
}
})
},
otherFilesCleaned() {
return this.otherFiles.map((af) => {
return {
path: af.path,
fullPath: af.fullPath,
relativePath: this.getRelativePath(af.path),
filetype: af.filetype
}
})
},
allFiles() {
return this.audioFilesCleaned.concat(this.otherFilesCleaned)
}
},
methods: {
getRelativePath(path) {
var relativePath = path.replace(/\\/g, '/').replace(this.audiobookPath.replace(/\\/g, '/') + '/', '')
return this.$encodeUriPath(relativePath)
}
},
mounted() {}
}
</script>

View file

@ -26,11 +26,11 @@
</td>
<td class="text-sm">{{ user.type }}</td>
<td class="hidden lg:table-cell">
<div v-if="usersOnline[user.id] && usersOnline[user.id].stream && usersOnline[user.id].stream.libraryItem && usersOnline[user.id].stream.libraryItem.media">
<p class="truncate text-xs">Reading: {{ usersOnline[user.id].stream.libraryItem.media.metadata.title || '' }}</p>
<div v-if="usersOnline[user.id] && usersOnline[user.id].session && usersOnline[user.id].session.libraryItem && usersOnline[user.id].session.libraryItem.media">
<p class="truncate text-xs">Listening: {{ usersOnline[user.id].session.libraryItem.media.metadata.title || '' }}</p>
</div>
<div v-else-if="user.audiobooks && getLastRead(user.audiobooks)">
<p class="truncate text-xs">Last: {{ getLastRead(user.audiobooks) }}</p>
<div v-else-if="user.mostRecent">
<p class="truncate text-xs">Last: {{ user.mostRecent.metadata.title }}</p>
</div>
</td>
<td class="text-xs font-mono hidden sm:table-cell">
@ -78,23 +78,11 @@ export default {
},
usersOnline() {
var usermap = {}
this.$store.state.users.users.forEach((u) => (usermap[u.id] = { online: true, stream: u.stream }))
this.$store.state.users.users.forEach((u) => (usermap[u.id] = { online: true, session: u.session }))
return usermap
}
},
methods: {
getLastRead(audiobooks) {
var abs = Object.values(audiobooks).filter((ab) => {
return ab.progress > 0
})
if (abs.length) {
abs = abs.sort((a, b) => b.lastUpdate - a.lastUpdate)
// Book object is attached on request
if (abs[0].book) return abs[0].book.title
return abs[0].audiobookTitle ? abs[0].audiobookTitle : null
}
return null
},
deleteUserClick(user) {
if (this.isDeletingUser) return
if (confirm(`Are you sure you want to permanently delete user "${user.username}"?`)) {

View file

@ -33,7 +33,6 @@ export default {
var _files = Array.from(e.target.files)
if (_files && _files.length) {
var file = _files[0]
console.log('File', file)
this.$emit('change', file)
}
}

View file

@ -78,7 +78,7 @@ export default {
console.error('Failed to get search results', error)
return []
})
console.log('Search results', results)
// console.log('Search results', results)
this.items = results || []
this.searching = false
},

View file

@ -276,7 +276,7 @@ export default {
if (!matchingItem) return false
for (var key in item) {
if (item[key] !== matchingItem[key]) {
console.log('Object array item keys changed', key, item[key], matchingItem[key])
// console.log('Object array item keys changed', key, item[key], matchingItem[key])
return false
}
}