mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2026-06-02 00:40:39 +02:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b876256736 | |||
| 3ce6e45761 | |||
| 5ac6b85da1 | |||
| 69e0a0732a | |||
| 087835a9f3 | |||
| 1f7b181b7b | |||
| 1afb8840db |
@@ -74,19 +74,12 @@ export default {
|
|||||||
mediaTracks() {
|
mediaTracks() {
|
||||||
return this.media.tracks || []
|
return this.media.tracks || []
|
||||||
},
|
},
|
||||||
isSingleM4b() {
|
|
||||||
return this.mediaTracks.length === 1 && this.mediaTracks[0].metadata.ext.toLowerCase() === '.m4b'
|
|
||||||
},
|
|
||||||
chapters() {
|
chapters() {
|
||||||
return this.media.chapters || []
|
return this.media.chapters || []
|
||||||
},
|
},
|
||||||
showM4bDownload() {
|
showM4bDownload() {
|
||||||
if (!this.mediaTracks.length) return false
|
if (!this.mediaTracks.length) return false
|
||||||
return !this.isSingleM4b
|
return true
|
||||||
},
|
|
||||||
showMp3Split() {
|
|
||||||
if (!this.mediaTracks.length) return false
|
|
||||||
return this.isSingleM4b && this.chapters.length
|
|
||||||
},
|
},
|
||||||
queuedEmbedLIds() {
|
queuedEmbedLIds() {
|
||||||
return this.$store.state.tasks.queuedEmbedLIds || []
|
return this.$store.state.tasks.queuedEmbedLIds || []
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div id="lazy-episodes-table" class="w-full py-6">
|
<div id="lazy-episodes-table" class="w-full py-6">
|
||||||
<div class="flex flex-wrap flex-col md:flex-row md:items-center mb-4">
|
<div class="flex flex-wrap flex-col md:flex-row md:items-center mb-4">
|
||||||
@@ -176,6 +175,13 @@ export default {
|
|||||||
return episodeProgress && !episodeProgress.isFinished
|
return episodeProgress && !episodeProgress.isFinished
|
||||||
})
|
})
|
||||||
.sort((a, b) => {
|
.sort((a, b) => {
|
||||||
|
// Swap values if sort descending
|
||||||
|
if (this.sortDesc) {
|
||||||
|
const temp = a
|
||||||
|
a = b
|
||||||
|
b = temp
|
||||||
|
}
|
||||||
|
|
||||||
let aValue
|
let aValue
|
||||||
let bValue
|
let bValue
|
||||||
|
|
||||||
@@ -194,10 +200,23 @@ export default {
|
|||||||
if (!bValue) bValue = Number.MAX_VALUE
|
if (!bValue) bValue = Number.MAX_VALUE
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.sortDesc) {
|
const primaryCompare = String(aValue).localeCompare(String(bValue), undefined, { numeric: true, sensitivity: 'base' })
|
||||||
return String(bValue).localeCompare(String(aValue), undefined, { numeric: true, sensitivity: 'base' })
|
if (primaryCompare !== 0 || this.sortKey === 'publishedAt') return primaryCompare
|
||||||
|
|
||||||
|
// When sorting by season, secondary sort is by episode number
|
||||||
|
if (this.sortKey === 'season') {
|
||||||
|
const aEpisode = a.episode || ''
|
||||||
|
const bEpisode = b.episode || ''
|
||||||
|
|
||||||
|
const secondaryCompare = String(aEpisode).localeCompare(String(bEpisode), undefined, { numeric: true, sensitivity: 'base' })
|
||||||
|
if (secondaryCompare !== 0) return secondaryCompare
|
||||||
}
|
}
|
||||||
return String(aValue).localeCompare(String(bValue), undefined, { numeric: true, sensitivity: 'base' })
|
|
||||||
|
// Final sort by publishedAt
|
||||||
|
let aPubDate = a.publishedAt || Number.MAX_VALUE
|
||||||
|
let bPubDate = b.publishedAt || Number.MAX_VALUE
|
||||||
|
|
||||||
|
return String(aPubDate).localeCompare(String(bPubDate), undefined, { numeric: true, sensitivity: 'base' })
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
episodesList() {
|
episodesList() {
|
||||||
|
|||||||
@@ -2,7 +2,14 @@
|
|||||||
<div id="page-wrapper" class="bg-bg page p-8 overflow-auto relative" :class="streamLibraryItem ? 'streaming' : ''">
|
<div id="page-wrapper" class="bg-bg page p-8 overflow-auto relative" :class="streamLibraryItem ? 'streaming' : ''">
|
||||||
<div class="flex items-center justify-center mb-6">
|
<div class="flex items-center justify-center mb-6">
|
||||||
<div class="w-full max-w-2xl">
|
<div class="w-full max-w-2xl">
|
||||||
<p class="text-2xl mb-2">{{ $strings.HeaderAudiobookTools }}</p>
|
<div class="flex items-center mb-4">
|
||||||
|
<nuxt-link :to="`/item/${libraryItem.id}`" class="hover:underline">
|
||||||
|
<h1 class="text-lg lg:text-xl">{{ mediaMetadata.title }}</h1>
|
||||||
|
</nuxt-link>
|
||||||
|
<button class="w-7 h-7 flex items-center justify-center mx-4 hover:scale-110 duration-100 transform text-gray-200 hover:text-white" @click="editItem">
|
||||||
|
<span class="material-symbols text-base">edit</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full max-w-2xl">
|
<div class="w-full max-w-2xl">
|
||||||
<div class="flex justify-end">
|
<div class="flex justify-end">
|
||||||
@@ -13,7 +20,7 @@
|
|||||||
|
|
||||||
<div class="flex justify-center mb-2">
|
<div class="flex justify-center mb-2">
|
||||||
<div class="w-full max-w-2xl">
|
<div class="w-full max-w-2xl">
|
||||||
<p class="text-xl">{{ $strings.HeaderMetadataToEmbed }}</p>
|
<p class="text-lg">{{ $strings.HeaderMetadataToEmbed }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full max-w-2xl"></div>
|
<div class="w-full max-w-2xl"></div>
|
||||||
</div>
|
</div>
|
||||||
@@ -266,9 +273,6 @@ export default {
|
|||||||
audioFiles() {
|
audioFiles() {
|
||||||
return (this.media.audioFiles || []).filter((af) => !af.exclude)
|
return (this.media.audioFiles || []).filter((af) => !af.exclude)
|
||||||
},
|
},
|
||||||
isSingleM4b() {
|
|
||||||
return this.audioFiles.length === 1 && this.audioFiles[0].metadata.ext.toLowerCase() === '.m4b'
|
|
||||||
},
|
|
||||||
streamLibraryItem() {
|
streamLibraryItem() {
|
||||||
return this.$store.state.streamLibraryItem
|
return this.$store.state.streamLibraryItem
|
||||||
},
|
},
|
||||||
@@ -276,14 +280,10 @@ export default {
|
|||||||
return this.media.chapters || []
|
return this.media.chapters || []
|
||||||
},
|
},
|
||||||
availableTools() {
|
availableTools() {
|
||||||
if (this.isSingleM4b) {
|
return [
|
||||||
return [{ value: 'embed', text: this.$strings.LabelToolsEmbedMetadata }]
|
{ value: 'embed', text: this.$strings.LabelToolsEmbedMetadata },
|
||||||
} else {
|
{ value: 'm4b', text: this.$strings.LabelToolsM4bEncoder }
|
||||||
return [
|
]
|
||||||
{ value: 'embed', text: this.$strings.LabelToolsEmbedMetadata },
|
|
||||||
{ value: 'm4b', text: this.$strings.LabelToolsM4bEncoder }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
taskFailed() {
|
taskFailed() {
|
||||||
return this.isTaskFinished && this.task.isFailed
|
return this.isTaskFinished && this.task.isFailed
|
||||||
@@ -431,10 +431,24 @@ export default {
|
|||||||
},
|
},
|
||||||
taskUpdated(task) {
|
taskUpdated(task) {
|
||||||
this.processing = !task.isFinished
|
this.processing = !task.isFinished
|
||||||
|
},
|
||||||
|
editItem() {
|
||||||
|
this.$store.commit('showEditModal', this.libraryItem)
|
||||||
|
},
|
||||||
|
libraryItemUpdated(libraryItem) {
|
||||||
|
if (libraryItem.id === this.libraryItem.id) {
|
||||||
|
this.libraryItem = libraryItem
|
||||||
|
this.fetchMetadataEmbedObject()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.init()
|
this.init()
|
||||||
|
|
||||||
|
this.$eventBus.$on(`${this.libraryItem.id}_updated`, this.libraryItemUpdated)
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
this.$eventBus.$off(`${this.libraryItem.id}_updated`, this.libraryItemUpdated)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
export default class AudioTrack {
|
export default class AudioTrack {
|
||||||
constructor(track, sessionId, userToken, routerBasePath) {
|
constructor(track, sessionId, routerBasePath) {
|
||||||
this.index = track.index || 0
|
this.index = track.index || 0
|
||||||
this.startOffset = track.startOffset || 0 // Total time of all previous tracks
|
this.startOffset = track.startOffset || 0 // Total time of all previous tracks
|
||||||
this.duration = track.duration || 0
|
this.duration = track.duration || 0
|
||||||
@@ -11,7 +11,7 @@ export default class AudioTrack {
|
|||||||
this.sessionId = sessionId
|
this.sessionId = sessionId
|
||||||
this.routerBasePath = routerBasePath || ''
|
this.routerBasePath = routerBasePath || ''
|
||||||
if (this.contentUrl?.startsWith('/hls')) {
|
if (this.contentUrl?.startsWith('/hls')) {
|
||||||
this.sessionTrackUrl = `${this.contentUrl}?token=${userToken}`
|
this.sessionTrackUrl = this.contentUrl
|
||||||
} else {
|
} else {
|
||||||
this.sessionTrackUrl = `/public/session/${sessionId}/track/${this.index}`
|
this.sessionTrackUrl = `/public/session/${sessionId}/track/${this.index}`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,9 +37,6 @@ export default class PlayerHandler {
|
|||||||
get isPlayingLocalItem() {
|
get isPlayingLocalItem() {
|
||||||
return this.libraryItem && this.player instanceof LocalAudioPlayer
|
return this.libraryItem && this.player instanceof LocalAudioPlayer
|
||||||
}
|
}
|
||||||
get userToken() {
|
|
||||||
return this.ctx.$store.getters['user/getToken']
|
|
||||||
}
|
|
||||||
get playerPlaying() {
|
get playerPlaying() {
|
||||||
return this.playerState === 'PLAYING'
|
return this.playerState === 'PLAYING'
|
||||||
}
|
}
|
||||||
@@ -226,7 +223,7 @@ export default class PlayerHandler {
|
|||||||
|
|
||||||
console.log('[PlayerHandler] Preparing Session', session)
|
console.log('[PlayerHandler] Preparing Session', session)
|
||||||
|
|
||||||
var audioTracks = session.audioTracks.map((at) => new AudioTrack(at, session.id, this.userToken, this.ctx.$config.routerBasePath))
|
var audioTracks = session.audioTracks.map((at) => new AudioTrack(at, session.id, this.ctx.$config.routerBasePath))
|
||||||
|
|
||||||
this.ctx.playerLoading = true
|
this.ctx.playerLoading = true
|
||||||
this.isHlsTranscode = true
|
this.isHlsTranscode = true
|
||||||
|
|||||||
+1
-1
@@ -313,7 +313,7 @@ class Server {
|
|||||||
router.use(express.json({ limit: '5mb' }))
|
router.use(express.json({ limit: '5mb' }))
|
||||||
|
|
||||||
router.use('/api', this.auth.ifAuthNeeded(this.authMiddleware.bind(this)), this.apiRouter.router)
|
router.use('/api', this.auth.ifAuthNeeded(this.authMiddleware.bind(this)), this.apiRouter.router)
|
||||||
router.use('/hls', this.authMiddleware.bind(this), this.hlsRouter.router)
|
router.use('/hls', this.hlsRouter.router)
|
||||||
router.use('/public', this.publicRouter.router)
|
router.use('/public', this.publicRouter.router)
|
||||||
|
|
||||||
// Static path to generated nuxt
|
// Static path to generated nuxt
|
||||||
|
|||||||
Reference in New Issue
Block a user