Merge pull request #1231 from k9withabone/server/respond-with-objects

Server respond with objects
This commit is contained in:
advplyr 2022-12-12 17:53:57 -06:00 committed by GitHub
commit 05d10b73c3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 127 additions and 89 deletions

View File

@ -122,7 +122,7 @@ export default {
return this.$store.state.globals.selectedMediaItems return this.$store.state.globals.selectedMediaItems
}, },
selectedMediaItemsArePlayable() { selectedMediaItemsArePlayable() {
return !this.selectedMediaItems.some(i => !i.hasTracks) return !this.selectedMediaItems.some((i) => !i.hasTracks)
}, },
userMediaProgress() { userMediaProgress() {
return this.$store.state.user.user.mediaProgress || [] return this.$store.state.user.user.mediaProgress || []
@ -164,12 +164,15 @@ export default {
this.$store.commit('setProcessingBatch', true) this.$store.commit('setProcessingBatch', true)
const libraryItemIds = this.selectedMediaItems.map((i) => i.id) const libraryItemIds = this.selectedMediaItems.map((i) => i.id)
const libraryItems = await this.$axios.$post(`/api/items/batch/get`, { libraryItemIds }).catch((error) => { const libraryItems = await this.$axios
const errorMsg = error.response.data || 'Failed to get items' .$post(`/api/items/batch/get`, { libraryItemIds })
console.error(errorMsg, error) .then((res) => res.libraryItems)
this.$toast.error(errorMsg) .catch((error) => {
return [] const errorMsg = error.response.data || 'Failed to get items'
}) console.error(errorMsg, error)
this.$toast.error(errorMsg)
return []
})
if (!libraryItems.length) { if (!libraryItems.length) {
this.$store.commit('setProcessingBatch', false) this.$store.commit('setProcessingBatch', false)

View File

@ -201,8 +201,8 @@ export default {
this.loadingTags = true this.loadingTags = true
this.$axios this.$axios
.$get(`/api/tags`) .$get(`/api/tags`)
.then((tags) => { .then((res) => {
this.tags = tags this.tags = res.tags
this.loadingTags = false this.loadingTags = false
}) })
.catch((error) => { .catch((error) => {

View File

@ -303,11 +303,14 @@ export default {
this.persistProvider() this.persistProvider()
this.isProcessing = true this.isProcessing = true
var searchQuery = this.getSearchQuery() const searchQuery = this.getSearchQuery()
var results = await this.$axios.$get(`/api/search/covers?${searchQuery}`).catch((error) => { const results = await this.$axios
console.error('Failed', error) .$get(`/api/search/covers?${searchQuery}`)
return [] .then((res) => res.results)
}) .catch((error) => {
console.error('Failed', error)
return []
})
this.coversFound = results this.coversFound = results
this.isProcessing = false this.isProcessing = false
this.hasSearched = true this.hasSearched = true

View File

@ -306,13 +306,13 @@ export default {
this.runSearch() this.runSearch()
}, },
async runSearch() { async runSearch() {
var searchQuery = this.getSearchQuery() const searchQuery = this.getSearchQuery()
if (this.lastSearch === searchQuery) return if (this.lastSearch === searchQuery) return
this.searchResults = [] this.searchResults = []
this.isProcessing = true this.isProcessing = true
this.lastSearch = searchQuery this.lastSearch = searchQuery
var searchEntity = this.isPodcast ? 'podcast' : 'books' const searchEntity = this.isPodcast ? 'podcast' : 'books'
var results = await this.$axios.$get(`/api/search/${searchEntity}?${searchQuery}`, { timeout: 20000 }).catch((error) => { let results = await this.$axios.$get(`/api/search/${searchEntity}?${searchQuery}`, { timeout: 20000 }).catch((error) => {
console.error('Failed', error) console.error('Failed', error)
return [] return []
}) })

View File

@ -109,8 +109,8 @@ export default {
loadUsers() { loadUsers() {
this.$axios this.$axios
.$get('/api/users') .$get('/api/users')
.then((users) => { .then((res) => {
this.users = users.sort((a, b) => { this.users = res.users.sort((a, b) => {
return a.createdAt - b.createdAt return a.createdAt - b.createdAt
}) })
}) })

View File

@ -82,10 +82,10 @@ export default {
}) })
var newOrder = libraryOrderData.map((lib) => lib.id).join(',') var newOrder = libraryOrderData.map((lib) => lib.id).join(',')
if (currOrder !== newOrder) { if (currOrder !== newOrder) {
this.$axios.$post('/api/libraries/order', libraryOrderData).then((libraries) => { this.$axios.$post('/api/libraries/order', libraryOrderData).then((response) => {
if (libraries && libraries.length) { if (response.libraries && response.libraries.length) {
this.$toast.success('Library order saved', { timeout: 1500 }) this.$toast.success('Library order saved', { timeout: 1500 })
this.$store.commit('libraries/set', libraries) this.$store.commit('libraries/set', response.libraries)
} }
}) })
} }

View File

@ -96,11 +96,14 @@ export default {
} }
const libraryItemIds = store.state.globals.selectedMediaItems.map((i) => i.id) const libraryItemIds = store.state.globals.selectedMediaItems.map((i) => i.id)
const libraryItems = await app.$axios.$post(`/api/items/batch/get`, { libraryItemIds }).catch((error) => { const libraryItems = await app.$axios
const errorMsg = error.response.data || 'Failed to get items' .$post(`/api/items/batch/get`, { libraryItemIds })
console.error(errorMsg, error) .then((res) => res.libraryItems)
return [] .catch((error) => {
}) const errorMsg = error.response.data || 'Failed to get items'
console.error(errorMsg, error)
return []
})
return { return {
mediaType: libraryItems[0].mediaType, mediaType: libraryItems[0].mediaType,
libraryItems libraryItems

View File

@ -61,10 +61,10 @@
<script> <script>
export default { export default {
async asyncData({ params, redirect, app }) { async asyncData({ params, redirect, app }) {
var users = await app.$axios const users = await app.$axios
.$get('/api/users') .$get('/api/users')
.then((users) => { .then((res) => {
return users.sort((a, b) => { return res.users.sort((a, b) => {
return a.createdAt - b.createdAt return a.createdAt - b.createdAt
}) })
}) })

View File

@ -48,10 +48,13 @@ export default {
}, },
methods: { methods: {
async init() { async init() {
this.authors = await this.$axios.$get(`/api/libraries/${this.currentLibraryId}/authors`).catch((error) => { this.authors = await this.$axios
console.error('Failed to load authors', error) .$get(`/api/libraries/${this.currentLibraryId}/authors`)
return [] .then((response) => response.authors)
}) .catch((error) => {
console.error('Failed to load authors', error)
return []
})
this.loading = false this.loading = false
}, },
authorAdded(author) { authorAdded(author) {

View File

@ -86,8 +86,8 @@ export const actions = {
.$get('/api/filesystem') .$get('/api/filesystem')
.then((res) => { .then((res) => {
console.log('Settings folders', res) console.log('Settings folders', res)
commit('setFolders', res) commit('setFolders', res.directories)
return res return res.directories
}) })
.catch((error) => { .catch((error) => {
console.error('Failed to load dirs', error) console.error('Failed to load dirs', error)
@ -151,7 +151,7 @@ export const actions = {
this.$axios this.$axios
.$get(`/api/libraries`) .$get(`/api/libraries`)
.then((data) => { .then((data) => {
commit('set', data) commit('set', data.libraries)
commit('setLastLoad') commit('setLastLoad')
}) })
.catch((error) => { .catch((error) => {

View File

@ -148,7 +148,9 @@ class AuthorController {
var limit = (req.query.limit && !isNaN(req.query.limit)) ? Number(req.query.limit) : 25 var limit = (req.query.limit && !isNaN(req.query.limit)) ? Number(req.query.limit) : 25
var authors = this.db.authors.filter(au => au.name.toLowerCase().includes(q)) var authors = this.db.authors.filter(au => au.name.toLowerCase().includes(q))
authors = authors.slice(0, limit) authors = authors.slice(0, limit)
res.json(authors) res.json({
results: authors
})
} }
async match(req, res) { async match(req, res) {

View File

@ -20,8 +20,9 @@ class CollectionController {
} }
findAll(req, res) { findAll(req, res) {
var expandedCollections = this.db.collections.map(c => c.toJSONExpanded(this.db.libraryItems)) res.json({
res.json(expandedCollections) collections: this.db.collections.map(c => c.toJSONExpanded(this.db.libraryItems))
})
} }
findOne(req, res) { findOne(req, res) {

View File

@ -19,8 +19,9 @@ class FileSystemController {
}) })
Logger.debug(`[Server] get file system paths, excluded: ${excludedDirs.join(', ')}`) Logger.debug(`[Server] get file system paths, excluded: ${excludedDirs.join(', ')}`)
var dirs = await this.getDirectories(global.appRoot, '/', excludedDirs) res.json({
res.json(dirs) directories: await this.getDirectories(global.appRoot, '/', excludedDirs)
})
} }
} }
module.exports = new FileSystemController() module.exports = new FileSystemController()

View File

@ -62,7 +62,9 @@ class LibraryController {
return res.json(this.db.libraries.filter(lib => librariesAccessible.includes(lib.id)).map(lib => lib.toJSON())) return res.json(this.db.libraries.filter(lib => librariesAccessible.includes(lib.id)).map(lib => lib.toJSON()))
} }
res.json(this.db.libraries.map(lib => lib.toJSON())) res.json({
libraries: this.db.libraries.map(lib => lib.toJSON())
})
} }
async findOne(req, res) { async findOne(req, res) {
@ -496,8 +498,9 @@ class LibraryController {
Logger.debug(`[LibraryController] Library orders were up to date`) Logger.debug(`[LibraryController] Library orders were up to date`)
} }
var libraries = this.db.libraries.map(lib => lib.toJSON()) res.json({
res.json(libraries) libraries: this.db.libraries.map(lib => lib.toJSON())
})
} }
// GET: Global library search // GET: Global library search
@ -603,7 +606,9 @@ class LibraryController {
} }
}) })
res.json(naturalSort(Object.values(authors)).asc(au => au.name)) res.json({
authors: naturalSort(Object.values(authors)).asc(au => au.name)
})
} }
async matchAll(req, res) { async matchAll(req, res) {

View File

@ -308,16 +308,18 @@ class LibraryItemController {
// POST: api/items/batch/get // POST: api/items/batch/get
async batchGet(req, res) { async batchGet(req, res) {
var libraryItemIds = req.body.libraryItemIds || [] const libraryItemIds = req.body.libraryItemIds || []
if (!libraryItemIds.length) { if (!libraryItemIds.length) {
return res.status(403).send('Invalid payload') return res.status(403).send('Invalid payload')
} }
var libraryItems = [] const libraryItems = []
libraryItemIds.forEach((lid) => { libraryItemIds.forEach((lid) => {
const li = this.db.libraryItems.find(_li => _li.id === lid) const li = this.db.libraryItems.find(_li => _li.id === lid)
if (li) libraryItems.push(li.toJSONExpanded()) if (li) libraryItems.push(li.toJSONExpanded())
}) })
res.json(libraryItems) res.json({
libraryItems
})
} }
// POST: api/items/batch/quickmatch // POST: api/items/batch/quickmatch

View File

@ -137,7 +137,9 @@ class MiscController {
}) })
} }
}) })
res.json(tags) res.json({
tags: tags
})
} }
validateCronExpression(req, res) { validateCronExpression(req, res) {

View File

@ -4,15 +4,15 @@ class SearchController {
constructor() { } constructor() { }
async findBooks(req, res) { async findBooks(req, res) {
var provider = req.query.provider || 'google' const provider = req.query.provider || 'google'
var title = req.query.title || '' const title = req.query.title || ''
var author = req.query.author || '' const author = req.query.author || ''
var results = await this.bookFinder.search(provider, title, author) const results = await this.bookFinder.search(provider, title, author)
res.json(results) res.json(results)
} }
async findCovers(req, res) { async findCovers(req, res) {
var query = req.query const query = req.query
const podcast = query.podcast == 1 const podcast = query.podcast == 1
if (!query.title) { if (!query.title) {
@ -20,28 +20,30 @@ class SearchController {
return res.sendStatus(400) return res.sendStatus(400)
} }
var result = null let results = null
if (podcast) result = await this.podcastFinder.findCovers(query.title) if (podcast) results = await this.podcastFinder.findCovers(query.title)
else result = await this.bookFinder.findCovers(query.provider || 'google', query.title, query.author || null) else results = await this.bookFinder.findCovers(query.provider || 'google', query.title, query.author || null)
res.json(result) res.json({
results
})
} }
async findPodcasts(req, res) { async findPodcasts(req, res) {
var term = req.query.term const term = req.query.term
var results = await this.podcastFinder.search(term) const results = await this.podcastFinder.search(term)
res.json(results) res.json(results)
} }
async findAuthor(req, res) { async findAuthor(req, res) {
var query = req.query.q const query = req.query.q
var author = await this.authorFinder.findAuthorByName(query) const author = await this.authorFinder.findAuthorByName(query)
res.json(author) res.json(author)
} }
async findChapters(req, res) { async findChapters(req, res) {
var asin = req.query.asin const asin = req.query.asin
var region = (req.query.region || 'us').toLowerCase() const region = (req.query.region || 'us').toLowerCase()
var chapterData = await this.bookFinder.findChapters(asin, region) const chapterData = await this.bookFinder.findChapters(asin, region)
if (!chapterData) { if (!chapterData) {
return res.json({ error: 'Chapters not found' }) return res.json({ error: 'Chapters not found' })
} }

View File

@ -32,7 +32,9 @@ class SeriesController {
var limit = (req.query.limit && !isNaN(req.query.limit)) ? Number(req.query.limit) : 25 var limit = (req.query.limit && !isNaN(req.query.limit)) ? Number(req.query.limit) : 25
var series = this.db.series.filter(se => se.name.toLowerCase().includes(q)) var series = this.db.series.filter(se => se.name.toLowerCase().includes(q))
series = series.slice(0, limit) series = series.slice(0, limit)
res.json(series) res.json({
results: series
})
} }
async update(req, res) { async update(req, res) {

View File

@ -12,7 +12,9 @@ class UserController {
if (!req.user.isAdminOrUp) return res.sendStatus(403) if (!req.user.isAdminOrUp) return res.sendStatus(403)
const hideRootToken = !req.user.isRoot const hideRootToken = !req.user.isRoot
const users = this.db.users.map(u => this.userJsonWithItemProgressDetails(u, hideRootToken)) const users = this.db.users.map(u => this.userJsonWithItemProgressDetails(u, hideRootToken))
res.json(users) res.json({
users: users
})
} }
findOne(req, res) { findOne(req, res) {

View File

@ -31,7 +31,7 @@ class PlaybackSessionManager {
return this.sessions.find(s => s.userId === userId) return this.sessions.find(s => s.userId === userId)
} }
getStream(sessionId) { getStream(sessionId) {
var session = this.getSession(sessionId) const session = this.getSession(sessionId)
return session ? session.stream : null return session ? session.stream : null
} }
@ -54,7 +54,7 @@ class PlaybackSessionManager {
} }
async syncSessionRequest(user, session, payload, res) { async syncSessionRequest(user, session, payload, res) {
var result = await this.syncSession(user, session, payload) const result = await this.syncSession(user, session, payload)
if (result) { if (result) {
res.json(session.toJSONForClient(result.libraryItem)) res.json(session.toJSONForClient(result.libraryItem))
} }
@ -66,7 +66,7 @@ class PlaybackSessionManager {
return res.status(500).send('Local session is locked and already syncing') return res.status(500).send('Local session is locked and already syncing')
} }
var libraryItem = this.db.getLibraryItem(sessionJson.libraryItemId) const libraryItem = this.db.getLibraryItem(sessionJson.libraryItemId)
if (!libraryItem) { if (!libraryItem) {
Logger.error(`[PlaybackSessionManager] syncLocalSessionRequest: Library item not found for session "${sessionJson.libraryItemId}"`) Logger.error(`[PlaybackSessionManager] syncLocalSessionRequest: Library item not found for session "${sessionJson.libraryItemId}"`)
return res.status(500).send('Library item not found') return res.status(500).send('Library item not found')
@ -74,7 +74,7 @@ class PlaybackSessionManager {
this.localSessionLock[sessionJson.id] = true // Lock local session this.localSessionLock[sessionJson.id] = true // Lock local session
var session = await this.db.getPlaybackSession(sessionJson.id) let session = await this.db.getPlaybackSession(sessionJson.id)
if (!session) { if (!session) {
// New session from local // New session from local
session = new PlaybackSession(sessionJson) session = new PlaybackSession(sessionJson)
@ -96,10 +96,10 @@ class PlaybackSessionManager {
progress: session.progress, progress: session.progress,
lastUpdate: session.updatedAt // Keep media progress update times the same as local lastUpdate: session.updatedAt // Keep media progress update times the same as local
} }
var wasUpdated = user.createUpdateMediaProgress(libraryItem, itemProgressUpdate, session.episodeId) const wasUpdated = user.createUpdateMediaProgress(libraryItem, itemProgressUpdate, session.episodeId)
if (wasUpdated) { if (wasUpdated) {
await this.db.updateEntity('user', user) await this.db.updateEntity('user', user)
var itemProgress = user.getMediaProgress(session.libraryItemId, session.episodeId) const itemProgress = user.getMediaProgress(session.libraryItemId, session.episodeId)
SocketAuthority.clientEmitter(user.id, 'user_item_progress_updated', { SocketAuthority.clientEmitter(user.id, 'user_item_progress_updated', {
id: itemProgress.id, id: itemProgress.id,
data: itemProgress.toJSON() data: itemProgress.toJSON()
@ -118,18 +118,25 @@ class PlaybackSessionManager {
async startSession(user, deviceInfo, libraryItem, episodeId, options) { async startSession(user, deviceInfo, libraryItem, episodeId, options) {
// Close any sessions already open for user // Close any sessions already open for user
var userSessions = this.sessions.filter(playbackSession => playbackSession.userId === user.id) const userSessions = this.sessions.filter(playbackSession => playbackSession.userId === user.id)
for (const session of userSessions) { for (const session of userSessions) {
Logger.info(`[PlaybackSessionManager] startSession: Closing open session "${session.displayTitle}" for user "${user.username}"`) Logger.info(`[PlaybackSessionManager] startSession: Closing open session "${session.displayTitle}" for user "${user.username}"`)
await this.closeSession(user, session, null) await this.closeSession(user, session, null)
} }
var shouldDirectPlay = options.forceDirectPlay || (!options.forceTranscode && libraryItem.media.checkCanDirectPlay(options, episodeId)) const shouldDirectPlay = options.forceDirectPlay || (!options.forceTranscode && libraryItem.media.checkCanDirectPlay(options, episodeId))
var mediaPlayer = options.mediaPlayer || 'unknown' const mediaPlayer = options.mediaPlayer || 'unknown'
const userProgress = user.getMediaProgress(libraryItem.id, episodeId) const userProgress = user.getMediaProgress(libraryItem.id, episodeId)
var userStartTime = 0 let userStartTime = 0
if (userProgress) userStartTime = Number.parseFloat(userProgress.currentTime) || 0 if (userProgress) {
if (userProgress.isFinished) {
Logger.info(`[PlaybackSessionManager] Starting session for user "${user.username}" and resetting progress for finished item "${libraryItem.media.metadata.title}"`)
// Keep userStartTime as 0 so the client restarts the media
} else {
userStartTime = Number.parseFloat(userProgress.currentTime) || 0
}
}
const newPlaybackSession = new PlaybackSession() const newPlaybackSession = new PlaybackSession()
newPlaybackSession.setData(libraryItem, user, mediaPlayer, deviceInfo, userStartTime, episodeId) newPlaybackSession.setData(libraryItem, user, mediaPlayer, deviceInfo, userStartTime, episodeId)
@ -142,14 +149,14 @@ class PlaybackSessionManager {
// HLS not supported for video yet // HLS not supported for video yet
} }
} else { } else {
var audioTracks = [] let audioTracks = []
if (shouldDirectPlay) { if (shouldDirectPlay) {
Logger.debug(`[PlaybackSessionManager] "${user.username}" starting direct play session for item "${libraryItem.id}"`) Logger.debug(`[PlaybackSessionManager] "${user.username}" starting direct play session for item "${libraryItem.id}"`)
audioTracks = libraryItem.getDirectPlayTracklist(episodeId) audioTracks = libraryItem.getDirectPlayTracklist(episodeId)
newPlaybackSession.playMethod = PlayMethod.DIRECTPLAY newPlaybackSession.playMethod = PlayMethod.DIRECTPLAY
} else { } else {
Logger.debug(`[PlaybackSessionManager] "${user.username}" starting stream session for item "${libraryItem.id}"`) Logger.debug(`[PlaybackSessionManager] "${user.username}" starting stream session for item "${libraryItem.id}"`)
var stream = new Stream(newPlaybackSession.id, this.StreamsPath, user, libraryItem, episodeId, userStartTime) const stream = new Stream(newPlaybackSession.id, this.StreamsPath, user, libraryItem, episodeId, userStartTime)
await stream.generatePlaylist() await stream.generatePlaylist()
stream.start() // Start transcode stream.start() // Start transcode
@ -175,7 +182,7 @@ class PlaybackSessionManager {
} }
async syncSession(user, session, syncData) { async syncSession(user, session, syncData) {
var libraryItem = this.db.libraryItems.find(li => li.id === session.libraryItemId) const libraryItem = this.db.libraryItems.find(li => li.id === session.libraryItemId)
if (!libraryItem) { if (!libraryItem) {
Logger.error(`[PlaybackSessionManager] syncSession Library Item not found "${session.libraryItemId}"`) Logger.error(`[PlaybackSessionManager] syncSession Library Item not found "${session.libraryItemId}"`)
return null return null
@ -190,11 +197,11 @@ class PlaybackSessionManager {
currentTime: syncData.currentTime, currentTime: syncData.currentTime,
progress: session.progress progress: session.progress
} }
var wasUpdated = user.createUpdateMediaProgress(libraryItem, itemProgressUpdate, session.episodeId) const wasUpdated = user.createUpdateMediaProgress(libraryItem, itemProgressUpdate, session.episodeId)
if (wasUpdated) { if (wasUpdated) {
await this.db.updateEntity('user', user) await this.db.updateEntity('user', user)
var itemProgress = user.getMediaProgress(session.libraryItemId, session.episodeId) const itemProgress = user.getMediaProgress(session.libraryItemId, session.episodeId)
SocketAuthority.clientEmitter(user.id, 'user_item_progress_updated', { SocketAuthority.clientEmitter(user.id, 'user_item_progress_updated', {
id: itemProgress.id, id: itemProgress.id,
data: itemProgress.toJSON() data: itemProgress.toJSON()
@ -229,7 +236,7 @@ class PlaybackSessionManager {
} }
async removeSession(sessionId) { async removeSession(sessionId) {
var session = this.sessions.find(s => s.id === sessionId) const session = this.sessions.find(s => s.id === sessionId)
if (!session) return if (!session) return
if (session.stream) { if (session.stream) {
await session.stream.close() await session.stream.close()
@ -242,13 +249,13 @@ class PlaybackSessionManager {
async removeOrphanStreams() { async removeOrphanStreams() {
await fs.ensureDir(this.StreamsPath) await fs.ensureDir(this.StreamsPath)
try { try {
var streamsInPath = await fs.readdir(this.StreamsPath) const streamsInPath = await fs.readdir(this.StreamsPath)
for (let i = 0; i < streamsInPath.length; i++) { for (let i = 0; i < streamsInPath.length; i++) {
var streamId = streamsInPath[i] const streamId = streamsInPath[i]
if (streamId.startsWith('play_')) { // Make sure to only remove folders that are a stream if (streamId.startsWith('play_')) { // Make sure to only remove folders that are a stream
var session = this.sessions.find(se => se.id === streamId) const session = this.sessions.find(se => se.id === streamId)
if (!session) { if (!session) {
var streamPath = Path.join(this.StreamsPath, streamId) const streamPath = Path.join(this.StreamsPath, streamId)
Logger.debug(`[PlaybackSessionManager] Removing orphan stream "${streamPath}"`) Logger.debug(`[PlaybackSessionManager] Removing orphan stream "${streamPath}"`)
await fs.remove(streamPath) await fs.remove(streamPath)
} }