mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-01-03 00:06:46 +01:00
Update:Media item share endpoints and audio player #1768
- Add endpoints for getting tracks, getting cover image and updating progress - Implement share session cookie and caching share playback session - Audio player UI/UX
This commit is contained in:
parent
c1349e586a
commit
31146082f0
@ -1,10 +1,14 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="page-wrapper" class="w-full h-screen overflow-y-auto">
|
<div id="page-wrapper" class="w-full h-screen max-h-screen overflow-hidden">
|
||||||
<div class="w-full h-full flex items-center justify-center">
|
<div class="w-full h-full flex items-center justify-center">
|
||||||
<div class="w-full p-8">
|
<div class="w-full p-2 sm:p-4 md:p-8">
|
||||||
<p class="text-3xl font-semibold text-center mb-6">{{ mediaItemShare.playbackSession?.displayTitle || 'N/A' }}</p>
|
<div :style="{ width: coverWidth + 'px', height: coverHeight + 'px' }" class="mx-auto overflow-hidden rounded-xl my-2">
|
||||||
|
<img :src="coverUrl" class="object-contain w-full h-full" />
|
||||||
|
</div>
|
||||||
|
<p class="text-2xl md:text-3xl font-semibold text-center mb-1">{{ mediaItemShare.playbackSession.displayTitle || 'No title' }}</p>
|
||||||
|
<p v-if="mediaItemShare.playbackSession.displayAuthor" class="text-xl text-slate-400 font-semibold text-center mb-1">{{ mediaItemShare.playbackSession.displayAuthor }}</p>
|
||||||
|
|
||||||
<div class="w-full py-8">
|
<div class="w-full pt-16">
|
||||||
<player-ui ref="audioPlayer" :chapters="chapters" :paused="isPaused" :loading="!hasLoaded" :is-podcast="false" hide-bookmarks hide-sleep-timer @playPause="playPause" @jumpForward="jumpForward" @jumpBackward="jumpBackward" @setVolume="setVolume" @setPlaybackRate="setPlaybackRate" @seek="seek" />
|
<player-ui ref="audioPlayer" :chapters="chapters" :paused="isPaused" :loading="!hasLoaded" :is-podcast="false" hide-bookmarks hide-sleep-timer @playPause="playPause" @jumpForward="jumpForward" @jumpBackward="jumpBackward" @setVolume="setVolume" @setPlaybackRate="setPlaybackRate" @seek="seek" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -36,12 +40,25 @@ export default {
|
|||||||
playerState: null,
|
playerState: null,
|
||||||
playInterval: null,
|
playInterval: null,
|
||||||
hasLoaded: false,
|
hasLoaded: false,
|
||||||
totalDuration: 0
|
totalDuration: 0,
|
||||||
|
windowWidth: 0,
|
||||||
|
windowHeight: 0,
|
||||||
|
listeningTimeSinceSync: 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
playbackSession() {
|
||||||
|
return this.mediaItemShare.playbackSession
|
||||||
|
},
|
||||||
|
coverUrl() {
|
||||||
|
if (!this.playbackSession.coverPath) return `${this.$config.routerBasePath}/book_placeholder.jpg`
|
||||||
|
if (process.env.NODE_ENV === 'development') {
|
||||||
|
return `http://localhost:3333/public/share/${this.mediaItemShare.slug}/cover`
|
||||||
|
}
|
||||||
|
return `/public/share/${this.mediaItemShare.slug}/cover`
|
||||||
|
},
|
||||||
audioTracks() {
|
audioTracks() {
|
||||||
return (this.mediaItemShare.playbackSession?.audioTracks || []).map((track) => {
|
return (this.playbackSession.audioTracks || []).map((track) => {
|
||||||
if (process.env.NODE_ENV === 'development') {
|
if (process.env.NODE_ENV === 'development') {
|
||||||
track.contentUrl = `${process.env.serverUrl}${track.contentUrl}`
|
track.contentUrl = `${process.env.serverUrl}${track.contentUrl}`
|
||||||
}
|
}
|
||||||
@ -56,7 +73,24 @@ export default {
|
|||||||
return !this.isPlaying
|
return !this.isPlaying
|
||||||
},
|
},
|
||||||
chapters() {
|
chapters() {
|
||||||
return this.mediaItemShare.playbackSession?.chapters || []
|
return this.playbackSession.chapters || []
|
||||||
|
},
|
||||||
|
coverAspectRatio() {
|
||||||
|
const coverAspectRatio = this.playbackSession.coverAspectRatio
|
||||||
|
return coverAspectRatio === this.$constants.BookCoverAspectRatio.STANDARD ? 1.6 : 1
|
||||||
|
},
|
||||||
|
coverWidth() {
|
||||||
|
const availableCoverWidth = Math.min(450, this.windowWidth - 32)
|
||||||
|
const availableCoverHeight = Math.min(450, this.windowHeight - 250)
|
||||||
|
|
||||||
|
const mostCoverHeight = availableCoverWidth * this.coverAspectRatio
|
||||||
|
if (mostCoverHeight > availableCoverHeight) {
|
||||||
|
return availableCoverHeight / this.coverAspectRatio
|
||||||
|
}
|
||||||
|
return availableCoverWidth
|
||||||
|
},
|
||||||
|
coverHeight() {
|
||||||
|
return this.coverWidth * this.coverAspectRatio
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@ -102,11 +136,29 @@ export default {
|
|||||||
this.$refs.audioPlayer.setDuration(this.totalDuration)
|
this.$refs.audioPlayer.setDuration(this.totalDuration)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
sendProgressSync(currentTime) {
|
||||||
|
console.log('Sending progress sync for time', currentTime)
|
||||||
|
const progress = {
|
||||||
|
currentTime
|
||||||
|
}
|
||||||
|
this.$axios.$patch(`/public/share/${this.mediaItemShare.slug}/progress`, progress, { progress: false }).catch((error) => {
|
||||||
|
console.error('Failed to send progress sync', error)
|
||||||
|
})
|
||||||
|
},
|
||||||
startPlayInterval() {
|
startPlayInterval() {
|
||||||
|
let lastTick = Date.now()
|
||||||
clearInterval(this.playInterval)
|
clearInterval(this.playInterval)
|
||||||
this.playInterval = setInterval(() => {
|
this.playInterval = setInterval(() => {
|
||||||
if (this.localAudioPlayer) {
|
if (!this.localAudioPlayer) return
|
||||||
this.setCurrentTime(this.localAudioPlayer.getCurrentTime())
|
|
||||||
|
const currentTime = this.localAudioPlayer.getCurrentTime()
|
||||||
|
this.setCurrentTime(currentTime)
|
||||||
|
const exactTimeElapsed = (Date.now() - lastTick) / 1000
|
||||||
|
lastTick = Date.now()
|
||||||
|
this.listeningTimeSinceSync += exactTimeElapsed
|
||||||
|
if (this.listeningTimeSinceSync >= 30) {
|
||||||
|
this.listeningTimeSinceSync = 0
|
||||||
|
this.sendProgressSync(currentTime)
|
||||||
}
|
}
|
||||||
}, 1000)
|
}, 1000)
|
||||||
},
|
},
|
||||||
@ -115,7 +167,6 @@ export default {
|
|||||||
this.playInterval = null
|
this.playInterval = null
|
||||||
},
|
},
|
||||||
playerStateChange(state) {
|
playerStateChange(state) {
|
||||||
console.log('Player state change', state)
|
|
||||||
this.playerState = state
|
this.playerState = state
|
||||||
if (state === 'LOADED' || state === 'PLAYING') {
|
if (state === 'LOADED' || state === 'PLAYING') {
|
||||||
this.setDuration()
|
this.setDuration()
|
||||||
@ -158,17 +209,28 @@ export default {
|
|||||||
this.$eventBus.$emit('player-hotkey', name)
|
this.$eventBus.$emit('player-hotkey', name)
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
resize() {
|
||||||
|
this.windowWidth = window.innerWidth
|
||||||
|
this.windowHeight = window.innerHeight
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
this.resize()
|
||||||
|
window.addEventListener('resize', this.resize)
|
||||||
window.addEventListener('keydown', this.keyDown)
|
window.addEventListener('keydown', this.keyDown)
|
||||||
|
|
||||||
console.log('Loaded media item share', this.mediaItemShare)
|
if (process.env.NODE_ENV === 'development') {
|
||||||
this.localAudioPlayer.set(null, this.audioTracks, false, 0, false)
|
console.log('Loaded media item share', this.mediaItemShare)
|
||||||
|
}
|
||||||
|
|
||||||
|
const startTime = this.playbackSession.currentTime || 0
|
||||||
|
this.localAudioPlayer.set(null, this.audioTracks, false, startTime, false)
|
||||||
this.localAudioPlayer.on('stateChange', this.playerStateChange.bind(this))
|
this.localAudioPlayer.on('stateChange', this.playerStateChange.bind(this))
|
||||||
this.localAudioPlayer.on('timeupdate', this.playerTimeUpdate.bind(this))
|
this.localAudioPlayer.on('timeupdate', this.playerTimeUpdate.bind(this))
|
||||||
},
|
},
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
|
window.removeEventListener('resize', this.resize)
|
||||||
window.removeEventListener('keydown', this.keyDown)
|
window.removeEventListener('keydown', this.keyDown)
|
||||||
|
|
||||||
this.localAudioPlayer.off('stateChange', this.playerStateChange)
|
this.localAudioPlayer.off('stateChange', this.playerStateChange)
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
const uuidv4 = require('uuid').v4
|
||||||
const Path = require('path')
|
const Path = require('path')
|
||||||
const { Op } = require('sequelize')
|
const { Op } = require('sequelize')
|
||||||
const Logger = require('../Logger')
|
const Logger = require('../Logger')
|
||||||
@ -32,6 +33,18 @@ class ShareController {
|
|||||||
return res.status(404).send('Media item share not found')
|
return res.status(404).send('Media item share not found')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (req.cookies.share_session_id) {
|
||||||
|
const playbackSession = ShareManager.findPlaybackSessionBySessionId(req.cookies.share_session_id)
|
||||||
|
if (playbackSession) {
|
||||||
|
Logger.debug(`[ShareController] Found share playback session ${req.cookies.share_session_id}`)
|
||||||
|
mediaItemShare.playbackSession = playbackSession.toJSONForClient()
|
||||||
|
return res.json(mediaItemShare)
|
||||||
|
} else {
|
||||||
|
Logger.info(`[ShareController] Share playback session not found with id ${req.cookies.share_session_id}`)
|
||||||
|
res.clearCookie('share_session_id')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const oldLibraryItem = await Database.mediaItemShareModel.getMediaItemsOldLibraryItem(mediaItemShare.mediaItemId, mediaItemShare.mediaItemType)
|
const oldLibraryItem = await Database.mediaItemShareModel.getMediaItemsOldLibraryItem(mediaItemShare.mediaItemId, mediaItemShare.mediaItemType)
|
||||||
|
|
||||||
@ -46,7 +59,7 @@ class ShareController {
|
|||||||
startOffset,
|
startOffset,
|
||||||
duration: audioFile.duration,
|
duration: audioFile.duration,
|
||||||
title: audioFile.metadata.filename || '',
|
title: audioFile.metadata.filename || '',
|
||||||
contentUrl: `${global.RouterBasePath}/public/share/${slug}/file/${audioFile.ino}`,
|
contentUrl: `${global.RouterBasePath}/public/share/${slug}/track/${audioFile.index}`,
|
||||||
mimeType: audioFile.mimeType,
|
mimeType: audioFile.mimeType,
|
||||||
codec: audioFile.codec || null,
|
codec: audioFile.codec || null,
|
||||||
metadata: audioFile.metadata.clone()
|
metadata: audioFile.metadata.clone()
|
||||||
@ -59,8 +72,15 @@ class ShareController {
|
|||||||
newPlaybackSession.setData(oldLibraryItem, null, 'web-public', null, 0)
|
newPlaybackSession.setData(oldLibraryItem, null, 'web-public', null, 0)
|
||||||
newPlaybackSession.audioTracks = publicTracks
|
newPlaybackSession.audioTracks = publicTracks
|
||||||
newPlaybackSession.playMethod = PlayMethod.DIRECTPLAY
|
newPlaybackSession.playMethod = PlayMethod.DIRECTPLAY
|
||||||
|
newPlaybackSession.shareSessionId = uuidv4() // New share session id
|
||||||
|
newPlaybackSession.mediaItemShareId = mediaItemShare.id
|
||||||
|
newPlaybackSession.coverAspectRatio = oldLibraryItem.librarySettings.coverAspectRatio
|
||||||
|
|
||||||
mediaItemShare.playbackSession = newPlaybackSession.toJSONForClient()
|
mediaItemShare.playbackSession = newPlaybackSession.toJSONForClient()
|
||||||
|
ShareManager.addOpenSharePlaybackSession(newPlaybackSession)
|
||||||
|
|
||||||
|
// 30 day cookie
|
||||||
|
res.cookie('share_session_id', newPlaybackSession.shareSessionId, { maxAge: 1000 * 60 * 60 * 24 * 30, httpOnly: true })
|
||||||
|
|
||||||
res.json(mediaItemShare)
|
res.json(mediaItemShare)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -70,45 +90,127 @@ class ShareController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Public route
|
* Public route - requires share_session_id cookie
|
||||||
* GET: /api/share/:slug/file/:fileid
|
*
|
||||||
* Get media item share file
|
* GET: /api/share/:slug/cover
|
||||||
|
* Get media item share cover image
|
||||||
*
|
*
|
||||||
* @param {import('express').Request} req
|
* @param {import('express').Request} req
|
||||||
* @param {import('express').Response} res
|
* @param {import('express').Response} res
|
||||||
*/
|
*/
|
||||||
async getMediaItemShareFile(req, res) {
|
async getMediaItemShareCoverImage(req, res) {
|
||||||
const { slug, fileid } = req.params
|
if (!req.cookies.share_session_id) {
|
||||||
|
return res.status(404).send('Share session not set')
|
||||||
|
}
|
||||||
|
|
||||||
|
const { slug } = req.params
|
||||||
|
|
||||||
const mediaItemShare = ShareManager.findBySlug(slug)
|
const mediaItemShare = ShareManager.findBySlug(slug)
|
||||||
if (!mediaItemShare) {
|
if (!mediaItemShare) {
|
||||||
return res.status(404)
|
return res.status(404)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @type {import('../models/LibraryItem')} */
|
const playbackSession = ShareManager.findPlaybackSessionBySessionId(req.cookies.share_session_id)
|
||||||
const libraryItem = await Database.libraryItemModel.findOne({
|
if (!playbackSession || playbackSession.mediaItemShareId !== mediaItemShare.id) {
|
||||||
where: {
|
res.clearCookie('share_session_id')
|
||||||
mediaId: mediaItemShare.mediaItemId
|
return res.status(404).send('Share session not found')
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
|
||||||
const libraryFile = libraryItem?.libraryFiles.find((lf) => lf.ino === fileid)
|
const coverPath = playbackSession.coverPath
|
||||||
if (!libraryFile) {
|
if (!coverPath) {
|
||||||
return res.status(404).send('File not found')
|
return res.status(404).send('Cover image not found')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (global.XAccel) {
|
if (global.XAccel) {
|
||||||
const encodedURI = encodeUriPath(global.XAccel + libraryFile.metadata.path)
|
const encodedURI = encodeUriPath(global.XAccel + coverPath)
|
||||||
|
Logger.debug(`Use X-Accel to serve static file ${encodedURI}`)
|
||||||
|
return res.status(204).header({ 'X-Accel-Redirect': encodedURI }).send()
|
||||||
|
}
|
||||||
|
|
||||||
|
res.sendFile(coverPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Public route - requires share_session_id cookie
|
||||||
|
*
|
||||||
|
* GET: /api/share/:slug/track/:index
|
||||||
|
* Get media item share audio track
|
||||||
|
*
|
||||||
|
* @param {import('express').Request} req
|
||||||
|
* @param {import('express').Response} res
|
||||||
|
*/
|
||||||
|
async getMediaItemShareAudioTrack(req, res) {
|
||||||
|
if (!req.cookies.share_session_id) {
|
||||||
|
return res.status(404).send('Share session not set')
|
||||||
|
}
|
||||||
|
|
||||||
|
const { slug, index } = req.params
|
||||||
|
|
||||||
|
const mediaItemShare = ShareManager.findBySlug(slug)
|
||||||
|
if (!mediaItemShare) {
|
||||||
|
return res.status(404)
|
||||||
|
}
|
||||||
|
|
||||||
|
const playbackSession = ShareManager.findPlaybackSessionBySessionId(req.cookies.share_session_id)
|
||||||
|
if (!playbackSession || playbackSession.mediaItemShareId !== mediaItemShare.id) {
|
||||||
|
res.clearCookie('share_session_id')
|
||||||
|
return res.status(404).send('Share session not found')
|
||||||
|
}
|
||||||
|
|
||||||
|
const audioTrack = playbackSession.audioTracks.find((t) => t.index === parseInt(index))
|
||||||
|
if (!audioTrack) {
|
||||||
|
return res.status(404).send('Track not found')
|
||||||
|
}
|
||||||
|
const audioTrackPath = audioTrack.metadata.path
|
||||||
|
|
||||||
|
if (global.XAccel) {
|
||||||
|
const encodedURI = encodeUriPath(global.XAccel + audioTrackPath)
|
||||||
Logger.debug(`Use X-Accel to serve static file ${encodedURI}`)
|
Logger.debug(`Use X-Accel to serve static file ${encodedURI}`)
|
||||||
return res.status(204).header({ 'X-Accel-Redirect': encodedURI }).send()
|
return res.status(204).header({ 'X-Accel-Redirect': encodedURI }).send()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Express does not set the correct mimetype for m4b files so use our defined mimetypes if available
|
// Express does not set the correct mimetype for m4b files so use our defined mimetypes if available
|
||||||
const audioMimeType = getAudioMimeTypeFromExtname(Path.extname(libraryFile.metadata.path))
|
const audioMimeType = getAudioMimeTypeFromExtname(Path.extname(audioTrackPath))
|
||||||
if (audioMimeType) {
|
if (audioMimeType) {
|
||||||
res.setHeader('Content-Type', audioMimeType)
|
res.setHeader('Content-Type', audioMimeType)
|
||||||
}
|
}
|
||||||
res.sendFile(libraryFile.metadata.path)
|
res.sendFile(audioTrackPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Public route - requires share_session_id cookie
|
||||||
|
*
|
||||||
|
* PATCH: /api/share/:slug/progress
|
||||||
|
* Update media item share progress
|
||||||
|
*
|
||||||
|
* @param {import('express').Request} req
|
||||||
|
* @param {import('express').Response} res
|
||||||
|
*/
|
||||||
|
async updateMediaItemShareProgress(req, res) {
|
||||||
|
if (!req.cookies.share_session_id) {
|
||||||
|
return res.status(404).send('Share session not set')
|
||||||
|
}
|
||||||
|
|
||||||
|
const { slug } = req.params
|
||||||
|
const { currentTime } = req.body
|
||||||
|
if (currentTime === null || isNaN(currentTime) || currentTime < 0) {
|
||||||
|
return res.status(400).send('Invalid current time')
|
||||||
|
}
|
||||||
|
|
||||||
|
const mediaItemShare = ShareManager.findBySlug(slug)
|
||||||
|
if (!mediaItemShare) {
|
||||||
|
return res.status(404)
|
||||||
|
}
|
||||||
|
|
||||||
|
const playbackSession = ShareManager.findPlaybackSessionBySessionId(req.cookies.share_session_id)
|
||||||
|
if (!playbackSession || playbackSession.mediaItemShareId !== mediaItemShare.id) {
|
||||||
|
res.clearCookie('share_session_id')
|
||||||
|
return res.status(404).send('Share session not found')
|
||||||
|
}
|
||||||
|
|
||||||
|
playbackSession.currentTime = Math.min(currentTime, playbackSession.duration)
|
||||||
|
Logger.debug(`[ShareController] Update share playback session ${req.cookies.share_session_id} currentTime: ${playbackSession.currentTime}`)
|
||||||
|
res.sendStatus(204)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -12,12 +12,23 @@ class ShareManager {
|
|||||||
constructor() {
|
constructor() {
|
||||||
/** @type {OpenMediaItemShareObject[]} */
|
/** @type {OpenMediaItemShareObject[]} */
|
||||||
this.openMediaItemShares = []
|
this.openMediaItemShares = []
|
||||||
|
|
||||||
|
/** @type {import('../objects/PlaybackSession')[]} */
|
||||||
|
this.openSharePlaybackSessions = []
|
||||||
}
|
}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
this.loadMediaItemShares()
|
this.loadMediaItemShares()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {import('../objects/PlaybackSession')} playbackSession
|
||||||
|
*/
|
||||||
|
addOpenSharePlaybackSession(playbackSession) {
|
||||||
|
Logger.info(`[ShareManager] Adding new open share playback session ${playbackSession.shareSessionId}`)
|
||||||
|
this.openSharePlaybackSessions.push(playbackSession)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find an open media item share by media item ID
|
* Find an open media item share by media item ID
|
||||||
* @param {string} mediaItemId
|
* @param {string} mediaItemId
|
||||||
@ -52,6 +63,14 @@ class ShareManager {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} shareSessionId
|
||||||
|
* @returns {import('../objects/PlaybackSession')}
|
||||||
|
*/
|
||||||
|
findPlaybackSessionBySessionId(shareSessionId) {
|
||||||
|
return this.openSharePlaybackSessions.find((s) => s.shareSessionId === shareSessionId)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load all media item shares from the database
|
* Load all media item shares from the database
|
||||||
* Remove expired & schedule active
|
* Remove expired & schedule active
|
||||||
@ -123,6 +142,7 @@ class ShareManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.openMediaItemShares = this.openMediaItemShares.filter((s) => s.id !== mediaItemShareId)
|
this.openMediaItemShares = this.openMediaItemShares.filter((s) => s.id !== mediaItemShareId)
|
||||||
|
this.openSharePlaybackSessions = this.openSharePlaybackSessions.filter((s) => s.mediaItemShareId !== mediaItemShareId)
|
||||||
await this.destroyMediaItemShare(mediaItemShareId)
|
await this.destroyMediaItemShare(mediaItemShareId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,14 +67,20 @@ class MediaItemShare extends Model {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
model: this.sequelize.models.libraryItem
|
model: this.sequelize.models.libraryItem,
|
||||||
|
include: {
|
||||||
|
model: this.sequelize.models.library,
|
||||||
|
attributes: ['settings']
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
const libraryItem = book.libraryItem
|
const libraryItem = book.libraryItem
|
||||||
libraryItem.media = book
|
libraryItem.media = book
|
||||||
delete book.libraryItem
|
delete book.libraryItem
|
||||||
return this.sequelize.models.libraryItem.getOldLibraryItem(libraryItem)
|
const oldLibraryItem = this.sequelize.models.libraryItem.getOldLibraryItem(libraryItem)
|
||||||
|
oldLibraryItem.librarySettings = libraryItem.library.settings
|
||||||
|
return oldLibraryItem
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
@ -43,6 +43,10 @@ class PlaybackSession {
|
|||||||
this.audioTracks = []
|
this.audioTracks = []
|
||||||
this.videoTrack = null
|
this.videoTrack = null
|
||||||
this.stream = null
|
this.stream = null
|
||||||
|
// Used for share sessions
|
||||||
|
this.shareSessionId = null
|
||||||
|
this.mediaItemShareId = null
|
||||||
|
this.coverAspectRatio = null
|
||||||
|
|
||||||
if (session) {
|
if (session) {
|
||||||
this.construct(session)
|
this.construct(session)
|
||||||
|
@ -10,7 +10,9 @@ class PublicRouter {
|
|||||||
|
|
||||||
init() {
|
init() {
|
||||||
this.router.get('/share/:slug', ShareController.getMediaItemShareBySlug.bind(this))
|
this.router.get('/share/:slug', ShareController.getMediaItemShareBySlug.bind(this))
|
||||||
this.router.get('/share/:slug/file/:fileid', ShareController.getMediaItemShareFile.bind(this))
|
this.router.get('/share/:slug/track/:index', ShareController.getMediaItemShareAudioTrack.bind(this))
|
||||||
|
this.router.get('/share/:slug/cover', ShareController.getMediaItemShareCoverImage.bind(this))
|
||||||
|
this.router.patch('/share/:slug/progress', ShareController.updateMediaItemShareProgress.bind(this))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
module.exports = PublicRouter
|
module.exports = PublicRouter
|
||||||
|
Loading…
Reference in New Issue
Block a user