mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-12-20 18:59:37 +00:00
Merge master
This commit is contained in:
commit
ab14b561f5
147 changed files with 4669 additions and 5036 deletions
|
|
@ -1,6 +1,14 @@
|
|||
const Logger = require('../../Logger')
|
||||
const { areEquivalent, copyValue, isNullOrNaN } = require('../../utils')
|
||||
|
||||
/**
|
||||
* @typedef EreaderDeviceObject
|
||||
* @property {string} name
|
||||
* @property {string} email
|
||||
* @property {string} availabilityOption
|
||||
* @property {string[]} users
|
||||
*/
|
||||
|
||||
// REF: https://nodemailer.com/smtp/
|
||||
class EmailSettings {
|
||||
constructor(settings = null) {
|
||||
|
|
@ -13,7 +21,7 @@ class EmailSettings {
|
|||
this.testAddress = null
|
||||
this.fromAddress = null
|
||||
|
||||
// Array of { name:String, email:String }
|
||||
/** @type {EreaderDeviceObject[]} */
|
||||
this.ereaderDevices = []
|
||||
|
||||
if (settings) {
|
||||
|
|
@ -57,6 +65,26 @@ class EmailSettings {
|
|||
|
||||
if (payload.ereaderDevices !== undefined && !Array.isArray(payload.ereaderDevices)) payload.ereaderDevices = undefined
|
||||
|
||||
if (payload.ereaderDevices?.length) {
|
||||
// Validate ereader devices
|
||||
payload.ereaderDevices = payload.ereaderDevices.map((device) => {
|
||||
if (!device.name || !device.email) {
|
||||
Logger.error(`[EmailSettings] Update ereader device is invalid`, device)
|
||||
return null
|
||||
}
|
||||
if (!device.availabilityOption || !['adminOrUp', 'userOrUp', 'guestOrUp', 'specificUsers'].includes(device.availabilityOption)) {
|
||||
device.availabilityOption = 'adminOrUp'
|
||||
}
|
||||
if (device.availabilityOption === 'specificUsers' && !device.users?.length) {
|
||||
device.availabilityOption = 'adminOrUp'
|
||||
}
|
||||
if (device.availabilityOption !== 'specificUsers' && device.users?.length) {
|
||||
device.users = []
|
||||
}
|
||||
return device
|
||||
}).filter(d => d)
|
||||
}
|
||||
|
||||
let hasUpdates = false
|
||||
|
||||
const json = this.toJSON()
|
||||
|
|
@ -88,15 +116,40 @@ class EmailSettings {
|
|||
return payload
|
||||
}
|
||||
|
||||
getEReaderDevices(user) {
|
||||
// Only accessible to admin or up
|
||||
if (!user.isAdminOrUp) {
|
||||
return []
|
||||
/**
|
||||
*
|
||||
* @param {EreaderDeviceObject} device
|
||||
* @param {import('../user/User')} user
|
||||
* @returns {boolean}
|
||||
*/
|
||||
checkUserCanAccessDevice(device, user) {
|
||||
let deviceAvailability = device.availabilityOption || 'adminOrUp'
|
||||
if (deviceAvailability === 'adminOrUp' && user.isAdminOrUp) return true
|
||||
if (deviceAvailability === 'userOrUp' && (user.isAdminOrUp || user.isUser)) return true
|
||||
if (deviceAvailability === 'guestOrUp') return true
|
||||
if (deviceAvailability === 'specificUsers') {
|
||||
let deviceUsers = device.users || []
|
||||
return deviceUsers.includes(user.id)
|
||||
}
|
||||
|
||||
return this.ereaderDevices.map(d => ({ ...d }))
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Get ereader devices accessible to user
|
||||
*
|
||||
* @param {import('../user/User')} user
|
||||
* @returns {EreaderDeviceObject[]}
|
||||
*/
|
||||
getEReaderDevices(user) {
|
||||
return this.ereaderDevices.filter((device) => this.checkUserCanAccessDevice(device, user))
|
||||
}
|
||||
|
||||
/**
|
||||
* Get ereader device by name
|
||||
*
|
||||
* @param {string} deviceName
|
||||
* @returns {EreaderDeviceObject}
|
||||
*/
|
||||
getEReaderDevice(deviceName) {
|
||||
return this.ereaderDevices.find(d => d.name === deviceName)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ class LibrarySettings {
|
|||
this.autoScanCronExpression = null
|
||||
this.audiobooksOnly = false
|
||||
this.hideSingleBookSeries = false // Do not show series that only have 1 book
|
||||
this.metadataPrecedence = ['folderStructure', 'audioMetatags', 'txtFiles', 'opfFile', 'absMetadata']
|
||||
|
||||
if (settings) {
|
||||
this.construct(settings)
|
||||
|
|
@ -23,6 +24,12 @@ class LibrarySettings {
|
|||
this.autoScanCronExpression = settings.autoScanCronExpression || null
|
||||
this.audiobooksOnly = !!settings.audiobooksOnly
|
||||
this.hideSingleBookSeries = !!settings.hideSingleBookSeries
|
||||
if (settings.metadataPrecedence) {
|
||||
this.metadataPrecedence = [...settings.metadataPrecedence]
|
||||
} else {
|
||||
// Added in v2.4.5
|
||||
this.metadataPrecedence = ['folderStructure', 'audioMetatags', 'txtFiles', 'opfFile', 'absMetadata']
|
||||
}
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
|
|
@ -33,14 +40,20 @@ class LibrarySettings {
|
|||
skipMatchingMediaWithIsbn: this.skipMatchingMediaWithIsbn,
|
||||
autoScanCronExpression: this.autoScanCronExpression,
|
||||
audiobooksOnly: this.audiobooksOnly,
|
||||
hideSingleBookSeries: this.hideSingleBookSeries
|
||||
hideSingleBookSeries: this.hideSingleBookSeries,
|
||||
metadataPrecedence: [...this.metadataPrecedence]
|
||||
}
|
||||
}
|
||||
|
||||
update(payload) {
|
||||
let hasUpdates = false
|
||||
for (const key in payload) {
|
||||
if (this[key] !== payload[key]) {
|
||||
if (key === 'metadataPrecedence') {
|
||||
if (payload[key] && Array.isArray(payload[key]) && payload[key].join() !== this[key].join()) {
|
||||
this[key] = payload[key]
|
||||
hasUpdates = true
|
||||
}
|
||||
} else if (this[key] !== payload[key]) {
|
||||
this[key] = payload[key]
|
||||
hasUpdates = true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
const packageJson = require('../../../package.json')
|
||||
const { BookshelfView } = require('../../utils/constants')
|
||||
const Logger = require('../../Logger')
|
||||
|
||||
|
|
@ -10,11 +11,8 @@ class ServerSettings {
|
|||
this.scannerParseSubtitle = false
|
||||
this.scannerFindCovers = false
|
||||
this.scannerCoverProvider = 'google'
|
||||
this.scannerPreferAudioMetadata = false
|
||||
this.scannerPreferOpfMetadata = false
|
||||
this.scannerPreferMatchedMetadata = false
|
||||
this.scannerDisableWatcher = false
|
||||
this.scannerPreferOverdriveMediaMarker = false
|
||||
|
||||
// Metadata - choose to store inside users library item folder
|
||||
this.storeCoverWithItem = false
|
||||
|
|
@ -53,7 +51,8 @@ class ServerSettings {
|
|||
|
||||
this.logLevel = Logger.logLevel
|
||||
|
||||
this.version = null
|
||||
this.version = packageJson.version
|
||||
this.buildNumber = packageJson.buildNumber
|
||||
|
||||
// Auth settings
|
||||
// Active auth methodes
|
||||
|
|
@ -82,11 +81,8 @@ class ServerSettings {
|
|||
this.scannerFindCovers = !!settings.scannerFindCovers
|
||||
this.scannerCoverProvider = settings.scannerCoverProvider || 'google'
|
||||
this.scannerParseSubtitle = settings.scannerParseSubtitle
|
||||
this.scannerPreferAudioMetadata = !!settings.scannerPreferAudioMetadata
|
||||
this.scannerPreferOpfMetadata = !!settings.scannerPreferOpfMetadata
|
||||
this.scannerPreferMatchedMetadata = !!settings.scannerPreferMatchedMetadata
|
||||
this.scannerDisableWatcher = !!settings.scannerDisableWatcher
|
||||
this.scannerPreferOverdriveMediaMarker = !!settings.scannerPreferOverdriveMediaMarker
|
||||
|
||||
this.storeCoverWithItem = !!settings.storeCoverWithItem
|
||||
this.storeMetadataWithItem = !!settings.storeMetadataWithItem
|
||||
|
|
@ -113,6 +109,7 @@ class ServerSettings {
|
|||
this.language = settings.language || 'en-us'
|
||||
this.logLevel = settings.logLevel || Logger.logLevel
|
||||
this.version = settings.version || null
|
||||
this.buildNumber = settings.buildNumber || 0 // Added v2.4.5
|
||||
|
||||
this.authActiveAuthMethods = settings.authActiveAuthMethods || ['local']
|
||||
this.authGoogleOauth20ClientID = settings.authGoogleOauth20ClientID || ''
|
||||
|
|
@ -173,9 +170,9 @@ class ServerSettings {
|
|||
this.metadataFileFormat = 'abs'
|
||||
}
|
||||
|
||||
// Validation
|
||||
if (!['abs', 'json'].includes(this.metadataFileFormat)) {
|
||||
Logger.error(`[ServerSettings] construct: Invalid metadataFileFormat ${this.metadataFileFormat}`)
|
||||
// As of v2.4.5 only json is supported
|
||||
if (this.metadataFileFormat !== 'json') {
|
||||
Logger.warn(`[ServerSettings] Invalid metadataFileFormat ${this.metadataFileFormat} (as of v2.4.5 only json is supported)`)
|
||||
this.metadataFileFormat = 'json'
|
||||
}
|
||||
|
||||
|
|
@ -191,11 +188,8 @@ class ServerSettings {
|
|||
scannerFindCovers: this.scannerFindCovers,
|
||||
scannerCoverProvider: this.scannerCoverProvider,
|
||||
scannerParseSubtitle: this.scannerParseSubtitle,
|
||||
scannerPreferAudioMetadata: this.scannerPreferAudioMetadata,
|
||||
scannerPreferOpfMetadata: this.scannerPreferOpfMetadata,
|
||||
scannerPreferMatchedMetadata: this.scannerPreferMatchedMetadata,
|
||||
scannerDisableWatcher: this.scannerDisableWatcher,
|
||||
scannerPreferOverdriveMediaMarker: this.scannerPreferOverdriveMediaMarker,
|
||||
storeCoverWithItem: this.storeCoverWithItem,
|
||||
storeMetadataWithItem: this.storeMetadataWithItem,
|
||||
metadataFileFormat: this.metadataFileFormat,
|
||||
|
|
@ -217,6 +211,7 @@ class ServerSettings {
|
|||
language: this.language,
|
||||
logLevel: this.logLevel,
|
||||
version: this.version,
|
||||
buildNumber: this.buildNumber,
|
||||
authActiveAuthMethods: this.authActiveAuthMethods,
|
||||
authGoogleOauth20ClientID: this.authGoogleOauth20ClientID, // Do not return to client
|
||||
authGoogleOauth20ClientSecret: this.authGoogleOauth20ClientSecret, // Do not return to client
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue