mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-02-14 17:29:35 +01:00
Merge branch 'advplyr:master' into show-subtitles
This commit is contained in:
commit
0042604e6d
@ -341,7 +341,7 @@ export default {
|
||||
if (this.recentEpisode) return this.recentEpisode.title
|
||||
const ignorePrefix = this.orderBy === 'media.metadata.title' && this.sortingIgnorePrefix
|
||||
if (this.collapsedSeries) return ignorePrefix ? this.collapsedSeries.nameIgnorePrefix : this.collapsedSeries.name
|
||||
return ignorePrefix ? this.mediaMetadata.titleIgnorePrefix : this.title || '\u00A0'
|
||||
return ignorePrefix ? this.mediaMetadata.titleIgnorePrefix || '\u00A0' : this.title || '\u00A0'
|
||||
},
|
||||
displaySubtitle() {
|
||||
if (!this.libraryItem) return '\u00A0'
|
||||
@ -369,7 +369,10 @@ export default {
|
||||
if (this.orderBy === 'media.duration') return 'Duration: ' + this.$elapsedPrettyExtended(this.media.duration, false)
|
||||
if (this.orderBy === 'size') return 'Size: ' + this.$bytesPretty(this._libraryItem.size)
|
||||
if (this.orderBy === 'media.numTracks') return `${this.numEpisodes} Episodes`
|
||||
if (this.orderBy === 'media.metadata.publishedYear' && this.mediaMetadata.publishedYear) return 'Published ' + this.mediaMetadata.publishedYear
|
||||
if (this.orderBy === 'media.metadata.publishedYear') {
|
||||
if (this.mediaMetadata.publishedYear) return 'Published ' + this.mediaMetadata.publishedYear
|
||||
return '\u00A0'
|
||||
}
|
||||
return null
|
||||
},
|
||||
episodeProgress() {
|
||||
|
@ -229,6 +229,11 @@ export default {
|
||||
text: this.$strings.LabelRSSFeedOpen,
|
||||
value: 'feed-open',
|
||||
sublist: false
|
||||
},
|
||||
{
|
||||
text: this.$strings.LabelShareOpen,
|
||||
value: 'share-open',
|
||||
sublist: false
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="w-full h-screen max-h-screen overflow-hidden" :style="{ backgroundColor: coverRgb }">
|
||||
<div class="w-full h-dvh max-h-dvh overflow-hidden" :style="{ backgroundColor: coverRgb }">
|
||||
<div class="w-screen h-screen absolute inset-0 pointer-events-none" style="background: linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, rgba(38, 38, 38, 1) 80%)"></div>
|
||||
<div class="absolute inset-0 w-screen h-screen flex items-center justify-center z-10">
|
||||
<div class="absolute inset-0 w-screen h-dvh flex items-center justify-center z-10">
|
||||
<div class="w-full p-2 sm:p-4 md:p-8">
|
||||
<div v-if="!isMobileLandscape" :style="{ width: coverWidth + 'px', height: coverHeight + 'px' }" class="mx-auto overflow-hidden rounded-xl my-2">
|
||||
<img ref="coverImg" :src="coverUrl" class="object-contain w-full h-full" @load="coverImageLoaded" />
|
||||
@ -113,6 +113,8 @@ export default {
|
||||
.then((color) => {
|
||||
this.coverRgb = color.rgba
|
||||
this.coverBgIsLight = color.isLight
|
||||
|
||||
document.body.style.backgroundColor = color.hex
|
||||
})
|
||||
.catch((e) => {
|
||||
console.log(e)
|
||||
|
@ -512,6 +512,7 @@
|
||||
"LabelSettingsStoreMetadataWithItemHelp": "By default metadata files are stored in /metadata/items, enabling this setting will store metadata files in your library item folders",
|
||||
"LabelSettingsTimeFormat": "Time Format",
|
||||
"LabelShare": "Share",
|
||||
"LabelShareOpen": "Share Open",
|
||||
"LabelShareURL": "Share URL",
|
||||
"LabelShowAll": "Show All",
|
||||
"LabelShowSeconds": "Show seconds",
|
||||
|
@ -74,7 +74,7 @@ class Server {
|
||||
this.podcastManager = new PodcastManager(this.watcher, this.notificationManager)
|
||||
this.audioMetadataManager = new AudioMetadataMangaer()
|
||||
this.rssFeedManager = new RssFeedManager()
|
||||
this.cronManager = new CronManager(this.podcastManager)
|
||||
this.cronManager = new CronManager(this.podcastManager, this.playbackSessionManager)
|
||||
this.apiCacheManager = new ApiCacheManager()
|
||||
this.binaryManager = new BinaryManager()
|
||||
|
||||
|
@ -42,19 +42,14 @@ class ShareController {
|
||||
const playbackSession = ShareManager.findPlaybackSessionBySessionId(req.cookies.share_session_id)
|
||||
|
||||
if (playbackSession) {
|
||||
const playbackSessionMediaItemShare = ShareManager.findByMediaItemId(playbackSession.mediaItemId)
|
||||
if (!playbackSessionMediaItemShare) {
|
||||
Logger.error(`[ShareController] Share playback session ${req.cookies.share_session_id} media item share not found with id ${playbackSession.mediaItemId}`)
|
||||
return res.sendStatus(500)
|
||||
}
|
||||
if (playbackSessionMediaItemShare.slug === slug) {
|
||||
if (mediaItemShare.id === playbackSession.mediaItemShareId) {
|
||||
Logger.debug(`[ShareController] Found share playback session ${req.cookies.share_session_id}`)
|
||||
mediaItemShare.playbackSession = playbackSession.toJSONForClient()
|
||||
return res.json(mediaItemShare)
|
||||
} else {
|
||||
// TODO: Close old session and use same session id
|
||||
Logger.info(`[ShareController] Share playback session found with id ${req.cookies.share_session_id} but media item share slug ${playbackSessionMediaItemShare.slug} does not match requested slug ${slug}`)
|
||||
res.clearCookie('share_session_id')
|
||||
// Changed media item share - close other session
|
||||
Logger.debug(`[ShareController] Other playback session is already open for share session. Closing session "${playbackSession.displayTitle}"`)
|
||||
ShareManager.closeSharePlaybackSession(playbackSession)
|
||||
}
|
||||
} else {
|
||||
Logger.info(`[ShareController] Share playback session not found with id ${req.cookies.share_session_id}`)
|
||||
@ -238,6 +233,7 @@ class ShareController {
|
||||
}
|
||||
|
||||
playbackSession.currentTime = Math.min(currentTime, playbackSession.duration)
|
||||
playbackSession.updatedAt = Date.now()
|
||||
Logger.debug(`[ShareController] Update share playback session ${req.cookies.share_session_id} currentTime: ${playbackSession.currentTime}`)
|
||||
res.sendStatus(204)
|
||||
}
|
||||
|
@ -4,9 +4,14 @@ const Logger = require('../Logger')
|
||||
const Database = require('../Database')
|
||||
const LibraryScanner = require('../scanner/LibraryScanner')
|
||||
|
||||
const ShareManager = require('./ShareManager')
|
||||
|
||||
class CronManager {
|
||||
constructor(podcastManager) {
|
||||
constructor(podcastManager, playbackSessionManager) {
|
||||
/** @type {import('./PodcastManager')} */
|
||||
this.podcastManager = podcastManager
|
||||
/** @type {import('./PlaybackSessionManager')} */
|
||||
this.playbackSessionManager = playbackSessionManager
|
||||
|
||||
this.libraryScanCrons = []
|
||||
this.podcastCrons = []
|
||||
@ -19,10 +24,26 @@ class CronManager {
|
||||
* @param {import('../objects/Library')[]} libraries
|
||||
*/
|
||||
async init(libraries) {
|
||||
this.initOpenSessionCleanupCron()
|
||||
this.initLibraryScanCrons(libraries)
|
||||
await this.initPodcastCrons()
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize open session cleanup cron
|
||||
* Runs every day at 00:30
|
||||
* Closes open share sessions that have not been updated in 24 hours
|
||||
* Closes open playback sessions that have not been updated in 36 hours
|
||||
* TODO: Clients should re-open the session if it is closed so that stale sessions can be closed sooner
|
||||
*/
|
||||
initOpenSessionCleanupCron() {
|
||||
cron.schedule('30 0 * * *', async () => {
|
||||
Logger.debug('[CronManager] Open session cleanup cron executing')
|
||||
ShareManager.closeStaleOpenShareSessions()
|
||||
await this.playbackSessionManager.closeStaleOpenSessions()
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize library scan crons
|
||||
* @param {import('../objects/Library')[]} libraries
|
||||
|
@ -21,6 +21,8 @@ class PlaybackSessionManager {
|
||||
this.StreamsPath = Path.join(global.MetadataPath, 'streams')
|
||||
|
||||
this.oldPlaybackSessionMap = {} // TODO: Remove after updated mobile versions
|
||||
|
||||
/** @type {PlaybackSession[]} */
|
||||
this.sessions = []
|
||||
}
|
||||
|
||||
@ -346,6 +348,10 @@ class PlaybackSessionManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} sessionId
|
||||
*/
|
||||
async removeSession(sessionId) {
|
||||
const session = this.sessions.find((s) => s.id === sessionId)
|
||||
if (!session) return
|
||||
@ -378,5 +384,18 @@ class PlaybackSessionManager {
|
||||
Logger.error(`[PlaybackSessionManager] cleanOrphanStreams failed`, error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close all open sessions that have not been updated in the last 36 hours
|
||||
*/
|
||||
async closeStaleOpenSessions() {
|
||||
const updatedAtTimeCutoff = Date.now() - 1000 * 60 * 60 * 36
|
||||
const staleSessions = this.sessions.filter((session) => session.updatedAt < updatedAtTimeCutoff)
|
||||
for (const session of staleSessions) {
|
||||
const sessionLastUpdate = new Date(session.updatedAt)
|
||||
Logger.info(`[PlaybackSessionManager] Closing stale session "${session.displayTitle}" (${session.id}) last updated at ${sessionLastUpdate}`)
|
||||
await this.removeSession(session.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
module.exports = PlaybackSessionManager
|
||||
|
@ -25,10 +25,19 @@ class ShareManager {
|
||||
* @param {import('../objects/PlaybackSession')} playbackSession
|
||||
*/
|
||||
addOpenSharePlaybackSession(playbackSession) {
|
||||
Logger.info(`[ShareManager] Adding new open share playback session ${playbackSession.shareSessionId}`)
|
||||
Logger.info(`[ShareManager] Adding new open share playback session "${playbackSession.displayTitle}"`)
|
||||
this.openSharePlaybackSessions.push(playbackSession)
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {import('../objects/PlaybackSession')} playbackSession
|
||||
*/
|
||||
closeSharePlaybackSession(playbackSession) {
|
||||
Logger.info(`[ShareManager] Closing share playback session "${playbackSession.displayTitle}"`)
|
||||
this.openSharePlaybackSessions = this.openSharePlaybackSessions.filter((s) => s.id !== playbackSession.id)
|
||||
}
|
||||
|
||||
/**
|
||||
* Find an open media item share by media item ID
|
||||
* @param {string} mediaItemId
|
||||
@ -153,5 +162,18 @@ class ShareManager {
|
||||
destroyMediaItemShare(mediaItemShareId) {
|
||||
return Database.models.mediaItemShare.destroy({ where: { id: mediaItemShareId } })
|
||||
}
|
||||
|
||||
/**
|
||||
* Close open share sessions that have not been updated in the last 24 hours
|
||||
*/
|
||||
closeStaleOpenShareSessions() {
|
||||
const updatedAtTimeCutoff = Date.now() - 1000 * 60 * 60 * 24
|
||||
const staleSessions = this.openSharePlaybackSessions.filter((session) => session.updatedAt < updatedAtTimeCutoff)
|
||||
for (const session of staleSessions) {
|
||||
const sessionLastUpdate = new Date(session.updatedAt)
|
||||
Logger.info(`[PlaybackSessionManager] Closing stale session "${session.displayTitle}" (${session.id}) last updated at ${sessionLastUpdate}`)
|
||||
this.closeSharePlaybackSession(session)
|
||||
}
|
||||
}
|
||||
}
|
||||
module.exports = new ShareManager()
|
||||
|
@ -568,7 +568,6 @@ class LibraryItem extends Model {
|
||||
oldLibraryItem.numEpisodesIncomplete = li.numEpisodesIncomplete
|
||||
}
|
||||
if (li.mediaType === 'book' && options.include?.includes?.('share')) {
|
||||
console.log('Lookup share for media item id', li.mediaId)
|
||||
oldLibraryItem.mediaItemShare = ShareManager.findByMediaItemId(li.mediaId)
|
||||
}
|
||||
|
||||
|
@ -412,6 +412,11 @@ module.exports = {
|
||||
model: Database.feedModel,
|
||||
required: true
|
||||
})
|
||||
} else if (filterGroup === 'share-open') {
|
||||
bookIncludes.push({
|
||||
model: Database.mediaItemShareModel,
|
||||
required: true
|
||||
})
|
||||
} else if (filterGroup === 'ebooks' && filterValue === 'supplementary') {
|
||||
// TODO: Temp workaround for filtering supplementary ebook
|
||||
libraryItemWhere['libraryFiles'] = {
|
||||
|
Loading…
Reference in New Issue
Block a user