mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-02-01 00:18:14 +01:00
Update:Paginated listening sessions
This commit is contained in:
parent
0e1692d26b
commit
3171ce5aba
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<button class="icon-btn rounded-md flex items-center justify-center h-9 w-9 relative" @mousedown.prevent :disabled="disabled || loading" :class="className" @click="clickBtn">
|
<button class="icon-btn rounded-md flex items-center justify-center relative" @mousedown.prevent :disabled="disabled || loading" :class="className" @click="clickBtn">
|
||||||
<div v-if="loading" class="text-white absolute top-0 left-0 w-full h-full flex items-center justify-center text-opacity-100">
|
<div v-if="loading" class="text-white absolute top-0 left-0 w-full h-full flex items-center justify-center text-opacity-100">
|
||||||
<svg class="animate-spin" style="width: 24px; height: 24px" viewBox="0 0 24 24">
|
<svg class="animate-spin" style="width: 24px; height: 24px" viewBox="0 0 24 24">
|
||||||
<path fill="currentColor" d="M12,4V2A10,10 0 0,0 2,12H4A8,8 0 0,1 12,4Z" />
|
<path fill="currentColor" d="M12,4V2A10,10 0 0,0 2,12H4A8,8 0 0,1 12,4Z" />
|
||||||
@ -20,20 +20,29 @@ export default {
|
|||||||
},
|
},
|
||||||
outlined: Boolean,
|
outlined: Boolean,
|
||||||
borderless: Boolean,
|
borderless: Boolean,
|
||||||
loading: Boolean
|
loading: Boolean,
|
||||||
|
iconFontSize: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
type: Number,
|
||||||
|
default: 9
|
||||||
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {}
|
return {}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
className() {
|
className() {
|
||||||
var classes = []
|
var classes = [`h-${this.size} w-${this.size}`]
|
||||||
if (!this.borderless) {
|
if (!this.borderless) {
|
||||||
classes.push(`bg-${this.bgColor} border border-gray-600`)
|
classes.push(`bg-${this.bgColor} border border-gray-600`)
|
||||||
}
|
}
|
||||||
return classes.join(' ')
|
return classes.join(' ')
|
||||||
},
|
},
|
||||||
fontSize() {
|
fontSize() {
|
||||||
|
if (this.iconFontSize) return this.iconFontSize
|
||||||
if (this.icon === 'edit') return '1.25rem'
|
if (this.icon === 'edit') return '1.25rem'
|
||||||
return '1.4rem'
|
return '1.4rem'
|
||||||
}
|
}
|
||||||
|
@ -138,7 +138,9 @@ export default {
|
|||||||
this.$copyToClipboard(str, this)
|
this.$copyToClipboard(str, this)
|
||||||
},
|
},
|
||||||
async init() {
|
async init() {
|
||||||
this.listeningSessions = await this.$axios.$get(`/api/users/${this.user.id}/listening-sessions`).catch((err) => {
|
this.listeningSessions = await this.$axios.$get(`/api/users/${this.user.id}/listening-sessions?page=0&itemsPerPage=10`).then((data) => {
|
||||||
|
return data.sessions || []
|
||||||
|
}).catch((err) => {
|
||||||
console.error('Failed to load listening sesions', err)
|
console.error('Failed to load listening sesions', err)
|
||||||
return []
|
return []
|
||||||
})
|
})
|
||||||
|
@ -18,7 +18,8 @@
|
|||||||
|
|
||||||
<div class="py-2">
|
<div class="py-2">
|
||||||
<h1 class="text-lg mb-2 text-white text-opacity-90 px-2 sm:px-0">Listening Sessions ({{ listeningSessions.length }})</h1>
|
<h1 class="text-lg mb-2 text-white text-opacity-90 px-2 sm:px-0">Listening Sessions ({{ listeningSessions.length }})</h1>
|
||||||
<table v-if="listeningSessions.length" class="userSessionsTable">
|
<div v-if="listeningSessions.length">
|
||||||
|
<table class="userSessionsTable">
|
||||||
<tr class="bg-primary bg-opacity-40">
|
<tr class="bg-primary bg-opacity-40">
|
||||||
<th class="flex-grow text-left">Item</th>
|
<th class="flex-grow text-left">Item</th>
|
||||||
<th class="w-32 text-left hidden md:table-cell">Play Method</th>
|
<th class="w-32 text-left hidden md:table-cell">Play Method</th>
|
||||||
@ -51,6 +52,12 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
<div class="flex items-center justify-end py-1">
|
||||||
|
<ui-icon-btn icon="arrow_back_ios_new" :size="7" icon-font-size="1rem" class="mx-1" :disabled="currentPage === 0" @click="prevPage" />
|
||||||
|
<p class="text-sm mx-1">Page {{ currentPage + 1 }} of {{ numPages }}</p>
|
||||||
|
<ui-icon-btn icon="arrow_forward_ios" :size="7" icon-font-size="1rem" class="mx-1" :disabled="currentPage >= numPages - 1" @click="nextPage" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<p v-else class="text-white text-opacity-50">No sessions yet...</p>
|
<p v-else class="text-white text-opacity-50">No sessions yet...</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -75,7 +82,10 @@ export default {
|
|||||||
return {
|
return {
|
||||||
showSessionModal: false,
|
showSessionModal: false,
|
||||||
selectedSession: null,
|
selectedSession: null,
|
||||||
listeningSessions: []
|
listeningSessions: [],
|
||||||
|
numPages: 0,
|
||||||
|
total: 0,
|
||||||
|
currentPage: 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@ -87,6 +97,12 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
prevPage() {
|
||||||
|
this.loadSessions(this.currentPage - 1)
|
||||||
|
},
|
||||||
|
nextPage() {
|
||||||
|
this.loadSessions(this.currentPage + 1)
|
||||||
|
},
|
||||||
showSession(session) {
|
showSession(session) {
|
||||||
this.selectedSession = session
|
this.selectedSession = session
|
||||||
this.showSessionModal = true
|
this.showSessionModal = true
|
||||||
@ -108,13 +124,23 @@ export default {
|
|||||||
else if (playMethod === this.$constants.PlayMethod.LOCAL) return 'Local'
|
else if (playMethod === this.$constants.PlayMethod.LOCAL) return 'Local'
|
||||||
return 'Unknown'
|
return 'Unknown'
|
||||||
},
|
},
|
||||||
async init() {
|
async loadSessions(page) {
|
||||||
console.log(navigator)
|
const data = await this.$axios.$get(`/api/users/${this.user.id}/listening-sessions?page=${page}&itemsPerPage=10`).catch((err) => {
|
||||||
|
|
||||||
this.listeningSessions = await this.$axios.$get(`/api/users/${this.user.id}/listening-sessions`).catch((err) => {
|
|
||||||
console.error('Failed to load listening sesions', err)
|
console.error('Failed to load listening sesions', err)
|
||||||
return []
|
return null
|
||||||
})
|
})
|
||||||
|
if (!data) {
|
||||||
|
this.$toast.error('Failed to load listening sessions')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.numPages = data.numPages
|
||||||
|
this.total = data.total
|
||||||
|
this.currentPage = data.page
|
||||||
|
this.listeningSessions = data.sessions
|
||||||
|
},
|
||||||
|
init() {
|
||||||
|
this.loadSessions(0)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
const Logger = require('../Logger')
|
const Logger = require('../Logger')
|
||||||
const { isObject } = require('../utils/index')
|
const { isObject, toNumber } = require('../utils/index')
|
||||||
|
|
||||||
class MeController {
|
class MeController {
|
||||||
constructor() { }
|
constructor() { }
|
||||||
@ -7,7 +7,22 @@ class MeController {
|
|||||||
// GET: api/me/listening-sessions
|
// GET: api/me/listening-sessions
|
||||||
async getListeningSessions(req, res) {
|
async getListeningSessions(req, res) {
|
||||||
var listeningSessions = await this.getUserListeningSessionsHelper(req.user.id)
|
var listeningSessions = await this.getUserListeningSessionsHelper(req.user.id)
|
||||||
res.json(listeningSessions.slice(0, 10))
|
|
||||||
|
const itemsPerPage = toNumber(req.query.itemsPerPage, 10) || 10
|
||||||
|
const page = toNumber(req.query.page, 0)
|
||||||
|
|
||||||
|
const start = page * itemsPerPage
|
||||||
|
const sessions = listeningSessions.slice(start, start + itemsPerPage)
|
||||||
|
|
||||||
|
const payload = {
|
||||||
|
total: listeningSessions.length,
|
||||||
|
numPages: Math.ceil(listeningSessions.length / itemsPerPage),
|
||||||
|
page,
|
||||||
|
itemsPerPage,
|
||||||
|
sessions
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json(payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GET: api/me/listening-stats
|
// GET: api/me/listening-stats
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
const Logger = require('../Logger')
|
const Logger = require('../Logger')
|
||||||
const User = require('../objects/user/User')
|
const User = require('../objects/user/User')
|
||||||
|
|
||||||
const { getId } = require('../utils/index')
|
const { getId, toNumber } = require('../utils/index')
|
||||||
|
|
||||||
class UserController {
|
class UserController {
|
||||||
constructor() { }
|
constructor() { }
|
||||||
@ -142,8 +142,24 @@ class UserController {
|
|||||||
if (!req.user.isAdminOrUp && req.user.id !== req.params.id) {
|
if (!req.user.isAdminOrUp && req.user.id !== req.params.id) {
|
||||||
return res.sendStatus(403)
|
return res.sendStatus(403)
|
||||||
}
|
}
|
||||||
|
|
||||||
var listeningSessions = await this.getUserListeningSessionsHelper(req.params.id)
|
var listeningSessions = await this.getUserListeningSessionsHelper(req.params.id)
|
||||||
res.json(listeningSessions.slice(0, 10))
|
|
||||||
|
const itemsPerPage = toNumber(req.query.itemsPerPage, 10) || 10
|
||||||
|
const page = toNumber(req.query.page, 0)
|
||||||
|
|
||||||
|
const start = page * itemsPerPage
|
||||||
|
const sessions = listeningSessions.slice(start, start + itemsPerPage)
|
||||||
|
|
||||||
|
const payload = {
|
||||||
|
total: listeningSessions.length,
|
||||||
|
numPages: Math.ceil(listeningSessions.length / itemsPerPage),
|
||||||
|
page,
|
||||||
|
itemsPerPage,
|
||||||
|
sessions
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json(payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GET: api/users/:id/listening-stats
|
// GET: api/users/:id/listening-stats
|
||||||
|
@ -696,7 +696,7 @@ class Scanner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add or set author if not set
|
// Add or set author if not set
|
||||||
if (matchData.author && !libraryItem.media.metadata.authorName || options.overrideDetails) {
|
if (matchData.author && (!libraryItem.media.metadata.authorName || options.overrideDetails)) {
|
||||||
if (!Array.isArray(matchData.author)) matchData.author = [matchData.author]
|
if (!Array.isArray(matchData.author)) matchData.author = [matchData.author]
|
||||||
const authorPayload = []
|
const authorPayload = []
|
||||||
for (let index = 0; index < matchData.author.length; index++) {
|
for (let index = 0; index < matchData.author.length; index++) {
|
||||||
@ -714,7 +714,7 @@ class Scanner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add or set series if not set
|
// Add or set series if not set
|
||||||
if (matchData.series && !libraryItem.media.metadata.seriesName || options.overrideDetails) {
|
if (matchData.series && (!libraryItem.media.metadata.seriesName || options.overrideDetails)) {
|
||||||
if (!Array.isArray(matchData.series)) matchData.series = [{ series: matchData.series, volumeNumber: matchData.volumeNumber }]
|
if (!Array.isArray(matchData.series)) matchData.series = [{ series: matchData.series, volumeNumber: matchData.volumeNumber }]
|
||||||
const seriesPayload = []
|
const seriesPayload = []
|
||||||
for (let index = 0; index < matchData.series.length; index++) {
|
for (let index = 0; index < matchData.series.length; index++) {
|
||||||
|
@ -125,3 +125,8 @@ module.exports.copyValue = (val) => {
|
|||||||
module.exports.encodeUriPath = (path) => {
|
module.exports.encodeUriPath = (path) => {
|
||||||
return path.replace(/\\/g, '/').replace(/%/g, '%25').replace(/#/g, '%23')
|
return path.replace(/\\/g, '/').replace(/%/g, '%25').replace(/#/g, '%23')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
module.exports.toNumber = (val, fallback = 0) => {
|
||||||
|
if (isNaN(val) || val === null) return fallback
|
||||||
|
return Number(val)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user