Change: config page to multiple pages, Add: user permissions for accessible libraries #120, Add: map genre metadata tag #114, Add: experimental audio player keyboard controls #121, Add: view user audiobook progress list

This commit is contained in:
advplyr 2021-10-22 20:08:02 -05:00
parent 7d9ed75a28
commit ff1eeda468
42 changed files with 957 additions and 464 deletions

View file

@ -1,7 +1,8 @@
class AudiobookProgress {
constructor(progress) {
this.audiobookId = null
this.audiobookTitle = null
this.id = null
this.totalDuration = null // seconds
this.progress = null // 0 to 1
this.currentTime = null // seconds
@ -18,7 +19,6 @@ class AudiobookProgress {
toJSON() {
return {
audiobookId: this.audiobookId,
audiobookTitle: this.audiobookTitle,
totalDuration: this.totalDuration,
progress: this.progress,
currentTime: this.currentTime,
@ -31,7 +31,6 @@ class AudiobookProgress {
construct(progress) {
this.audiobookId = progress.audiobookId
this.audiobookTitle = progress.audiobookTitle || null
this.totalDuration = progress.totalDuration
this.progress = progress.progress
this.currentTime = progress.currentTime
@ -43,7 +42,6 @@ class AudiobookProgress {
updateFromStream(stream) {
this.audiobookId = stream.audiobookId
this.audiobookTitle = stream.audiobookTitle
this.totalDuration = stream.totalDuration
this.progress = stream.clientProgress
this.currentTime = stream.clientCurrentTime
@ -89,6 +87,9 @@ class AudiobookProgress {
if (!this.startedAt) {
this.startedAt = Date.now()
}
if (hasUpdates) {
this.lastUpdate = Date.now()
}
return hasUpdates
}
}

View file

@ -235,6 +235,17 @@ class Book {
}
}
parseGenresTag(genreTag) {
if (!genreTag || !genreTag.length) return []
var separators = ['/', '//', ';']
for (let i = 0; i < separators.length; i++) {
if (genreTag.includes(separators[i])) {
return genreTag.split(separators[i]).map(genre => genre.trim()).filter(g => !!g)
}
}
return [genreTag]
}
setDetailsFromFileMetadata(audioFileMetadata) {
const MetadataMapArray = [
{
@ -260,14 +271,24 @@ class Book {
{
tag: 'tagArtist',
key: 'author'
},
{
tag: 'tagGenre',
key: 'genres'
}
]
var updatePayload = {}
MetadataMapArray.forEach((mapping) => {
if (!this[mapping.key] && audioFileMetadata[mapping.tag]) {
updatePayload[mapping.key] = audioFileMetadata[mapping.tag]
Logger.debug(`[Book] Mapping metadata to key ${mapping.tag} => ${mapping.key}: ${updatePayload[mapping.key]}`)
// Genres can contain multiple
if (mapping.key === 'genres') {
updatePayload[mapping.key] = this.parseGenresTag(audioFileMetadata[mapping.tag])
Logger.debug(`[Book] Mapping metadata to key ${mapping.tag} => ${mapping.key}: ${updatePayload[mapping.key].join(',')}`)
} else {
updatePayload[mapping.key] = audioFileMetadata[mapping.tag]
Logger.debug(`[Book] Mapping metadata to key ${mapping.tag} => ${mapping.key}: ${updatePayload[mapping.key]}`)
}
}
})

View file

@ -16,6 +16,7 @@ class User {
this.settings = {}
this.permissions = {}
this.librariesAccessible = [] // Library IDs (Empty if ALL libraries)
if (user) {
this.construct(user)
@ -37,6 +38,9 @@ class User {
get canUpload() {
return !!this.permissions.upload && this.isActive
}
get canAccessAllLibraries() {
return !!this.permissions.accessAllLibraries && this.isActive
}
get hasPw() {
return !!this.pash && !!this.pash.length
}
@ -59,7 +63,8 @@ class User {
download: true,
update: true,
delete: this.type === 'root',
upload: this.type === 'root' || this.type === 'admin'
upload: this.type === 'root' || this.type === 'admin',
accessAllLibraries: true
}
}
@ -88,7 +93,8 @@ class User {
lastSeen: this.lastSeen,
createdAt: this.createdAt,
settings: this.settings,
permissions: this.permissions
permissions: this.permissions,
librariesAccessible: [...this.librariesAccessible]
}
}
@ -105,10 +111,12 @@ class User {
lastSeen: this.lastSeen,
createdAt: this.createdAt,
settings: this.settings,
permissions: this.permissions
permissions: this.permissions,
librariesAccessible: [...this.librariesAccessible]
}
}
// Data broadcasted
toJSONForPublic(streams) {
var stream = this.stream && streams ? streams.find(s => s.id === this.stream) : null
return {
@ -144,6 +152,11 @@ class User {
this.permissions = user.permissions || this.getDefaultUserPermissions()
// Upload permission added v1.1.13, make sure root user has upload permissions
if (this.type === 'root' && !this.permissions.upload) this.permissions.upload = true
// Library restriction permissions added v1.4.14, defaults to all libraries
if (this.permissions.accessAllLibraries === undefined) this.permissions.accessAllLibraries = true
this.librariesAccessible = (user.librariesAccessible || []).map(l => l)
}
update(payload) {
@ -169,6 +182,18 @@ class User {
}
}
}
// Update accessible libraries
if (payload.librariesAccessible !== undefined) {
if (payload.librariesAccessible.length) {
if (payload.librariesAccessible.join(',') !== this.librariesAccessible.join(',')) {
hasUpdates = true
this.librariesAccessible = [...payload.librariesAccessible]
}
} else if (this.librariesAccessible.length > 0) {
hasUpdates = true
this.librariesAccessible = []
}
}
return hasUpdates
}
@ -180,13 +205,13 @@ class User {
this.audiobooks[stream.audiobookId].updateFromStream(stream)
}
updateAudiobookProgress(audiobookId, updatePayload) {
updateAudiobookProgress(audiobook, updatePayload) {
if (!this.audiobooks) this.audiobooks = {}
if (!this.audiobooks[audiobookId]) {
this.audiobooks[audiobookId] = new AudiobookProgress()
this.audiobooks[audiobookId].audiobookId = audiobookId
if (!this.audiobooks[audiobook.id]) {
this.audiobooks[audiobook.id] = new AudiobookProgress()
this.audiobooks[audiobook.id].audiobookId = audiobook.id
}
return this.audiobooks[audiobookId].update(updatePayload)
return this.audiobooks[audiobook.id].update(updatePayload)
}
// Returns Boolean If update was made
@ -215,11 +240,11 @@ class User {
return madeUpdates
}
resetAudiobookProgress(audiobookId) {
if (!this.audiobooks || !this.audiobooks[audiobookId]) {
resetAudiobookProgress(audiobook) {
if (!this.audiobooks || !this.audiobooks[audiobook.id]) {
return false
}
return this.updateAudiobookProgress(audiobookId, {
return this.updateAudiobookProgress(audiobook, {
progress: 0,
currentTime: 0,
isRead: false,
@ -236,5 +261,11 @@ class User {
delete this.audiobooks[audiobookId]
return true
}
checkCanAccessLibrary(libraryId) {
if (this.permissions.accessAllLibraries) return true
if (!this.librariesAccessible) return false
return this.librariesAccessible.includes(libraryId)
}
}
module.exports = User