mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-02-06 13:30:03 +01:00
Removing remaining legacy objects, remove njodb error for fileExists
This commit is contained in:
parent
99e2ea228d
commit
c60807f998
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "audiobookshelf-client",
|
"name": "audiobookshelf-client",
|
||||||
"version": "1.7.2",
|
"version": "1.7.3",
|
||||||
"description": "Audiobook manager and player",
|
"description": "Audiobook manager and player",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "audiobookshelf",
|
"name": "audiobookshelf",
|
||||||
"version": "1.7.2",
|
"version": "1.7.3",
|
||||||
"description": "Self-hosted audiobook server for managing and playing audiobooks",
|
"description": "Self-hosted audiobook server for managing and playing audiobooks",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@ -53,4 +53,4 @@
|
|||||||
"xml2js": "^0.4.23"
|
"xml2js": "^0.4.23"
|
||||||
},
|
},
|
||||||
"devDependencies": {}
|
"devDependencies": {}
|
||||||
}
|
}
|
@ -117,7 +117,7 @@ class Server {
|
|||||||
// await this.streamManager.removeOrphanStreams()
|
// await this.streamManager.removeOrphanStreams()
|
||||||
await this.downloadManager.removeOrphanDownloads()
|
await this.downloadManager.removeOrphanDownloads()
|
||||||
|
|
||||||
if (version.localeCompare('1.7.3') < 0) { // Old version data model migration
|
if (version.localeCompare('2.0.0') < 0) { // Old version data model migration
|
||||||
await dbMigration.migrate(this.db)
|
await dbMigration.migrate(this.db)
|
||||||
} else {
|
} else {
|
||||||
await this.db.init()
|
await this.db.init()
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
const fs = require('fs-extra')
|
const fs = require('fs-extra')
|
||||||
const Logger = require('../Logger')
|
const Logger = require('../Logger')
|
||||||
const Path = require('path')
|
const Path = require('path')
|
||||||
const Author = require('../objects/legacy/Author')
|
|
||||||
const Audnexus = require('../providers/Audnexus')
|
const Audnexus = require('../providers/Audnexus')
|
||||||
|
|
||||||
const { downloadFile } = require('../utils/fileUtils')
|
const { downloadFile } = require('../utils/fileUtils')
|
||||||
@ -51,80 +50,5 @@ class AuthorFinder {
|
|||||||
relPath
|
relPath
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async createAuthor(payload) {
|
|
||||||
if (!payload || !payload.name) return null
|
|
||||||
|
|
||||||
var authorDir = Path.posix.join(this.AuthorPath, payload.name)
|
|
||||||
var relAuthorDir = Path.posix.join('/metadata', 'authors', payload.name)
|
|
||||||
|
|
||||||
if (payload.image && payload.image.startsWith('http')) {
|
|
||||||
await fs.ensureDir(authorDir)
|
|
||||||
|
|
||||||
var imageExtension = payload.image.toLowerCase().split('.').pop()
|
|
||||||
var ext = imageExtension === 'png' ? 'png' : 'jpg'
|
|
||||||
var filename = 'photo.' + ext
|
|
||||||
var outputPath = Path.posix.join(authorDir, filename)
|
|
||||||
var relPath = Path.posix.join(relAuthorDir, filename)
|
|
||||||
|
|
||||||
var success = await this.downloadImage(payload.image, outputPath)
|
|
||||||
if (!success) {
|
|
||||||
await fs.rmdir(authorDir).catch((error) => {
|
|
||||||
Logger.error(`[AuthorFinder] Failed to remove author dir`, authorDir, error)
|
|
||||||
})
|
|
||||||
payload.image = null
|
|
||||||
payload.imageFullPath = null
|
|
||||||
} else {
|
|
||||||
payload.image = relPath
|
|
||||||
payload.imageFullPath = outputPath
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
payload.image = null
|
|
||||||
payload.imageFullPath = null
|
|
||||||
}
|
|
||||||
|
|
||||||
var author = new Author()
|
|
||||||
author.setData(payload)
|
|
||||||
|
|
||||||
return author
|
|
||||||
}
|
|
||||||
|
|
||||||
async getAuthorByName(name, options = {}) {
|
|
||||||
var authorData = await this.findAuthorByName(name, options)
|
|
||||||
if (!authorData) return null
|
|
||||||
|
|
||||||
var authorDir = Path.posix.join(this.AuthorPath, authorData.name)
|
|
||||||
var relAuthorDir = Path.posix.join('/metadata', 'authors', authorData.name)
|
|
||||||
|
|
||||||
if (authorData.image) {
|
|
||||||
await fs.ensureDir(authorDir)
|
|
||||||
|
|
||||||
var imageExtension = authorData.image.toLowerCase().split('.').pop()
|
|
||||||
var ext = imageExtension === 'png' ? 'png' : 'jpg'
|
|
||||||
var filename = 'photo.' + ext
|
|
||||||
var outputPath = Path.posix.join(authorDir, filename)
|
|
||||||
var relPath = Path.posix.join(relAuthorDir, filename)
|
|
||||||
|
|
||||||
var success = await this.downloadImage(authorData.image, outputPath)
|
|
||||||
if (!success) {
|
|
||||||
await fs.rmdir(authorDir).catch((error) => {
|
|
||||||
Logger.error(`[AuthorFinder] Failed to remove author dir`, authorDir, error)
|
|
||||||
})
|
|
||||||
authorData.image = null
|
|
||||||
authorData.imageFullPath = null
|
|
||||||
} else {
|
|
||||||
authorData.image = relPath
|
|
||||||
authorData.imageFullPath = outputPath
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
authorData.image = null
|
|
||||||
authorData.imageFullPath = null
|
|
||||||
}
|
|
||||||
|
|
||||||
var author = new Author()
|
|
||||||
author.setData(authorData)
|
|
||||||
|
|
||||||
return author
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
module.exports = AuthorFinder
|
module.exports = AuthorFinder
|
@ -27,7 +27,7 @@ const max = (a, b) => {
|
|||||||
const convertSize = (size) => {
|
const convertSize = (size) => {
|
||||||
const sizes = ["bytes", "KB", "MB", "GB"];
|
const sizes = ["bytes", "KB", "MB", "GB"];
|
||||||
|
|
||||||
var index = Math.floor(Math.log2(size)/10);
|
var index = Math.floor(Math.log2(size) / 10);
|
||||||
if (index > 3) index = 3;
|
if (index > 3) index = 3;
|
||||||
|
|
||||||
return Math.round(((size / Math.pow(1024, index)) + Number.EPSILON) * 100) / 100 + " " + sizes[index];
|
return Math.round(((size / Math.pow(1024, index)) + Number.EPSILON) * 100) / 100 + " " + sizes[index];
|
||||||
@ -38,7 +38,7 @@ const fileExists = async (a) => {
|
|||||||
await promisify(access)(a, constants.F_OK);
|
await promisify(access)(a, constants.F_OK);
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
// console.error(error); file does not exist no need for error
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,243 +0,0 @@
|
|||||||
const { isNullOrNaN } = require('../../utils/index')
|
|
||||||
const AudioFileMetadata = require('../metadata/AudioMetaTags')
|
|
||||||
|
|
||||||
class AudioFile {
|
|
||||||
constructor(data) {
|
|
||||||
this.index = null
|
|
||||||
this.ino = null
|
|
||||||
this.filename = null
|
|
||||||
this.ext = null
|
|
||||||
this.path = null
|
|
||||||
this.fullPath = null
|
|
||||||
this.mtimeMs = null
|
|
||||||
this.ctimeMs = null
|
|
||||||
this.birthtimeMs = null
|
|
||||||
this.addedAt = null
|
|
||||||
|
|
||||||
this.trackNumFromMeta = null
|
|
||||||
this.discNumFromMeta = null
|
|
||||||
this.trackNumFromFilename = null
|
|
||||||
this.discNumFromFilename = null
|
|
||||||
|
|
||||||
this.format = null
|
|
||||||
this.duration = null
|
|
||||||
this.size = null
|
|
||||||
this.bitRate = null
|
|
||||||
this.language = null
|
|
||||||
this.codec = null
|
|
||||||
this.timeBase = null
|
|
||||||
this.channels = null
|
|
||||||
this.channelLayout = null
|
|
||||||
this.chapters = []
|
|
||||||
this.embeddedCoverArt = null
|
|
||||||
|
|
||||||
// Tags scraped from the audio file
|
|
||||||
this.metadata = null
|
|
||||||
|
|
||||||
this.manuallyVerified = false
|
|
||||||
this.invalid = false
|
|
||||||
this.exclude = false
|
|
||||||
this.error = null
|
|
||||||
|
|
||||||
if (data) {
|
|
||||||
this.construct(data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
toJSON() {
|
|
||||||
return {
|
|
||||||
index: this.index,
|
|
||||||
ino: this.ino,
|
|
||||||
filename: this.filename,
|
|
||||||
ext: this.ext,
|
|
||||||
path: this.path,
|
|
||||||
fullPath: this.fullPath,
|
|
||||||
mtimeMs: this.mtimeMs,
|
|
||||||
ctimeMs: this.ctimeMs,
|
|
||||||
birthtimeMs: this.birthtimeMs,
|
|
||||||
addedAt: this.addedAt,
|
|
||||||
trackNumFromMeta: this.trackNumFromMeta,
|
|
||||||
discNumFromMeta: this.discNumFromMeta,
|
|
||||||
trackNumFromFilename: this.trackNumFromFilename,
|
|
||||||
discNumFromFilename: this.discNumFromFilename,
|
|
||||||
manuallyVerified: !!this.manuallyVerified,
|
|
||||||
invalid: !!this.invalid,
|
|
||||||
exclude: !!this.exclude,
|
|
||||||
error: this.error || null,
|
|
||||||
format: this.format,
|
|
||||||
duration: this.duration,
|
|
||||||
size: this.size,
|
|
||||||
bitRate: this.bitRate,
|
|
||||||
language: this.language,
|
|
||||||
codec: this.codec,
|
|
||||||
timeBase: this.timeBase,
|
|
||||||
channels: this.channels,
|
|
||||||
channelLayout: this.channelLayout,
|
|
||||||
chapters: this.chapters,
|
|
||||||
embeddedCoverArt: this.embeddedCoverArt,
|
|
||||||
metadata: this.metadata ? this.metadata.toJSON() : {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
construct(data) {
|
|
||||||
this.index = data.index
|
|
||||||
this.ino = data.ino
|
|
||||||
this.filename = data.filename
|
|
||||||
this.ext = data.ext
|
|
||||||
this.path = data.path
|
|
||||||
this.fullPath = data.fullPath
|
|
||||||
this.mtimeMs = data.mtimeMs || 0
|
|
||||||
this.ctimeMs = data.ctimeMs || 0
|
|
||||||
this.birthtimeMs = data.birthtimeMs || 0
|
|
||||||
this.addedAt = data.addedAt
|
|
||||||
this.manuallyVerified = !!data.manuallyVerified
|
|
||||||
this.invalid = !!data.invalid
|
|
||||||
this.exclude = !!data.exclude
|
|
||||||
this.error = data.error || null
|
|
||||||
|
|
||||||
this.trackNumFromMeta = data.trackNumFromMeta
|
|
||||||
this.discNumFromMeta = data.discNumFromMeta
|
|
||||||
this.trackNumFromFilename = data.trackNumFromFilename
|
|
||||||
|
|
||||||
if (data.cdNumFromFilename !== undefined) this.discNumFromFilename = data.cdNumFromFilename // TEMP:Support old var name
|
|
||||||
else this.discNumFromFilename = data.discNumFromFilename
|
|
||||||
|
|
||||||
this.format = data.format
|
|
||||||
this.duration = data.duration
|
|
||||||
this.size = data.size
|
|
||||||
this.bitRate = data.bitRate
|
|
||||||
this.language = data.language
|
|
||||||
this.codec = data.codec || null
|
|
||||||
this.timeBase = data.timeBase
|
|
||||||
this.channels = data.channels
|
|
||||||
this.channelLayout = data.channelLayout
|
|
||||||
this.chapters = data.chapters
|
|
||||||
this.embeddedCoverArt = data.embeddedCoverArt || null
|
|
||||||
|
|
||||||
// Old version of AudioFile used `tagAlbum` etc.
|
|
||||||
var isOldVersion = Object.keys(data).find(key => key.startsWith('tag'))
|
|
||||||
if (isOldVersion) {
|
|
||||||
this.metadata = new AudioFileMetadata(data)
|
|
||||||
} else {
|
|
||||||
this.metadata = new AudioFileMetadata(data.metadata || {})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// New scanner creates AudioFile from AudioFileScanner
|
|
||||||
setDataFromProbe(fileData, probeData) {
|
|
||||||
this.index = fileData.index || null
|
|
||||||
this.ino = fileData.ino || null
|
|
||||||
this.filename = fileData.filename
|
|
||||||
this.ext = fileData.ext
|
|
||||||
this.path = fileData.path
|
|
||||||
this.fullPath = fileData.fullPath
|
|
||||||
this.mtimeMs = fileData.mtimeMs || 0
|
|
||||||
this.ctimeMs = fileData.ctimeMs || 0
|
|
||||||
this.birthtimeMs = fileData.birthtimeMs || 0
|
|
||||||
this.addedAt = Date.now()
|
|
||||||
|
|
||||||
this.trackNumFromMeta = fileData.trackNumFromMeta
|
|
||||||
this.discNumFromMeta = fileData.discNumFromMeta
|
|
||||||
this.trackNumFromFilename = fileData.trackNumFromFilename
|
|
||||||
this.discNumFromFilename = fileData.discNumFromFilename
|
|
||||||
|
|
||||||
this.format = probeData.format
|
|
||||||
this.duration = probeData.duration
|
|
||||||
this.size = probeData.size
|
|
||||||
this.bitRate = probeData.bitRate || null
|
|
||||||
this.language = probeData.language
|
|
||||||
this.codec = probeData.codec || null
|
|
||||||
this.timeBase = probeData.timeBase
|
|
||||||
this.channels = probeData.channels
|
|
||||||
this.channelLayout = probeData.channelLayout
|
|
||||||
this.chapters = probeData.chapters || []
|
|
||||||
this.metadata = probeData.audioFileMetadata
|
|
||||||
this.embeddedCoverArt = probeData.embeddedCoverArt
|
|
||||||
}
|
|
||||||
|
|
||||||
validateTrackIndex() {
|
|
||||||
var numFromMeta = isNullOrNaN(this.trackNumFromMeta) ? null : Number(this.trackNumFromMeta)
|
|
||||||
var numFromFilename = isNullOrNaN(this.trackNumFromFilename) ? null : Number(this.trackNumFromFilename)
|
|
||||||
|
|
||||||
if (numFromMeta !== null) return numFromMeta
|
|
||||||
if (numFromFilename !== null) return numFromFilename
|
|
||||||
|
|
||||||
this.invalid = true
|
|
||||||
this.error = 'Failed to get track number'
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
setDuplicateTrackNumber(num) {
|
|
||||||
this.invalid = true
|
|
||||||
this.error = 'Duplicate track number "' + num + '"'
|
|
||||||
}
|
|
||||||
|
|
||||||
syncChapters(updatedChapters) {
|
|
||||||
if (this.chapters.length !== updatedChapters.length) {
|
|
||||||
this.chapters = updatedChapters.map(ch => ({ ...ch }))
|
|
||||||
return true
|
|
||||||
} else if (updatedChapters.length === 0) {
|
|
||||||
if (this.chapters.length > 0) {
|
|
||||||
this.chapters = []
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
var hasUpdates = false
|
|
||||||
for (let i = 0; i < updatedChapters.length; i++) {
|
|
||||||
if (JSON.stringify(updatedChapters[i]) !== JSON.stringify(this.chapters[i])) {
|
|
||||||
hasUpdates = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (hasUpdates) {
|
|
||||||
this.chapters = updatedChapters.map(ch => ({ ...ch }))
|
|
||||||
}
|
|
||||||
return hasUpdates
|
|
||||||
}
|
|
||||||
|
|
||||||
clone() {
|
|
||||||
return new AudioFile(this.toJSON())
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the file or parent directory was renamed it is synced here
|
|
||||||
syncFile(newFile) {
|
|
||||||
var hasUpdates = false
|
|
||||||
var keysToSync = ['path', 'fullPath', 'ext', 'filename']
|
|
||||||
keysToSync.forEach((key) => {
|
|
||||||
if (newFile[key] !== undefined && newFile[key] !== this[key]) {
|
|
||||||
hasUpdates = true
|
|
||||||
this[key] = newFile[key]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return hasUpdates
|
|
||||||
}
|
|
||||||
|
|
||||||
updateFromScan(scannedAudioFile) {
|
|
||||||
var hasUpdated = false
|
|
||||||
|
|
||||||
var newjson = scannedAudioFile.toJSON()
|
|
||||||
if (this.manuallyVerified) newjson.manuallyVerified = true
|
|
||||||
if (this.exclude) newjson.exclude = true
|
|
||||||
newjson.addedAt = this.addedAt
|
|
||||||
|
|
||||||
for (const key in newjson) {
|
|
||||||
if (key === 'metadata') {
|
|
||||||
if (!this.metadata || !this.metadata.isEqual(scannedAudioFile.metadata)) {
|
|
||||||
this.metadata = scannedAudioFile.metadata
|
|
||||||
hasUpdated = true
|
|
||||||
}
|
|
||||||
} else if (key === 'chapters') {
|
|
||||||
if (this.syncChapters(newjson.chapters || [])) {
|
|
||||||
hasUpdated = true
|
|
||||||
}
|
|
||||||
} else if (this[key] !== newjson[key]) {
|
|
||||||
// console.log(this.filename, 'key', key, 'updated', this[key], newjson[key])
|
|
||||||
this[key] = newjson[key]
|
|
||||||
hasUpdated = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return hasUpdated
|
|
||||||
}
|
|
||||||
}
|
|
||||||
module.exports = AudioFile
|
|
@ -1,116 +0,0 @@
|
|||||||
var { bytesPretty } = require('../../utils/fileUtils')
|
|
||||||
|
|
||||||
class AudioTrack {
|
|
||||||
constructor(audioTrack = null) {
|
|
||||||
this.index = null
|
|
||||||
this.ino = null
|
|
||||||
|
|
||||||
this.path = null
|
|
||||||
this.fullPath = null
|
|
||||||
this.ext = null
|
|
||||||
this.filename = null
|
|
||||||
|
|
||||||
this.format = null
|
|
||||||
this.duration = null
|
|
||||||
this.size = null
|
|
||||||
this.bitRate = null
|
|
||||||
this.language = null
|
|
||||||
this.codec = null
|
|
||||||
this.timeBase = null
|
|
||||||
this.channels = null
|
|
||||||
this.channelLayout = null
|
|
||||||
|
|
||||||
if (audioTrack) {
|
|
||||||
this.construct(audioTrack)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
construct(audioTrack) {
|
|
||||||
this.index = audioTrack.index
|
|
||||||
this.ino = audioTrack.ino || null
|
|
||||||
|
|
||||||
this.path = audioTrack.path
|
|
||||||
this.fullPath = audioTrack.fullPath
|
|
||||||
this.ext = audioTrack.ext
|
|
||||||
this.filename = audioTrack.filename
|
|
||||||
|
|
||||||
this.format = audioTrack.format
|
|
||||||
this.duration = audioTrack.duration
|
|
||||||
this.size = audioTrack.size
|
|
||||||
this.bitRate = audioTrack.bitRate
|
|
||||||
this.language = audioTrack.language
|
|
||||||
this.codec = audioTrack.codec
|
|
||||||
this.timeBase = audioTrack.timeBase
|
|
||||||
this.channels = audioTrack.channels
|
|
||||||
this.channelLayout = audioTrack.channelLayout
|
|
||||||
}
|
|
||||||
|
|
||||||
get name() {
|
|
||||||
return `${String(this.index).padStart(3, '0')}: ${this.filename} (${bytesPretty(this.size)}) [${this.duration}]`
|
|
||||||
}
|
|
||||||
|
|
||||||
toJSON() {
|
|
||||||
return {
|
|
||||||
index: this.index,
|
|
||||||
ino: this.ino,
|
|
||||||
path: this.path,
|
|
||||||
fullPath: this.fullPath,
|
|
||||||
ext: this.ext,
|
|
||||||
filename: this.filename,
|
|
||||||
format: this.format,
|
|
||||||
duration: this.duration,
|
|
||||||
size: this.size,
|
|
||||||
bitRate: this.bitRate,
|
|
||||||
language: this.language,
|
|
||||||
codec: this.codec,
|
|
||||||
timeBase: this.timeBase,
|
|
||||||
channels: this.channels,
|
|
||||||
channelLayout: this.channelLayout,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setData(probeData) {
|
|
||||||
this.index = probeData.index
|
|
||||||
this.ino = probeData.ino || null
|
|
||||||
|
|
||||||
this.path = probeData.path
|
|
||||||
this.fullPath = probeData.fullPath
|
|
||||||
this.ext = probeData.ext
|
|
||||||
this.filename = probeData.filename
|
|
||||||
|
|
||||||
this.format = probeData.format
|
|
||||||
this.duration = probeData.duration
|
|
||||||
this.size = probeData.size
|
|
||||||
this.bitRate = probeData.bitRate
|
|
||||||
this.language = probeData.language
|
|
||||||
this.codec = probeData.codec || null
|
|
||||||
this.timeBase = probeData.timeBase
|
|
||||||
this.channels = probeData.channels
|
|
||||||
this.channelLayout = probeData.channelLayout
|
|
||||||
}
|
|
||||||
|
|
||||||
syncMetadata(audioFile) {
|
|
||||||
var hasUpdates = false
|
|
||||||
var keysToSync = ['format', 'duration', 'size', 'bitRate', 'language', 'codec', 'timeBase', 'channels', 'channelLayout']
|
|
||||||
keysToSync.forEach((key) => {
|
|
||||||
if (audioFile[key] !== undefined && audioFile[key] !== this[key]) {
|
|
||||||
hasUpdates = true
|
|
||||||
this[key] = audioFile[key]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return hasUpdates
|
|
||||||
}
|
|
||||||
|
|
||||||
syncFile(newFile) {
|
|
||||||
var hasUpdates = false
|
|
||||||
var keysToSync = ['path', 'fullPath', 'ext', 'filename']
|
|
||||||
keysToSync.forEach((key) => {
|
|
||||||
if (newFile[key] !== undefined && newFile[key] !== this[key]) {
|
|
||||||
hasUpdates = true
|
|
||||||
this[key] = newFile[key]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return hasUpdates
|
|
||||||
}
|
|
||||||
}
|
|
||||||
module.exports = AudioTrack
|
|
File diff suppressed because it is too large
Load Diff
@ -1,69 +0,0 @@
|
|||||||
class AudiobookFile {
|
|
||||||
constructor(data) {
|
|
||||||
this.ino = null
|
|
||||||
this.filetype = null
|
|
||||||
this.filename = null
|
|
||||||
this.ext = null
|
|
||||||
this.path = null
|
|
||||||
this.fullPath = null
|
|
||||||
this.size = null
|
|
||||||
this.mtimeMs = null
|
|
||||||
this.ctimeMs = null
|
|
||||||
this.birthtimeMs = null
|
|
||||||
|
|
||||||
this.addedAt = null
|
|
||||||
|
|
||||||
if (data) {
|
|
||||||
this.construct(data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get isOPFFile() {
|
|
||||||
return this.ext ? this.ext.toLowerCase() === '.opf' : false
|
|
||||||
}
|
|
||||||
|
|
||||||
toJSON() {
|
|
||||||
return {
|
|
||||||
ino: this.ino || null,
|
|
||||||
filetype: this.filetype,
|
|
||||||
filename: this.filename,
|
|
||||||
ext: this.ext,
|
|
||||||
path: this.path,
|
|
||||||
fullPath: this.fullPath,
|
|
||||||
size: this.size,
|
|
||||||
mtimeMs: this.mtimeMs,
|
|
||||||
ctimeMs: this.ctimeMs,
|
|
||||||
birthtimeMs: this.birthtimeMs,
|
|
||||||
addedAt: this.addedAt
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
construct(data) {
|
|
||||||
this.ino = data.ino || null
|
|
||||||
this.filetype = data.filetype
|
|
||||||
this.filename = data.filename
|
|
||||||
this.ext = data.ext
|
|
||||||
this.path = data.path
|
|
||||||
this.fullPath = data.fullPath
|
|
||||||
this.size = data.size || 0
|
|
||||||
this.mtimeMs = data.mtimeMs || 0
|
|
||||||
this.ctimeMs = data.ctimeMs || 0
|
|
||||||
this.birthtimeMs = data.birthtimeMs || 0
|
|
||||||
this.addedAt = data.addedAt
|
|
||||||
}
|
|
||||||
|
|
||||||
setData(data) {
|
|
||||||
this.ino = data.ino || null
|
|
||||||
this.filetype = data.filetype
|
|
||||||
this.filename = data.filename
|
|
||||||
this.ext = data.ext
|
|
||||||
this.path = data.path
|
|
||||||
this.fullPath = data.fullPath
|
|
||||||
this.size = data.size || 0
|
|
||||||
this.mtimeMs = data.mtimeMs || 0
|
|
||||||
this.ctimeMs = data.ctimeMs || 0
|
|
||||||
this.birthtimeMs = data.birthtimeMs || 0
|
|
||||||
this.addedAt = Date.now()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
module.exports = AudiobookFile
|
|
@ -1,72 +0,0 @@
|
|||||||
const { getId } = require('../../utils/index')
|
|
||||||
const Logger = require('../../Logger')
|
|
||||||
|
|
||||||
class Author {
|
|
||||||
constructor(author = null) {
|
|
||||||
this.id = null
|
|
||||||
this.name = null
|
|
||||||
this.description = null
|
|
||||||
this.asin = null
|
|
||||||
this.image = null
|
|
||||||
this.imageFullPath = null
|
|
||||||
|
|
||||||
this.createdAt = null
|
|
||||||
this.lastUpdate = null
|
|
||||||
|
|
||||||
if (author) {
|
|
||||||
this.construct(author)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
construct(author) {
|
|
||||||
this.id = author.id
|
|
||||||
this.name = author.name
|
|
||||||
this.description = author.description
|
|
||||||
this.asin = author.asin
|
|
||||||
this.image = author.image
|
|
||||||
this.imageFullPath = author.imageFullPath
|
|
||||||
|
|
||||||
this.createdAt = author.createdAt
|
|
||||||
this.lastUpdate = author.lastUpdate
|
|
||||||
}
|
|
||||||
|
|
||||||
toJSON() {
|
|
||||||
return {
|
|
||||||
id: this.id,
|
|
||||||
name: this.name,
|
|
||||||
description: this.description,
|
|
||||||
asin: this.asin,
|
|
||||||
image: this.image,
|
|
||||||
imageFullPath: this.imageFullPath,
|
|
||||||
createdAt: this.createdAt,
|
|
||||||
lastUpdate: this.lastUpdate
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setData(data) {
|
|
||||||
this.id = data.id ? data.id : getId('per')
|
|
||||||
this.name = data.name
|
|
||||||
this.description = data.description
|
|
||||||
this.asin = data.asin || null
|
|
||||||
this.image = data.image || null
|
|
||||||
this.imageFullPath = data.imageFullPath || null
|
|
||||||
this.createdAt = Date.now()
|
|
||||||
this.lastUpdate = Date.now()
|
|
||||||
}
|
|
||||||
|
|
||||||
update(payload) {
|
|
||||||
var hasUpdates = false
|
|
||||||
for (const key in payload) {
|
|
||||||
if (this[key] === undefined) continue;
|
|
||||||
if (this[key] !== payload[key]) {
|
|
||||||
hasUpdates = true
|
|
||||||
this[key] = payload[key]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (hasUpdates) {
|
|
||||||
this.lastUpdate = Date.now()
|
|
||||||
}
|
|
||||||
return hasUpdates
|
|
||||||
}
|
|
||||||
}
|
|
||||||
module.exports = Author
|
|
@ -1,417 +0,0 @@
|
|||||||
const Path = require('path')
|
|
||||||
const Logger = require('../../Logger')
|
|
||||||
const parseAuthors = require('../../utils/parseNameString')
|
|
||||||
|
|
||||||
class Book {
|
|
||||||
constructor(book = null) {
|
|
||||||
this.title = null
|
|
||||||
this.subtitle = null
|
|
||||||
this.author = null
|
|
||||||
this.authorFL = null
|
|
||||||
this.authorLF = null
|
|
||||||
this.authors = []
|
|
||||||
this.narrator = null
|
|
||||||
this.narratorFL = null
|
|
||||||
this.series = null
|
|
||||||
this.volumeNumber = null
|
|
||||||
this.publishYear = null
|
|
||||||
this.publisher = null
|
|
||||||
this.description = null
|
|
||||||
this.isbn = null
|
|
||||||
this.asin = null
|
|
||||||
this.language = null
|
|
||||||
this.cover = null
|
|
||||||
this.coverFullPath = null
|
|
||||||
this.genres = []
|
|
||||||
|
|
||||||
this.lastUpdate = null
|
|
||||||
|
|
||||||
// Should not continue looking up a cover when it is not findable
|
|
||||||
this.lastCoverSearch = null
|
|
||||||
this.lastCoverSearchTitle = null
|
|
||||||
this.lastCoverSearchAuthor = null
|
|
||||||
|
|
||||||
if (book) {
|
|
||||||
this.construct(book)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get _title() { return this.title || '' }
|
|
||||||
get _subtitle() { return this.subtitle || '' }
|
|
||||||
get _narrator() { return this.narrator || '' }
|
|
||||||
get _author() { return this.authorFL || '' }
|
|
||||||
get _series() { return this.series || '' }
|
|
||||||
get _authorsList() { return this._author.split(', ') }
|
|
||||||
get _narratorsList() { return this._narrator.split(', ') }
|
|
||||||
get _genres() { return this.genres || [] }
|
|
||||||
get _language() { return this.language || '' }
|
|
||||||
get _isbn() { return this.isbn || '' }
|
|
||||||
get _asin() { return this.asin || '' }
|
|
||||||
get genresCommaSeparated() { return this._genres.join(', ') }
|
|
||||||
|
|
||||||
get titleIgnorePrefix() {
|
|
||||||
if (this._title.toLowerCase().startsWith('the ')) {
|
|
||||||
return this._title.substr(4) + ', The'
|
|
||||||
}
|
|
||||||
return this._title
|
|
||||||
}
|
|
||||||
|
|
||||||
get seriesIgnorePrefix() {
|
|
||||||
if (this._series.toLowerCase().startsWith('the ')) {
|
|
||||||
return this._series.substr(4) + ', The'
|
|
||||||
}
|
|
||||||
return this._series
|
|
||||||
}
|
|
||||||
|
|
||||||
get shouldSearchForCover() {
|
|
||||||
if (this.cover) return false
|
|
||||||
if (this.authorFL !== this.lastCoverSearchAuthor || this.title !== this.lastCoverSearchTitle || !this.lastCoverSearch) return true
|
|
||||||
var timeSinceLastSearch = Date.now() - this.lastCoverSearch
|
|
||||||
return timeSinceLastSearch > 1000 * 60 * 60 * 24 * 7 // every 7 days do another lookup
|
|
||||||
}
|
|
||||||
|
|
||||||
construct(book) {
|
|
||||||
this.title = book.title
|
|
||||||
this.subtitle = book.subtitle || null
|
|
||||||
this.author = book.author
|
|
||||||
this.authors = (book.authors || []).map(a => ({ ...a }))
|
|
||||||
this.authorFL = book.authorFL || null
|
|
||||||
this.authorLF = book.authorLF || null
|
|
||||||
this.narrator = book.narrator || book.narrarator || null // Mispelled initially... need to catch those
|
|
||||||
this.narratorFL = book.narratorFL || null
|
|
||||||
this.series = book.series
|
|
||||||
this.volumeNumber = book.volumeNumber || null
|
|
||||||
this.publishYear = book.publishYear
|
|
||||||
this.publisher = book.publisher
|
|
||||||
this.description = book.description
|
|
||||||
this.isbn = book.isbn || null
|
|
||||||
this.asin = book.asin || null
|
|
||||||
this.language = book.language || null
|
|
||||||
this.cover = book.cover
|
|
||||||
this.coverFullPath = book.coverFullPath || null
|
|
||||||
this.genres = book.genres
|
|
||||||
this.lastUpdate = book.lastUpdate || Date.now()
|
|
||||||
this.lastCoverSearch = book.lastCoverSearch || null
|
|
||||||
this.lastCoverSearchTitle = book.lastCoverSearchTitle || null
|
|
||||||
this.lastCoverSearchAuthor = book.lastCoverSearchAuthor || null
|
|
||||||
|
|
||||||
// narratorFL added in v1.6.21 to support multi-narrators
|
|
||||||
if (this.narrator && !this.narratorFL) {
|
|
||||||
this.setParseNarrator(this.narrator)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
toJSON() {
|
|
||||||
return {
|
|
||||||
title: this.title,
|
|
||||||
subtitle: this.subtitle,
|
|
||||||
author: this.author,
|
|
||||||
authors: this.authors,
|
|
||||||
authorFL: this.authorFL,
|
|
||||||
authorLF: this.authorLF,
|
|
||||||
narrator: this.narrator,
|
|
||||||
narratorFL: this.narratorFL,
|
|
||||||
series: this.series,
|
|
||||||
volumeNumber: this.volumeNumber,
|
|
||||||
publishYear: this.publishYear,
|
|
||||||
publisher: this.publisher,
|
|
||||||
description: this.description,
|
|
||||||
isbn: this.isbn,
|
|
||||||
asin: this.asin,
|
|
||||||
language: this.language,
|
|
||||||
cover: this.cover,
|
|
||||||
coverFullPath: this.coverFullPath,
|
|
||||||
genres: this.genres,
|
|
||||||
lastUpdate: this.lastUpdate,
|
|
||||||
lastCoverSearch: this.lastCoverSearch,
|
|
||||||
lastCoverSearchTitle: this.lastCoverSearchTitle,
|
|
||||||
lastCoverSearchAuthor: this.lastCoverSearchAuthor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setParseAuthor(author) {
|
|
||||||
if (!author) {
|
|
||||||
var hasUpdated = this.authorFL || this.authorLF
|
|
||||||
this.authorFL = null
|
|
||||||
this.authorLF = null
|
|
||||||
return hasUpdated
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
var { authorLF, authorFL } = parseAuthors.parse(author)
|
|
||||||
var hasUpdated = authorLF !== this.authorLF || authorFL !== this.authorFL
|
|
||||||
this.authorFL = authorFL || null
|
|
||||||
this.authorLF = authorLF || null
|
|
||||||
return hasUpdated
|
|
||||||
} catch (err) {
|
|
||||||
Logger.error('[Book] Parse authors failed', err)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setParseNarrator(narrator) {
|
|
||||||
if (!narrator) {
|
|
||||||
var hasUpdated = this.narratorFL
|
|
||||||
this.narratorFL = null
|
|
||||||
return hasUpdated
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
var { authorFL } = parseAuthors.parse(narrator)
|
|
||||||
var hasUpdated = authorFL !== this.narratorFL
|
|
||||||
this.narratorFL = authorFL || null
|
|
||||||
return hasUpdated
|
|
||||||
} catch (err) {
|
|
||||||
Logger.error('[Book] Parse narrator failed', err)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setData(data) {
|
|
||||||
this.title = data.title || null
|
|
||||||
this.subtitle = data.subtitle || null
|
|
||||||
this.author = data.author || null
|
|
||||||
this.authors = data.authors || []
|
|
||||||
this.narrator = data.narrator || data.narrarator || null
|
|
||||||
this.series = data.series || null
|
|
||||||
this.volumeNumber = data.volumeNumber || null
|
|
||||||
this.publishYear = data.publishYear || null
|
|
||||||
this.description = data.description || null
|
|
||||||
this.isbn = data.isbn || null
|
|
||||||
this.asin = data.asin || null
|
|
||||||
this.language = data.language || null
|
|
||||||
this.cover = data.cover || null
|
|
||||||
this.coverFullPath = data.coverFullPath || null
|
|
||||||
this.genres = data.genres || []
|
|
||||||
this.lastUpdate = Date.now()
|
|
||||||
this.lastCoverSearch = data.lastCoverSearch || null
|
|
||||||
this.lastCoverSearchTitle = data.lastCoverSearchTitle || null
|
|
||||||
this.lastCoverSearchAuthor = data.lastCoverSearchAuthor || null
|
|
||||||
|
|
||||||
if (data.author) {
|
|
||||||
this.setParseAuthor(this.author)
|
|
||||||
}
|
|
||||||
if (data.narrator) {
|
|
||||||
this.setParseNarrator(this.narrator)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
update(payload) {
|
|
||||||
var hasUpdates = false
|
|
||||||
|
|
||||||
// Clean cover paths if passed
|
|
||||||
if (payload.cover) {
|
|
||||||
if (!payload.cover.startsWith('http:') && !payload.cover.startsWith('https:')) {
|
|
||||||
payload.cover = payload.cover.replace(/\\/g, '/')
|
|
||||||
if (payload.coverFullPath) payload.coverFullPath = payload.coverFullPath.replace(/\\/g, '/')
|
|
||||||
else {
|
|
||||||
Logger.warn(`[Book] "${this.title}" updating book cover to "${payload.cover}" but no full path was passed`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (payload.coverFullPath) {
|
|
||||||
Logger.warn(`[Book] "${this.title}" updating book full cover path to "${payload.coverFullPath}" but no relative path was passed`)
|
|
||||||
payload.coverFullPath = payload.coverFullPath.replace(/\\/g, '/')
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const key in payload) {
|
|
||||||
if (payload[key] === undefined) continue;
|
|
||||||
|
|
||||||
if (key === 'genres') {
|
|
||||||
if (payload['genres'] === null && this.genres !== null) {
|
|
||||||
this.genres = []
|
|
||||||
hasUpdates = true
|
|
||||||
} else if (payload['genres'].join(',') !== this.genres.join(',')) {
|
|
||||||
this.genres = payload['genres']
|
|
||||||
hasUpdates = true
|
|
||||||
}
|
|
||||||
} else if (key === 'author') {
|
|
||||||
if (this.author !== payload.author) {
|
|
||||||
this.author = payload.author || null
|
|
||||||
hasUpdates = true
|
|
||||||
}
|
|
||||||
if (this.setParseAuthor(this.author)) {
|
|
||||||
hasUpdates = true
|
|
||||||
}
|
|
||||||
} else if (key === 'narrator') {
|
|
||||||
if (this.narrator !== payload.narrator) {
|
|
||||||
this.narrator = payload.narrator || null
|
|
||||||
hasUpdates = true
|
|
||||||
}
|
|
||||||
if (this.setParseNarrator(this.narrator)) {
|
|
||||||
hasUpdates = true
|
|
||||||
}
|
|
||||||
} else if (this[key] !== undefined && payload[key] !== this[key]) {
|
|
||||||
this[key] = payload[key]
|
|
||||||
hasUpdates = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasUpdates) {
|
|
||||||
this.lastUpdate = Date.now()
|
|
||||||
}
|
|
||||||
|
|
||||||
return hasUpdates
|
|
||||||
}
|
|
||||||
|
|
||||||
updateLastCoverSearch(coverWasFound) {
|
|
||||||
this.lastCoverSearch = coverWasFound ? null : Date.now()
|
|
||||||
this.lastCoverSearchAuthor = coverWasFound ? null : this.authorFL
|
|
||||||
this.lastCoverSearchTitle = coverWasFound ? null : this.title
|
|
||||||
}
|
|
||||||
|
|
||||||
updateCover(cover, coverFullPath) {
|
|
||||||
if (!cover) return false
|
|
||||||
if (!cover.startsWith('http:') && !cover.startsWith('https:')) {
|
|
||||||
cover = cover.replace(/\\/g, '/')
|
|
||||||
this.coverFullPath = coverFullPath.replace(/\\/g, '/')
|
|
||||||
} else {
|
|
||||||
this.coverFullPath = cover
|
|
||||||
}
|
|
||||||
this.cover = cover
|
|
||||||
this.lastUpdate = Date.now()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
removeCover() {
|
|
||||||
this.cover = null
|
|
||||||
this.coverFullPath = null
|
|
||||||
this.lastUpdate = Date.now()
|
|
||||||
}
|
|
||||||
|
|
||||||
// If audiobook directory path was changed, check and update properties set from dirnames
|
|
||||||
// May be worthwhile checking if these were manually updated and not override manual updates
|
|
||||||
syncPathsUpdated(audiobookData) {
|
|
||||||
var keysToSync = ['author', 'title', 'series', 'publishYear', 'volumeNumber']
|
|
||||||
var syncPayload = {}
|
|
||||||
keysToSync.forEach((key) => {
|
|
||||||
if (audiobookData[key]) syncPayload[key] = audiobookData[key]
|
|
||||||
})
|
|
||||||
if (!Object.keys(syncPayload).length) return false
|
|
||||||
return this.update(syncPayload)
|
|
||||||
}
|
|
||||||
|
|
||||||
isSearchMatch(search) {
|
|
||||||
return this._title.toLowerCase().includes(search) || this._subtitle.toLowerCase().includes(search) || this._author.toLowerCase().includes(search) || this._series.toLowerCase().includes(search)
|
|
||||||
}
|
|
||||||
|
|
||||||
getQueryMatches(search) {
|
|
||||||
var titleMatch = this._title.toLowerCase().includes(search)
|
|
||||||
var subtitleMatch = this._subtitle.toLowerCase().includes(search)
|
|
||||||
|
|
||||||
var authorsMatched = this._authorsList.filter(auth => auth.toLowerCase().includes(search))
|
|
||||||
|
|
||||||
// var authorMatch = this._author.toLowerCase().includes(search)
|
|
||||||
var seriesMatch = this._series.toLowerCase().includes(search)
|
|
||||||
|
|
||||||
// ISBN match has to be exact to prevent isbn matches to flood results. Remove dashes since isbn might have those
|
|
||||||
var isbnMatch = this._isbn.toLowerCase().replace(/-/g, '') === search.replace(/-/g, '')
|
|
||||||
|
|
||||||
var asinMatch = this._asin.toLowerCase() === search
|
|
||||||
|
|
||||||
var bookMatchKey = titleMatch ? 'title' : subtitleMatch ? 'subtitle' : authorsMatched.length ? 'authorFL' : seriesMatch ? 'series' : isbnMatch ? 'isbn' : asinMatch ? 'asin' : false
|
|
||||||
|
|
||||||
var bookMatchText = bookMatchKey ? this[bookMatchKey] : ''
|
|
||||||
return {
|
|
||||||
book: bookMatchKey,
|
|
||||||
bookMatchText,
|
|
||||||
authors: authorsMatched.length ? authorsMatched : false,
|
|
||||||
series: seriesMatch ? this._series : false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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, overrideExistingDetails = false) {
|
|
||||||
const MetadataMapArray = [
|
|
||||||
{
|
|
||||||
tag: 'tagComposer',
|
|
||||||
key: 'narrator'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
tag: 'tagDescription',
|
|
||||||
key: 'description'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
tag: 'tagPublisher',
|
|
||||||
key: 'publisher'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
tag: 'tagDate',
|
|
||||||
key: 'publishYear'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
tag: 'tagSubtitle',
|
|
||||||
key: 'subtitle'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
tag: 'tagAlbum',
|
|
||||||
altTag: 'tagTitle',
|
|
||||||
key: 'title',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
tag: 'tagArtist',
|
|
||||||
altTag: 'tagAlbumArtist',
|
|
||||||
key: 'author'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
tag: 'tagGenre',
|
|
||||||
key: 'genres'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
tag: 'tagSeries',
|
|
||||||
key: 'series'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
tag: 'tagSeriesPart',
|
|
||||||
key: 'volumeNumber'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
tag: 'tagIsbn',
|
|
||||||
key: 'isbn'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
tag: 'tagLanguage',
|
|
||||||
key: 'language'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
tag: 'tagASIN',
|
|
||||||
key: 'asin'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
var updatePayload = {}
|
|
||||||
|
|
||||||
// Metadata is only mapped to the book if it is empty
|
|
||||||
MetadataMapArray.forEach((mapping) => {
|
|
||||||
var value = audioFileMetadata[mapping.tag]
|
|
||||||
var tagToUse = mapping.tag
|
|
||||||
if (!value && mapping.altTag) {
|
|
||||||
value = audioFileMetadata[mapping.altTag]
|
|
||||||
tagToUse = mapping.altTag
|
|
||||||
}
|
|
||||||
if (value) {
|
|
||||||
// Genres can contain multiple
|
|
||||||
if (mapping.key === 'genres' && (!this[mapping.key].length || !this[mapping.key] || overrideExistingDetails)) {
|
|
||||||
updatePayload[mapping.key] = this.parseGenresTag(audioFileMetadata[tagToUse])
|
|
||||||
// Logger.debug(`[Book] Mapping metadata to key ${tagToUse} => ${mapping.key}: ${updatePayload[mapping.key].join(',')}`)
|
|
||||||
} else if (!this[mapping.key] || overrideExistingDetails) {
|
|
||||||
updatePayload[mapping.key] = audioFileMetadata[tagToUse]
|
|
||||||
// Logger.debug(`[Book] Mapping metadata to key ${tagToUse} => ${mapping.key}: ${updatePayload[mapping.key]}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
if (Object.keys(updatePayload).length) {
|
|
||||||
return this.update(updatePayload)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
module.exports = Book
|
|
@ -1,231 +0,0 @@
|
|||||||
const Stream = require('../Stream')
|
|
||||||
// const StreamTest = require('./test/StreamTest')
|
|
||||||
const Logger = require('../../Logger')
|
|
||||||
const fs = require('fs-extra')
|
|
||||||
const Path = require('path')
|
|
||||||
|
|
||||||
class StreamManager {
|
|
||||||
constructor(db, emitter, clientEmitter) {
|
|
||||||
this.db = db
|
|
||||||
|
|
||||||
this.emitter = emitter
|
|
||||||
this.clientEmitter = clientEmitter
|
|
||||||
|
|
||||||
this.streams = []
|
|
||||||
this.StreamsPath = Path.join(global.MetadataPath, 'streams')
|
|
||||||
}
|
|
||||||
|
|
||||||
getStream(streamId) {
|
|
||||||
return this.streams.find(s => s.id === streamId)
|
|
||||||
}
|
|
||||||
|
|
||||||
removeStream(stream) {
|
|
||||||
this.streams = this.streams.filter(s => s.id !== stream.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
async openStream(client, libraryItem, transcodeOptions = {}) {
|
|
||||||
if (!client || !client.user) {
|
|
||||||
Logger.error('[StreamManager] Cannot open stream invalid client', client)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var stream = new Stream(this.StreamsPath, client, libraryItem, transcodeOptions)
|
|
||||||
|
|
||||||
stream.on('closed', () => {
|
|
||||||
this.removeStream(stream)
|
|
||||||
})
|
|
||||||
|
|
||||||
this.streams.push(stream)
|
|
||||||
|
|
||||||
await stream.generatePlaylist()
|
|
||||||
stream.start()
|
|
||||||
|
|
||||||
Logger.info('Stream Opened for client', client.user.username, 'for item', stream.itemTitle, 'with streamId', stream.id)
|
|
||||||
|
|
||||||
client.stream = stream
|
|
||||||
client.user.stream = stream.id
|
|
||||||
|
|
||||||
return stream
|
|
||||||
}
|
|
||||||
|
|
||||||
ensureStreamsDir() {
|
|
||||||
return fs.ensureDir(this.StreamsPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
removeOrphanStreamFiles(streamId) {
|
|
||||||
try {
|
|
||||||
var StreamsPath = Path.join(this.StreamsPath, streamId)
|
|
||||||
return fs.remove(StreamsPath)
|
|
||||||
} catch (error) {
|
|
||||||
Logger.debug('No orphan stream', streamId)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async removeOrphanStreams() {
|
|
||||||
try {
|
|
||||||
var dirs = await fs.readdir(this.StreamsPath)
|
|
||||||
if (!dirs || !dirs.length) return true
|
|
||||||
|
|
||||||
await Promise.all(dirs.map(async (dirname) => {
|
|
||||||
var fullPath = Path.join(this.StreamsPath, dirname)
|
|
||||||
Logger.info(`Removing Orphan Stream ${dirname}`)
|
|
||||||
return fs.remove(fullPath)
|
|
||||||
}))
|
|
||||||
return true
|
|
||||||
} catch (error) {
|
|
||||||
Logger.debug('No orphan stream', error)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async openStreamApiRequest(res, user, libraryItem) {
|
|
||||||
Logger.info(`[StreamManager] User "${user.username}" open stream request for "${libraryItem.media.metadata.title}"`)
|
|
||||||
var client = {
|
|
||||||
user
|
|
||||||
}
|
|
||||||
var stream = await this.openStream(client, libraryItem)
|
|
||||||
this.db.updateUserStream(client.user.id, stream.id)
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
libraryItemId: libraryItem.id,
|
|
||||||
startTime: stream.startTime,
|
|
||||||
streamId: stream.id,
|
|
||||||
streamUrl: stream.clientPlaylistUri
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async openStreamSocketRequest(socket, libraryItemId) {
|
|
||||||
Logger.info('[StreamManager] Open Stream Request', socket.id, libraryItemId)
|
|
||||||
var libraryItem = this.db.libraryItems.find(li => li.id === libraryItemId)
|
|
||||||
var client = socket.sheepClient
|
|
||||||
|
|
||||||
if (client.stream) {
|
|
||||||
Logger.info('Closing client stream first', client.stream.id)
|
|
||||||
await client.stream.close()
|
|
||||||
client.user.stream = null
|
|
||||||
client.stream = null
|
|
||||||
}
|
|
||||||
|
|
||||||
var stream = await this.openStream(client, libraryItem)
|
|
||||||
this.db.updateUserStream(client.user.id, stream.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
async closeStreamRequest(socket) {
|
|
||||||
Logger.info('Close Stream Request', socket.id)
|
|
||||||
var client = socket.sheepClient
|
|
||||||
if (!client || !client.stream) {
|
|
||||||
Logger.error('No stream for client', (client && client.user) ? client.user.username : 'No Client')
|
|
||||||
client.socket.emit('stream_closed', 'n/a')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// var streamId = client.stream.id
|
|
||||||
await client.stream.close()
|
|
||||||
client.user.stream = null
|
|
||||||
client.stream = null
|
|
||||||
this.db.updateUserStream(client.user.id, null)
|
|
||||||
}
|
|
||||||
|
|
||||||
async closeStreamApiRequest(userId, streamId) {
|
|
||||||
Logger.info('[StreamManager] Close Stream Api Request', streamId)
|
|
||||||
|
|
||||||
var stream = this.streams.find(s => s.id === streamId)
|
|
||||||
if (!stream) {
|
|
||||||
Logger.warn('[StreamManager] Stream not found', streamId)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!stream.client || !stream.client.user || stream.client.user.id !== userId) {
|
|
||||||
Logger.warn(`[StreamManager] Stream close request from invalid user ${userId}`, stream.client)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
stream.client.user.stream = null
|
|
||||||
stream.client.stream = null
|
|
||||||
this.db.updateUserStream(stream.client.user.id, null)
|
|
||||||
|
|
||||||
await stream.close()
|
|
||||||
|
|
||||||
this.streams = this.streams.filter(s => s.id !== streamId)
|
|
||||||
Logger.info(`[StreamManager] Stream ${streamId} closed via API request by ${userId}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
streamSync(socket, syncData) {
|
|
||||||
const client = socket.sheepClient
|
|
||||||
if (!client || !client.stream) {
|
|
||||||
Logger.error('[StreamManager] streamSync: No stream for client', (client && client.user) ? client.user.id : 'No Client')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (client.stream.id !== syncData.streamId) {
|
|
||||||
Logger.error('[StreamManager] streamSync: Stream id mismatch on stream update', syncData.streamId, client.stream.id)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (!client.user) {
|
|
||||||
Logger.error('[StreamManager] streamSync: No User for client', client)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// const { timeListened, currentTime, streamId } = syncData
|
|
||||||
var listeningSession = client.stream.syncStream(syncData)
|
|
||||||
|
|
||||||
if (listeningSession && listeningSession.timeListening > 0) {
|
|
||||||
// Save listening session
|
|
||||||
var existingListeningSession = this.db.sessions.find(s => s.id === listeningSession.id)
|
|
||||||
if (existingListeningSession) {
|
|
||||||
this.db.updateEntity('session', listeningSession)
|
|
||||||
} else {
|
|
||||||
this.db.sessions.push(listeningSession.toJSON()) // Insert right away to prevent duplicate session
|
|
||||||
this.db.insertEntity('session', listeningSession)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var userAudiobook = client.user.updateAudiobookProgressFromStream(client.stream)
|
|
||||||
this.db.updateEntity('user', client.user)
|
|
||||||
|
|
||||||
if (userAudiobook) {
|
|
||||||
this.clientEmitter(client.user.id, 'current_user_audiobook_update', {
|
|
||||||
id: userAudiobook.audiobookId,
|
|
||||||
data: userAudiobook.toJSON()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
streamSyncFromApi(req, res) {
|
|
||||||
var user = req.user
|
|
||||||
var syncData = req.body
|
|
||||||
|
|
||||||
var stream = this.streams.find(s => s.id === syncData.streamId)
|
|
||||||
if (!stream) {
|
|
||||||
Logger.error(`[StreamManager] streamSyncFromApi stream not found ${syncData.streamId}`)
|
|
||||||
return res.status(404).send('Stream not found')
|
|
||||||
}
|
|
||||||
if (stream.userToken !== user.token) {
|
|
||||||
Logger.error(`[StreamManager] streamSyncFromApi Invalid stream not owned by user`)
|
|
||||||
return res.status(500).send('Invalid stream auth')
|
|
||||||
}
|
|
||||||
|
|
||||||
var listeningSession = stream.syncStream(syncData)
|
|
||||||
|
|
||||||
if (listeningSession && listeningSession.timeListening > 0) {
|
|
||||||
// Save listening session
|
|
||||||
var existingListeningSession = this.db.sessions.find(s => s.id === listeningSession.id)
|
|
||||||
if (existingListeningSession) {
|
|
||||||
this.db.updateEntity('session', listeningSession)
|
|
||||||
} else {
|
|
||||||
this.db.sessions.push(listeningSession.toJSON()) // Insert right away to prevent duplicate session
|
|
||||||
this.db.insertEntity('session', listeningSession)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var userAudiobook = user.updateAudiobookProgressFromStream(stream)
|
|
||||||
this.db.updateEntity('user', user)
|
|
||||||
|
|
||||||
if (userAudiobook) {
|
|
||||||
this.clientEmitter(user.id, 'current_user_audiobook_update', {
|
|
||||||
id: userAudiobook.audiobookId,
|
|
||||||
data: userAudiobook.toJSON()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
res.sendStatus(200)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
module.exports = StreamManager
|
|
@ -1,136 +0,0 @@
|
|||||||
const Logger = require('../../Logger')
|
|
||||||
const AudioBookmark = require('../user/AudioBookmark')
|
|
||||||
|
|
||||||
class UserAudiobookData {
|
|
||||||
constructor(progress) {
|
|
||||||
this.audiobookId = null
|
|
||||||
|
|
||||||
this.totalDuration = null // seconds
|
|
||||||
this.progress = null // 0 to 1
|
|
||||||
this.currentTime = null // seconds
|
|
||||||
this.isRead = false
|
|
||||||
this.lastUpdate = null
|
|
||||||
this.startedAt = null
|
|
||||||
this.finishedAt = null
|
|
||||||
this.bookmarks = []
|
|
||||||
|
|
||||||
if (progress) {
|
|
||||||
this.construct(progress)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bookmarksToJSON() {
|
|
||||||
if (!this.bookmarks) return []
|
|
||||||
return this.bookmarks.filter((b) => {
|
|
||||||
if (!b.toJSON) {
|
|
||||||
Logger.error(`[UserAudiobookData] Invalid bookmark ${JSON.stringify(b)}`)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}).map(b => b.toJSON())
|
|
||||||
}
|
|
||||||
|
|
||||||
toJSON() {
|
|
||||||
return {
|
|
||||||
audiobookId: this.audiobookId,
|
|
||||||
totalDuration: this.totalDuration,
|
|
||||||
progress: this.progress,
|
|
||||||
currentTime: this.currentTime,
|
|
||||||
isRead: this.isRead,
|
|
||||||
lastUpdate: this.lastUpdate,
|
|
||||||
startedAt: this.startedAt,
|
|
||||||
finishedAt: this.finishedAt,
|
|
||||||
bookmarks: this.bookmarksToJSON()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
construct(progress) {
|
|
||||||
this.audiobookId = progress.audiobookId
|
|
||||||
this.totalDuration = progress.totalDuration
|
|
||||||
this.progress = progress.progress
|
|
||||||
this.currentTime = progress.currentTime
|
|
||||||
this.isRead = !!progress.isRead
|
|
||||||
this.lastUpdate = progress.lastUpdate
|
|
||||||
this.startedAt = progress.startedAt
|
|
||||||
this.finishedAt = progress.finishedAt || null
|
|
||||||
if (progress.bookmarks) {
|
|
||||||
this.bookmarks = progress.bookmarks.map(b => new AudioBookmark(b))
|
|
||||||
} else {
|
|
||||||
this.bookmarks = []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
updateProgressFromStream(stream) {
|
|
||||||
this.audiobookId = stream.libraryItemId
|
|
||||||
this.totalDuration = stream.totalDuration
|
|
||||||
this.progress = stream.clientProgress
|
|
||||||
this.currentTime = stream.clientCurrentTime
|
|
||||||
this.lastUpdate = Date.now()
|
|
||||||
|
|
||||||
if (!this.startedAt) {
|
|
||||||
this.startedAt = Date.now()
|
|
||||||
}
|
|
||||||
|
|
||||||
// If has < 10 seconds remaining mark as read
|
|
||||||
var timeRemaining = this.totalDuration - this.currentTime
|
|
||||||
if (timeRemaining < 10) {
|
|
||||||
this.isRead = true
|
|
||||||
this.progress = 1
|
|
||||||
this.finishedAt = Date.now()
|
|
||||||
} else {
|
|
||||||
this.isRead = false
|
|
||||||
this.finishedAt = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
update(payload) {
|
|
||||||
var hasUpdates = false
|
|
||||||
for (const key in payload) {
|
|
||||||
if (this[key] !== undefined && payload[key] !== this[key]) {
|
|
||||||
if (key === 'isRead') {
|
|
||||||
if (!payload[key]) { // Updating to Not Read - Reset progress and current time
|
|
||||||
this.finishedAt = null
|
|
||||||
this.progress = 0
|
|
||||||
this.currentTime = 0
|
|
||||||
} else { // Updating to Read
|
|
||||||
if (!this.finishedAt) this.finishedAt = Date.now()
|
|
||||||
this.progress = 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this[key] = payload[key]
|
|
||||||
hasUpdates = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!this.startedAt) {
|
|
||||||
this.startedAt = Date.now()
|
|
||||||
}
|
|
||||||
if (hasUpdates) {
|
|
||||||
this.lastUpdate = Date.now()
|
|
||||||
}
|
|
||||||
return hasUpdates
|
|
||||||
}
|
|
||||||
|
|
||||||
checkBookmarkExists(time) {
|
|
||||||
return this.bookmarks.find(bm => bm.time === time)
|
|
||||||
}
|
|
||||||
|
|
||||||
createBookmark(time, title) {
|
|
||||||
var newBookmark = new AudioBookmark()
|
|
||||||
newBookmark.setData(time, title)
|
|
||||||
this.bookmarks.push(newBookmark)
|
|
||||||
return newBookmark
|
|
||||||
}
|
|
||||||
|
|
||||||
updateBookmark(time, title) {
|
|
||||||
var bookmark = this.bookmarks.find(bm => bm.time === time)
|
|
||||||
if (!bookmark) return false
|
|
||||||
bookmark.title = title
|
|
||||||
return bookmark
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteBookmark(time) {
|
|
||||||
this.bookmarks = this.bookmarks.filter(bm => bm.time !== time)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
module.exports = UserAudiobookData
|
|
@ -1,98 +0,0 @@
|
|||||||
const Logger = require('../../Logger')
|
|
||||||
const date = require('date-and-time')
|
|
||||||
const { getId } = require('../../utils/index')
|
|
||||||
|
|
||||||
class UserListeningSession {
|
|
||||||
constructor(session) {
|
|
||||||
this.id = null
|
|
||||||
this.sessionType = 'listeningSession'
|
|
||||||
this.userId = null
|
|
||||||
this.audiobookId = null
|
|
||||||
this.audiobookTitle = null
|
|
||||||
this.audiobookAuthor = null
|
|
||||||
this.audiobookDuration = 0
|
|
||||||
this.audiobookGenres = []
|
|
||||||
|
|
||||||
this.date = null
|
|
||||||
this.dayOfWeek = null
|
|
||||||
|
|
||||||
this.timeListening = null
|
|
||||||
this.lastUpdate = null
|
|
||||||
this.startedAt = null
|
|
||||||
|
|
||||||
if (session) {
|
|
||||||
this.construct(session)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
toJSON() {
|
|
||||||
return {
|
|
||||||
id: this.id,
|
|
||||||
sessionType: this.sessionType,
|
|
||||||
userId: this.userId,
|
|
||||||
audiobookId: this.audiobookId,
|
|
||||||
audiobookTitle: this.audiobookTitle,
|
|
||||||
audiobookAuthor: this.audiobookAuthor,
|
|
||||||
audiobookDuration: this.audiobookDuration,
|
|
||||||
audiobookGenres: [...this.audiobookGenres],
|
|
||||||
date: this.date,
|
|
||||||
dayOfWeek: this.dayOfWeek,
|
|
||||||
timeListening: this.timeListening,
|
|
||||||
lastUpdate: this.lastUpdate,
|
|
||||||
startedAt: this.startedAt
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
construct(session) {
|
|
||||||
this.id = session.id
|
|
||||||
this.sessionType = session.sessionType
|
|
||||||
this.userId = session.userId
|
|
||||||
this.audiobookId = session.audiobookId
|
|
||||||
this.audiobookTitle = session.audiobookTitle
|
|
||||||
this.audiobookAuthor = session.audiobookAuthor
|
|
||||||
this.audiobookDuration = session.audiobookDuration || 0
|
|
||||||
this.audiobookGenres = session.audiobookGenres
|
|
||||||
|
|
||||||
this.date = session.date
|
|
||||||
this.dayOfWeek = session.dayOfWeek
|
|
||||||
|
|
||||||
this.timeListening = session.timeListening || null
|
|
||||||
this.lastUpdate = session.lastUpdate || null
|
|
||||||
this.startedAt = session.startedAt
|
|
||||||
}
|
|
||||||
|
|
||||||
setData(libraryItem, user) {
|
|
||||||
this.id = getId('ls')
|
|
||||||
this.userId = user.id
|
|
||||||
this.audiobookId = libraryItem.id
|
|
||||||
// TODO: For podcasts this needs to be generic
|
|
||||||
this.audiobookTitle = libraryItem.media.metadata.title || ''
|
|
||||||
this.audiobookAuthor = libraryItem.media.metadata.authorName || ''
|
|
||||||
this.audiobookDuration = libraryItem.media.duration || 0
|
|
||||||
this.audiobookGenres = [...libraryItem.media.metadata.genres]
|
|
||||||
|
|
||||||
this.timeListening = 0
|
|
||||||
this.lastUpdate = Date.now()
|
|
||||||
this.startedAt = Date.now()
|
|
||||||
}
|
|
||||||
|
|
||||||
addListeningTime(timeListened) {
|
|
||||||
if (timeListened && !isNaN(timeListened)) {
|
|
||||||
if (!this.date) {
|
|
||||||
// Set date info on first listening update
|
|
||||||
this.date = date.format(new Date(), 'YYYY-MM-DD')
|
|
||||||
this.dayOfWeek = date.format(new Date(), 'dddd')
|
|
||||||
}
|
|
||||||
|
|
||||||
this.timeListening += timeListened
|
|
||||||
this.lastUpdate = Date.now()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// New date since start of listening session
|
|
||||||
checkDateRollover() {
|
|
||||||
if (!this.date) return false
|
|
||||||
return date.format(new Date(), 'YYYY-MM-DD') !== this.date
|
|
||||||
}
|
|
||||||
}
|
|
||||||
module.exports = UserListeningSession
|
|
@ -7,9 +7,6 @@ const { PlayMethod } = require('./constants')
|
|||||||
const { getId } = require('./index')
|
const { getId } = require('./index')
|
||||||
const Logger = require('../Logger')
|
const Logger = require('../Logger')
|
||||||
|
|
||||||
const LegacyAudiobook = require('../objects/legacy/Audiobook')
|
|
||||||
const UserAudiobookData = require('../objects/legacy/UserAudiobookData')
|
|
||||||
|
|
||||||
const Library = require('../objects/Library')
|
const Library = require('../objects/Library')
|
||||||
const LibraryItem = require('../objects/LibraryItem')
|
const LibraryItem = require('../objects/LibraryItem')
|
||||||
const Book = require('../objects/mediaTypes/Book')
|
const Book = require('../objects/mediaTypes/Book')
|
||||||
@ -40,14 +37,17 @@ var existingDbSeries = []
|
|||||||
async function loadAudiobooks() {
|
async function loadAudiobooks() {
|
||||||
var audiobookPath = Path.join(global.ConfigPath, 'audiobooks')
|
var audiobookPath = Path.join(global.ConfigPath, 'audiobooks')
|
||||||
|
|
||||||
|
Logger.debug(`[dbMigration] loadAudiobooks path ${audiobookPath}`)
|
||||||
var pathExists = await fs.pathExists(audiobookPath)
|
var pathExists = await fs.pathExists(audiobookPath)
|
||||||
if (!pathExists) {
|
if (!pathExists) {
|
||||||
|
Logger.debug(`[dbMigration] loadAudiobooks path does not exist ${audiobookPath}`)
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
var audiobooksDb = new njodb.Database(audiobookPath)
|
var audiobooksDb = new njodb.Database(audiobookPath)
|
||||||
return audiobooksDb.select(() => true).then((results) => {
|
return audiobooksDb.select(() => true).then((results) => {
|
||||||
return results.data.map(a => new LegacyAudiobook(a))
|
Logger.debug(`[dbMigration] loadAudiobooks select results ${results.data.length}`)
|
||||||
|
return results.data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,7 +65,7 @@ function makeAuthorsFromOldAb(authorsList) {
|
|||||||
var newAuthor = new Author()
|
var newAuthor = new Author()
|
||||||
newAuthor.setData({ name: authorName })
|
newAuthor.setData({ name: authorName })
|
||||||
authorsToAdd.push(newAuthor)
|
authorsToAdd.push(newAuthor)
|
||||||
Logger.debug(`>>> Created new author named "${authorName}"`)
|
// Logger.debug(`>>> Created new author named "${authorName}"`)
|
||||||
return newAuthor.toJSONMinimal()
|
return newAuthor.toJSONMinimal()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -96,7 +96,8 @@ function makeFilesFromOldAb(audiobook) {
|
|||||||
var libraryFiles = []
|
var libraryFiles = []
|
||||||
var ebookFiles = []
|
var ebookFiles = []
|
||||||
|
|
||||||
var audioFiles = audiobook._audioFiles.map((af) => {
|
var _audioFiles = audiobook.audioFiles || []
|
||||||
|
var audioFiles = _audioFiles.map((af) => {
|
||||||
var fileMetadata = new FileMetadata(af)
|
var fileMetadata = new FileMetadata(af)
|
||||||
fileMetadata.path = af.fullPath
|
fileMetadata.path = af.fullPath
|
||||||
fileMetadata.relPath = getRelativePath(af.fullPath, audiobook.fullPath)
|
fileMetadata.relPath = getRelativePath(af.fullPath, audiobook.fullPath)
|
||||||
@ -118,7 +119,8 @@ function makeFilesFromOldAb(audiobook) {
|
|||||||
return newAudioFile
|
return newAudioFile
|
||||||
})
|
})
|
||||||
|
|
||||||
audiobook._otherFiles.forEach((file) => {
|
var _otherFiles = audiobook.otherFiles || []
|
||||||
|
_otherFiles.forEach((file) => {
|
||||||
var fileMetadata = new FileMetadata(file)
|
var fileMetadata = new FileMetadata(file)
|
||||||
fileMetadata.path = file.fullPath
|
fileMetadata.path = file.fullPath
|
||||||
fileMetadata.relPath = getRelativePath(file.fullPath, audiobook.fullPath)
|
fileMetadata.relPath = getRelativePath(file.fullPath, audiobook.fullPath)
|
||||||
@ -182,10 +184,10 @@ function makeLibraryItemFromOldAb(audiobook) {
|
|||||||
var bookMetadata = new BookMetadata(audiobook.book)
|
var bookMetadata = new BookMetadata(audiobook.book)
|
||||||
bookMetadata.publishedYear = audiobook.book.publishYear || null
|
bookMetadata.publishedYear = audiobook.book.publishYear || null
|
||||||
if (audiobook.book.narrator) {
|
if (audiobook.book.narrator) {
|
||||||
bookMetadata.narrators = audiobook.book._narratorsList
|
bookMetadata.narrators = (audiobook.book.narrator || '').split(', ')
|
||||||
}
|
}
|
||||||
// Returns array of json minimal authors
|
// Returns array of json minimal authors
|
||||||
bookMetadata.authors = makeAuthorsFromOldAb(audiobook.book._authorsList)
|
bookMetadata.authors = makeAuthorsFromOldAb((audiobook.book.authorFL || '').split(', '))
|
||||||
|
|
||||||
// Returns array of json minimal series
|
// Returns array of json minimal series
|
||||||
if (audiobook.book.series) {
|
if (audiobook.book.series) {
|
||||||
@ -276,7 +278,7 @@ function cleanUserObject(db, userObj) {
|
|||||||
cleanedUserPayload.bookmarks = cleanedUserPayload.bookmarks.concat(cleanedBookmarks)
|
cleanedUserPayload.bookmarks = cleanedUserPayload.bookmarks.concat(cleanedBookmarks)
|
||||||
}
|
}
|
||||||
|
|
||||||
var userAudiobookData = new UserAudiobookData(userObj.audiobooks[audiobookId]) // Legacy object
|
var userAudiobookData = userObj.audiobooks[audiobookId]
|
||||||
var liProgress = new MediaProgress() // New Progress Object
|
var liProgress = new MediaProgress() // New Progress Object
|
||||||
liProgress.id = userAudiobookData.audiobookId
|
liProgress.id = userAudiobookData.audiobookId
|
||||||
liProgress.libraryItemId = userAudiobookData.audiobookId
|
liProgress.libraryItemId = userAudiobookData.audiobookId
|
||||||
|
Loading…
Reference in New Issue
Block a user