mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-07-15 11:54:56 +02:00
Add playing podcast episodes, episode progress, podcast page, podcast home page shelves
This commit is contained in:
parent
e32d05ea27
commit
0e665e2091
28 changed files with 526 additions and 82 deletions
|
@ -440,8 +440,8 @@ class LibraryItem {
|
|||
return this.media.searchQuery(query)
|
||||
}
|
||||
|
||||
getDirectPlayTracklist(libraryItemId) {
|
||||
return this.media.getDirectPlayTracklist(libraryItemId)
|
||||
getDirectPlayTracklist(libraryItemId, episodeId) {
|
||||
return this.media.getDirectPlayTracklist(libraryItemId, episodeId)
|
||||
}
|
||||
}
|
||||
module.exports = LibraryItem
|
|
@ -9,6 +9,7 @@ class PlaybackSession {
|
|||
this.id = null
|
||||
this.userId = null
|
||||
this.libraryItemId = null
|
||||
this.episodeId = null
|
||||
|
||||
this.mediaType = null
|
||||
this.mediaMetadata = null
|
||||
|
@ -41,6 +42,7 @@ class PlaybackSession {
|
|||
sessionType: this.sessionType,
|
||||
userId: this.userId,
|
||||
libraryItemId: this.libraryItemId,
|
||||
episodeId: this.episodeId,
|
||||
mediaType: this.mediaType,
|
||||
mediaMetadata: this.mediaMetadata ? this.mediaMetadata.toJSON() : null,
|
||||
coverPath: this.coverPath,
|
||||
|
@ -60,6 +62,7 @@ class PlaybackSession {
|
|||
sessionType: this.sessionType,
|
||||
userId: this.userId,
|
||||
libraryItemId: this.libraryItemId,
|
||||
episodeId: this.episodeId,
|
||||
mediaType: this.mediaType,
|
||||
mediaMetadata: this.mediaMetadata ? this.mediaMetadata.toJSON() : null,
|
||||
coverPath: this.coverPath,
|
||||
|
@ -81,7 +84,8 @@ class PlaybackSession {
|
|||
this.sessionType = session.sessionType
|
||||
this.userId = session.userId
|
||||
this.libraryItemId = session.libraryItemId
|
||||
this.mediaType = session.mediaType
|
||||
this.episodeId = session.episodeId,
|
||||
this.mediaType = session.mediaType
|
||||
this.duration = session.duration
|
||||
this.playMethod = session.playMethod
|
||||
|
||||
|
@ -107,10 +111,11 @@ class PlaybackSession {
|
|||
return Math.max(0, Math.min(this.currentTime / this.duration, 1))
|
||||
}
|
||||
|
||||
setData(libraryItem, user) {
|
||||
setData(libraryItem, user, episodeId = null) {
|
||||
this.id = getId('play')
|
||||
this.userId = user.id
|
||||
this.libraryItemId = libraryItem.id
|
||||
this.episodeId = episodeId
|
||||
this.mediaType = libraryItem.mediaType
|
||||
this.mediaMetadata = libraryItem.media.metadata.clone()
|
||||
this.coverPath = libraryItem.media.coverPath
|
||||
|
|
|
@ -9,12 +9,13 @@ const hlsPlaylistGenerator = require('../utils/hlsPlaylistGenerator')
|
|||
const AudioTrack = require('./files/AudioTrack')
|
||||
|
||||
class Stream extends EventEmitter {
|
||||
constructor(sessionId, streamPath, user, libraryItem, startTime, clientEmitter, transcodeOptions = {}) {
|
||||
constructor(sessionId, streamPath, user, libraryItem, episodeId, startTime, clientEmitter, transcodeOptions = {}) {
|
||||
super()
|
||||
|
||||
this.id = sessionId
|
||||
this.user = user
|
||||
this.libraryItem = libraryItem
|
||||
this.episodeId = episodeId
|
||||
this.clientEmitter = clientEmitter
|
||||
|
||||
this.transcodeOptions = transcodeOptions
|
||||
|
@ -34,22 +35,28 @@ class Stream extends EventEmitter {
|
|||
this.isTranscodeComplete = false
|
||||
this.segmentsCreated = new Set()
|
||||
this.furthestSegmentCreated = 0
|
||||
// this.clientCurrentTime = 0
|
||||
|
||||
this.init()
|
||||
}
|
||||
|
||||
get isPodcast() {
|
||||
return this.libraryItem.mediaType === 'podcast'
|
||||
}
|
||||
get episode() {
|
||||
if (!this.isPodcast) return null
|
||||
return this.libraryItem.media.episodes.find(ep => ep.id === this.episodeId)
|
||||
}
|
||||
get libraryItemId() {
|
||||
return this.libraryItem.id
|
||||
}
|
||||
get mediaTitle() {
|
||||
if (this.episode) return this.episode.title || ''
|
||||
return this.libraryItem.media.metadata.title || ''
|
||||
}
|
||||
get totalDuration() {
|
||||
if (this.episode) return this.episode.duration
|
||||
return this.libraryItem.media.duration
|
||||
}
|
||||
get tracks() {
|
||||
// TODO: Podcast episode tracks
|
||||
if (this.episode) return this.episode.tracks
|
||||
return this.libraryItem.media.tracks
|
||||
}
|
||||
get tracksAudioFileType() {
|
||||
|
@ -99,28 +106,16 @@ class Stream extends EventEmitter {
|
|||
id: this.id,
|
||||
userId: this.user.id,
|
||||
libraryItem: this.libraryItem.toJSONExpanded(),
|
||||
episode: this.episode ? this.episode.toJSONExpanded() : null,
|
||||
segmentLength: this.segmentLength,
|
||||
playlistPath: this.playlistPath,
|
||||
clientPlaylistUri: this.clientPlaylistUri,
|
||||
// clientCurrentTime: this.clientCurrentTime,
|
||||
startTime: this.startTime,
|
||||
segmentStartNumber: this.segmentStartNumber,
|
||||
isTranscodeComplete: this.isTranscodeComplete,
|
||||
// lastUpdate: this.clientUserAudiobookData ? this.clientUserAudiobookData.lastUpdate : 0
|
||||
}
|
||||
}
|
||||
|
||||
init() {
|
||||
// if (this.clientUserAudiobookData) {
|
||||
// var timeRemaining = this.totalDuration - this.clientUserAudiobookData.currentTime
|
||||
// Logger.info('[STREAM] User has progress for item', this.clientUserAudiobookData.progress, `Time Remaining: ${timeRemaining}s`)
|
||||
// if (timeRemaining > 15) {
|
||||
// this.startTime = this.clientUserAudiobookData.currentTime
|
||||
// this.clientCurrentTime = this.startTime
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
async checkSegmentNumberRequest(segNum) {
|
||||
var segStartTime = segNum * this.segmentLength
|
||||
if (this.startTime > segStartTime) {
|
||||
|
|
|
@ -143,14 +143,20 @@ class Podcast {
|
|||
return payload || {}
|
||||
}
|
||||
|
||||
checkHasEpisode(episodeId) {
|
||||
return this.episodes.some(ep => ep.id === episodeId)
|
||||
}
|
||||
|
||||
// Only checks container format
|
||||
checkCanDirectPlay(payload, epsiodeIndex = 0) {
|
||||
var episode = this.episodes[epsiodeIndex]
|
||||
checkCanDirectPlay(payload, episodeId) {
|
||||
var episode = this.episodes.find(ep => ep.id === episodeId)
|
||||
if (!episode) return false
|
||||
return episode.checkCanDirectPlay(payload)
|
||||
}
|
||||
|
||||
getDirectPlayTracklist(libraryItemId, episodeIndex = 0) {
|
||||
var episode = this.episodes[episodeIndex]
|
||||
getDirectPlayTracklist(libraryItemId, episodeId) {
|
||||
var episode = this.episodes.find(ep => ep.id === episodeId)
|
||||
if (!episode) return false
|
||||
return episode.getDirectPlayTracklist(libraryItemId)
|
||||
}
|
||||
|
||||
|
@ -164,6 +170,15 @@ class Podcast {
|
|||
this.episodes.push(pe)
|
||||
}
|
||||
|
||||
setEpisodeOrder(episodeIds) {
|
||||
this.episodes = this.episodes.map(ep => {
|
||||
var indexOf = episodeIds.findIndex(id => id === ep.id)
|
||||
ep.index = indexOf + 1
|
||||
return ep
|
||||
})
|
||||
this.episodes.sort((a, b) => b.index - a.index)
|
||||
}
|
||||
|
||||
reorderEpisodes() {
|
||||
var hasUpdates = false
|
||||
this.episodes = naturalSort(this.episodes).asc((ep) => ep.bestFilename)
|
||||
|
@ -173,7 +188,12 @@ class Podcast {
|
|||
hasUpdates = true
|
||||
}
|
||||
}
|
||||
this.episodes.sort((a, b) => b.index - a.index)
|
||||
return hasUpdates
|
||||
}
|
||||
|
||||
removeEpisode(episodeId) {
|
||||
this.episodes = this.episodes.filter(ep => ep.id !== episodeId)
|
||||
}
|
||||
}
|
||||
module.exports = Podcast
|
|
@ -52,10 +52,10 @@ class MediaProgress {
|
|||
return !this.isFinished && this.progress > 0
|
||||
}
|
||||
|
||||
setData(libraryItemId, progress) {
|
||||
setData(libraryItemId, progress, episodeId = null) {
|
||||
this.id = libraryItemId
|
||||
this.libraryItemId = libraryItemId
|
||||
this.episodeId = progress.episodeId || null
|
||||
this.episodeId = episodeId
|
||||
this.duration = progress.duration || 0
|
||||
this.progress = Math.min(1, (progress.progress || 0))
|
||||
this.currentTime = progress.currentTime || 0
|
||||
|
@ -74,11 +74,11 @@ class MediaProgress {
|
|||
for (const key in payload) {
|
||||
if (this[key] !== undefined && payload[key] !== this[key]) {
|
||||
if (key === 'isFinished') {
|
||||
if (!payload[key]) { // Updating to Not Read - Reset progress and current time
|
||||
if (!payload[key]) { // Updating to Not Finished - Reset progress and current time
|
||||
this.finishedAt = null
|
||||
this.progress = 0
|
||||
this.currentTime = 0
|
||||
} else { // Updating to Read
|
||||
} else { // Updating to Finished
|
||||
if (!this.finishedAt) this.finishedAt = Date.now()
|
||||
this.progress = 1
|
||||
}
|
||||
|
@ -88,6 +88,16 @@ class MediaProgress {
|
|||
hasUpdates = true
|
||||
}
|
||||
}
|
||||
|
||||
if (this.progress >= 1 && !this.isFinished) {
|
||||
this.isFinished = true
|
||||
this.finishedAt = Date.now()
|
||||
this.progress = 1
|
||||
} else if (this.progress < 1 && this.isFinished) {
|
||||
this.isFinished = false
|
||||
this.finishedAt = null
|
||||
}
|
||||
|
||||
if (!this.startedAt) {
|
||||
this.startedAt = Date.now()
|
||||
}
|
||||
|
|
|
@ -236,17 +236,23 @@ class User {
|
|||
}
|
||||
}
|
||||
|
||||
getMediaProgress(libraryItemId) {
|
||||
getMediaProgress(libraryItemId, episodeId = null) {
|
||||
if (!this.mediaProgress) return null
|
||||
return this.mediaProgress.find(lip => lip.id === libraryItemId)
|
||||
return this.mediaProgress.find(lip => {
|
||||
if (episodeId && lip.episodeId !== episodeId) return false
|
||||
return lip.id === libraryItemId
|
||||
})
|
||||
}
|
||||
|
||||
createUpdateMediaProgress(libraryItem, updatePayload) {
|
||||
var itemProgress = this.mediaProgress.find(li => li.id === libraryItem.id)
|
||||
createUpdateMediaProgress(libraryItem, updatePayload, episodeId = null) {
|
||||
var itemProgress = this.mediaProgress.find(li => {
|
||||
if (episodeId && li.episodeId !== episodeId) return false
|
||||
return li.id === libraryItem.id
|
||||
})
|
||||
if (!itemProgress) {
|
||||
var newItemProgress = new MediaProgress()
|
||||
|
||||
newItemProgress.setData(libraryItem.id, updatePayload)
|
||||
newItemProgress.setData(libraryItem.id, updatePayload, episodeId)
|
||||
this.mediaProgress.push(newItemProgress)
|
||||
return true
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue