mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-12-20 18:59:37 +00:00
Migrate tools and collapse series. fix continue shelves. remove old objects
This commit is contained in:
parent
ac159bea72
commit
108eaba022
21 changed files with 132 additions and 1341 deletions
|
|
@ -1,153 +0,0 @@
|
|||
const fs = require('../libs/fsExtra')
|
||||
const Path = require('path')
|
||||
const Logger = require('../Logger')
|
||||
const LibraryFile = require('./files/LibraryFile')
|
||||
const Book = require('./mediaTypes/Book')
|
||||
const Podcast = require('./mediaTypes/Podcast')
|
||||
const { areEquivalent, copyValue } = require('../utils/index')
|
||||
const { filePathToPOSIX, getFileTimestampsWithIno } = require('../utils/fileUtils')
|
||||
|
||||
class LibraryItem {
|
||||
constructor(libraryItem = null) {
|
||||
this.id = null
|
||||
this.ino = null // Inode
|
||||
this.oldLibraryItemId = null
|
||||
|
||||
this.libraryId = null
|
||||
this.folderId = null
|
||||
|
||||
this.path = null
|
||||
this.relPath = null
|
||||
this.isFile = false
|
||||
this.mtimeMs = null
|
||||
this.ctimeMs = null
|
||||
this.birthtimeMs = null
|
||||
this.addedAt = null
|
||||
this.updatedAt = null
|
||||
this.lastScan = null
|
||||
this.scanVersion = null
|
||||
|
||||
// Was scanned and no longer exists
|
||||
this.isMissing = false
|
||||
// Was scanned and no longer has media files
|
||||
this.isInvalid = false
|
||||
|
||||
this.mediaType = null
|
||||
this.media = null
|
||||
|
||||
/** @type {LibraryFile[]} */
|
||||
this.libraryFiles = []
|
||||
|
||||
if (libraryItem) {
|
||||
this.construct(libraryItem)
|
||||
}
|
||||
|
||||
// Temporary attributes
|
||||
this.isSavingMetadata = false
|
||||
}
|
||||
|
||||
construct(libraryItem) {
|
||||
this.id = libraryItem.id
|
||||
this.ino = libraryItem.ino || null
|
||||
this.oldLibraryItemId = libraryItem.oldLibraryItemId
|
||||
this.libraryId = libraryItem.libraryId
|
||||
this.folderId = libraryItem.folderId
|
||||
this.path = libraryItem.path
|
||||
this.relPath = libraryItem.relPath
|
||||
this.isFile = !!libraryItem.isFile
|
||||
this.mtimeMs = libraryItem.mtimeMs || 0
|
||||
this.ctimeMs = libraryItem.ctimeMs || 0
|
||||
this.birthtimeMs = libraryItem.birthtimeMs || 0
|
||||
this.addedAt = libraryItem.addedAt
|
||||
this.updatedAt = libraryItem.updatedAt || this.addedAt
|
||||
this.lastScan = libraryItem.lastScan || null
|
||||
this.scanVersion = libraryItem.scanVersion || null
|
||||
|
||||
this.isMissing = !!libraryItem.isMissing
|
||||
this.isInvalid = !!libraryItem.isInvalid
|
||||
|
||||
this.mediaType = libraryItem.mediaType
|
||||
if (this.mediaType === 'book') {
|
||||
this.media = new Book(libraryItem.media)
|
||||
} else if (this.mediaType === 'podcast') {
|
||||
this.media = new Podcast(libraryItem.media)
|
||||
}
|
||||
this.media.libraryItemId = this.id
|
||||
|
||||
this.libraryFiles = libraryItem.libraryFiles.map((f) => new LibraryFile(f))
|
||||
|
||||
// Migration for v2.2.23 to set ebook library files as supplementary
|
||||
if (this.isBook && this.media.ebookFile) {
|
||||
for (const libraryFile of this.libraryFiles) {
|
||||
if (libraryFile.isEBookFile && libraryFile.isSupplementary === null) {
|
||||
libraryFile.isSupplementary = this.media.ebookFile.ino !== libraryFile.ino
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
return {
|
||||
id: this.id,
|
||||
ino: this.ino,
|
||||
oldLibraryItemId: this.oldLibraryItemId,
|
||||
libraryId: this.libraryId,
|
||||
folderId: this.folderId,
|
||||
path: this.path,
|
||||
relPath: this.relPath,
|
||||
isFile: this.isFile,
|
||||
mtimeMs: this.mtimeMs,
|
||||
ctimeMs: this.ctimeMs,
|
||||
birthtimeMs: this.birthtimeMs,
|
||||
addedAt: this.addedAt,
|
||||
updatedAt: this.updatedAt,
|
||||
lastScan: this.lastScan,
|
||||
scanVersion: this.scanVersion,
|
||||
isMissing: !!this.isMissing,
|
||||
isInvalid: !!this.isInvalid,
|
||||
mediaType: this.mediaType,
|
||||
media: this.media.toJSON(),
|
||||
libraryFiles: this.libraryFiles.map((f) => f.toJSON())
|
||||
}
|
||||
}
|
||||
|
||||
toJSONMinified() {
|
||||
return {
|
||||
id: this.id,
|
||||
ino: this.ino,
|
||||
oldLibraryItemId: this.oldLibraryItemId,
|
||||
libraryId: this.libraryId,
|
||||
folderId: this.folderId,
|
||||
path: this.path,
|
||||
relPath: this.relPath,
|
||||
isFile: this.isFile,
|
||||
mtimeMs: this.mtimeMs,
|
||||
ctimeMs: this.ctimeMs,
|
||||
birthtimeMs: this.birthtimeMs,
|
||||
addedAt: this.addedAt,
|
||||
updatedAt: this.updatedAt,
|
||||
isMissing: !!this.isMissing,
|
||||
isInvalid: !!this.isInvalid,
|
||||
mediaType: this.mediaType,
|
||||
media: this.media.toJSONMinified(),
|
||||
numFiles: this.libraryFiles.length,
|
||||
size: this.size
|
||||
}
|
||||
}
|
||||
|
||||
get isPodcast() {
|
||||
return this.mediaType === 'podcast'
|
||||
}
|
||||
get isBook() {
|
||||
return this.mediaType === 'book'
|
||||
}
|
||||
get size() {
|
||||
let total = 0
|
||||
this.libraryFiles.forEach((lf) => (total += lf.metadata.size))
|
||||
return total
|
||||
}
|
||||
get hasAudioFiles() {
|
||||
return this.libraryFiles.some((lf) => lf.fileType === 'audio')
|
||||
}
|
||||
}
|
||||
module.exports = LibraryItem
|
||||
|
|
@ -1,149 +0,0 @@
|
|||
const { areEquivalent, copyValue } = require('../../utils/index')
|
||||
const AudioFile = require('../files/AudioFile')
|
||||
const AudioTrack = require('../files/AudioTrack')
|
||||
|
||||
class PodcastEpisode {
|
||||
constructor(episode) {
|
||||
this.libraryItemId = null
|
||||
this.podcastId = null
|
||||
this.id = null
|
||||
this.oldEpisodeId = null
|
||||
this.index = null
|
||||
|
||||
this.season = null
|
||||
this.episode = null
|
||||
this.episodeType = null
|
||||
this.title = null
|
||||
this.subtitle = null
|
||||
this.description = null
|
||||
this.enclosure = null
|
||||
this.guid = null
|
||||
this.pubDate = null
|
||||
this.chapters = []
|
||||
|
||||
this.audioFile = null
|
||||
this.publishedAt = null
|
||||
this.addedAt = null
|
||||
this.updatedAt = null
|
||||
|
||||
if (episode) {
|
||||
this.construct(episode)
|
||||
}
|
||||
}
|
||||
|
||||
construct(episode) {
|
||||
this.libraryItemId = episode.libraryItemId
|
||||
this.podcastId = episode.podcastId
|
||||
this.id = episode.id
|
||||
this.oldEpisodeId = episode.oldEpisodeId
|
||||
this.index = episode.index
|
||||
this.season = episode.season
|
||||
this.episode = episode.episode
|
||||
this.episodeType = episode.episodeType
|
||||
this.title = episode.title
|
||||
this.subtitle = episode.subtitle
|
||||
this.description = episode.description
|
||||
this.enclosure = episode.enclosure ? { ...episode.enclosure } : null
|
||||
this.guid = episode.guid || null
|
||||
this.pubDate = episode.pubDate
|
||||
this.chapters = episode.chapters?.map((ch) => ({ ...ch })) || []
|
||||
this.audioFile = episode.audioFile ? new AudioFile(episode.audioFile) : null
|
||||
this.publishedAt = episode.publishedAt
|
||||
this.addedAt = episode.addedAt
|
||||
this.updatedAt = episode.updatedAt
|
||||
|
||||
if (this.audioFile) {
|
||||
this.audioFile.index = 1 // Only 1 audio file per episode
|
||||
}
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
return {
|
||||
libraryItemId: this.libraryItemId,
|
||||
podcastId: this.podcastId,
|
||||
id: this.id,
|
||||
oldEpisodeId: this.oldEpisodeId,
|
||||
index: this.index,
|
||||
season: this.season,
|
||||
episode: this.episode,
|
||||
episodeType: this.episodeType,
|
||||
title: this.title,
|
||||
subtitle: this.subtitle,
|
||||
description: this.description,
|
||||
enclosure: this.enclosure ? { ...this.enclosure } : null,
|
||||
guid: this.guid,
|
||||
pubDate: this.pubDate,
|
||||
chapters: this.chapters.map((ch) => ({ ...ch })),
|
||||
audioFile: this.audioFile?.toJSON() || null,
|
||||
publishedAt: this.publishedAt,
|
||||
addedAt: this.addedAt,
|
||||
updatedAt: this.updatedAt
|
||||
}
|
||||
}
|
||||
|
||||
toJSONExpanded() {
|
||||
return {
|
||||
libraryItemId: this.libraryItemId,
|
||||
podcastId: this.podcastId,
|
||||
id: this.id,
|
||||
oldEpisodeId: this.oldEpisodeId,
|
||||
index: this.index,
|
||||
season: this.season,
|
||||
episode: this.episode,
|
||||
episodeType: this.episodeType,
|
||||
title: this.title,
|
||||
subtitle: this.subtitle,
|
||||
description: this.description,
|
||||
enclosure: this.enclosure ? { ...this.enclosure } : null,
|
||||
guid: this.guid,
|
||||
pubDate: this.pubDate,
|
||||
chapters: this.chapters.map((ch) => ({ ...ch })),
|
||||
audioFile: this.audioFile?.toJSON() || null,
|
||||
audioTrack: this.audioTrack?.toJSON() || null,
|
||||
publishedAt: this.publishedAt,
|
||||
addedAt: this.addedAt,
|
||||
updatedAt: this.updatedAt,
|
||||
duration: this.duration,
|
||||
size: this.size
|
||||
}
|
||||
}
|
||||
|
||||
get audioTrack() {
|
||||
if (!this.audioFile) return null
|
||||
const audioTrack = new AudioTrack()
|
||||
audioTrack.setData(this.libraryItemId, this.audioFile, 0)
|
||||
return audioTrack
|
||||
}
|
||||
get tracks() {
|
||||
return [this.audioTrack]
|
||||
}
|
||||
get duration() {
|
||||
return this.audioFile?.duration || 0
|
||||
}
|
||||
get size() {
|
||||
return this.audioFile?.metadata.size || 0
|
||||
}
|
||||
get enclosureUrl() {
|
||||
return this.enclosure?.url || null
|
||||
}
|
||||
|
||||
update(payload) {
|
||||
let hasUpdates = false
|
||||
for (const key in this.toJSON()) {
|
||||
let newValue = payload[key]
|
||||
if (newValue === '') newValue = null
|
||||
let existingValue = this[key]
|
||||
if (existingValue === '') existingValue = null
|
||||
|
||||
if (newValue != undefined && !areEquivalent(newValue, existingValue)) {
|
||||
this[key] = copyValue(newValue)
|
||||
hasUpdates = true
|
||||
}
|
||||
}
|
||||
if (hasUpdates) {
|
||||
this.updatedAt = Date.now()
|
||||
}
|
||||
return hasUpdates
|
||||
}
|
||||
}
|
||||
module.exports = PodcastEpisode
|
||||
|
|
@ -1,138 +0,0 @@
|
|||
const Logger = require('../../Logger')
|
||||
const BookMetadata = require('../metadata/BookMetadata')
|
||||
const { areEquivalent, copyValue } = require('../../utils/index')
|
||||
const { filePathToPOSIX } = require('../../utils/fileUtils')
|
||||
const AudioFile = require('../files/AudioFile')
|
||||
const AudioTrack = require('../files/AudioTrack')
|
||||
const EBookFile = require('../files/EBookFile')
|
||||
|
||||
class Book {
|
||||
constructor(book) {
|
||||
this.id = null
|
||||
this.libraryItemId = null
|
||||
this.metadata = null
|
||||
|
||||
this.coverPath = null
|
||||
this.tags = []
|
||||
|
||||
this.audioFiles = []
|
||||
this.chapters = []
|
||||
this.ebookFile = null
|
||||
|
||||
this.lastCoverSearch = null
|
||||
this.lastCoverSearchQuery = null
|
||||
|
||||
if (book) {
|
||||
this.construct(book)
|
||||
}
|
||||
}
|
||||
|
||||
construct(book) {
|
||||
this.id = book.id
|
||||
this.libraryItemId = book.libraryItemId
|
||||
this.metadata = new BookMetadata(book.metadata)
|
||||
this.coverPath = book.coverPath
|
||||
this.tags = [...book.tags]
|
||||
this.audioFiles = book.audioFiles.map((f) => new AudioFile(f))
|
||||
this.chapters = book.chapters.map((c) => ({ ...c }))
|
||||
this.ebookFile = book.ebookFile ? new EBookFile(book.ebookFile) : null
|
||||
this.lastCoverSearch = book.lastCoverSearch || null
|
||||
this.lastCoverSearchQuery = book.lastCoverSearchQuery || null
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
return {
|
||||
id: this.id,
|
||||
libraryItemId: this.libraryItemId,
|
||||
metadata: this.metadata.toJSON(),
|
||||
coverPath: this.coverPath,
|
||||
tags: [...this.tags],
|
||||
audioFiles: this.audioFiles.map((f) => f.toJSON()),
|
||||
chapters: this.chapters.map((c) => ({ ...c })),
|
||||
ebookFile: this.ebookFile ? this.ebookFile.toJSON() : null
|
||||
}
|
||||
}
|
||||
|
||||
toJSONMinified() {
|
||||
return {
|
||||
id: this.id,
|
||||
metadata: this.metadata.toJSONMinified(),
|
||||
coverPath: this.coverPath,
|
||||
tags: [...this.tags],
|
||||
numTracks: this.tracks.length,
|
||||
numAudioFiles: this.audioFiles.length,
|
||||
numChapters: this.chapters.length,
|
||||
duration: this.duration,
|
||||
size: this.size,
|
||||
ebookFormat: this.ebookFile?.ebookFormat
|
||||
}
|
||||
}
|
||||
|
||||
toJSONForMetadataFile() {
|
||||
return {
|
||||
tags: [...this.tags],
|
||||
chapters: this.chapters.map((c) => ({ ...c })),
|
||||
...this.metadata.toJSONForMetadataFile()
|
||||
}
|
||||
}
|
||||
|
||||
get size() {
|
||||
var total = 0
|
||||
this.audioFiles.forEach((af) => (total += af.metadata.size))
|
||||
if (this.ebookFile) {
|
||||
total += this.ebookFile.metadata.size
|
||||
}
|
||||
return total
|
||||
}
|
||||
get includedAudioFiles() {
|
||||
return this.audioFiles.filter((af) => !af.exclude)
|
||||
}
|
||||
get tracks() {
|
||||
let startOffset = 0
|
||||
return this.includedAudioFiles.map((af) => {
|
||||
const audioTrack = new AudioTrack()
|
||||
audioTrack.setData(this.libraryItemId, af, startOffset)
|
||||
startOffset += audioTrack.duration
|
||||
return audioTrack
|
||||
})
|
||||
}
|
||||
get duration() {
|
||||
let total = 0
|
||||
this.tracks.forEach((track) => (total += track.duration))
|
||||
return total
|
||||
}
|
||||
get numTracks() {
|
||||
return this.tracks.length
|
||||
}
|
||||
get isEBookOnly() {
|
||||
return this.ebookFile && !this.numTracks
|
||||
}
|
||||
|
||||
update(payload) {
|
||||
const json = this.toJSON()
|
||||
|
||||
let hasUpdates = false
|
||||
for (const key in json) {
|
||||
if (payload[key] !== undefined) {
|
||||
if (key === 'metadata') {
|
||||
if (this.metadata.update(payload.metadata)) {
|
||||
hasUpdates = true
|
||||
}
|
||||
} else if (!areEquivalent(payload[key], json[key])) {
|
||||
this[key] = copyValue(payload[key])
|
||||
Logger.debug('[Book] Key updated', key, this[key])
|
||||
hasUpdates = true
|
||||
}
|
||||
}
|
||||
}
|
||||
return hasUpdates
|
||||
}
|
||||
|
||||
updateCover(coverPath) {
|
||||
coverPath = filePathToPOSIX(coverPath)
|
||||
if (this.coverPath === coverPath) return false
|
||||
this.coverPath = coverPath
|
||||
return true
|
||||
}
|
||||
}
|
||||
module.exports = Book
|
||||
|
|
@ -1,161 +0,0 @@
|
|||
const Logger = require('../../Logger')
|
||||
const PodcastEpisode = require('../entities/PodcastEpisode')
|
||||
const PodcastMetadata = require('../metadata/PodcastMetadata')
|
||||
const { areEquivalent, copyValue } = require('../../utils/index')
|
||||
const { filePathToPOSIX } = require('../../utils/fileUtils')
|
||||
|
||||
class Podcast {
|
||||
constructor(podcast) {
|
||||
this.id = null
|
||||
this.libraryItemId = null
|
||||
this.metadata = null
|
||||
this.coverPath = null
|
||||
this.tags = []
|
||||
this.episodes = []
|
||||
|
||||
this.autoDownloadEpisodes = false
|
||||
this.autoDownloadSchedule = null
|
||||
this.lastEpisodeCheck = 0
|
||||
this.maxEpisodesToKeep = 0
|
||||
this.maxNewEpisodesToDownload = 3
|
||||
|
||||
this.lastCoverSearch = null
|
||||
this.lastCoverSearchQuery = null
|
||||
|
||||
if (podcast) {
|
||||
this.construct(podcast)
|
||||
}
|
||||
}
|
||||
|
||||
construct(podcast) {
|
||||
this.id = podcast.id
|
||||
this.libraryItemId = podcast.libraryItemId
|
||||
this.metadata = new PodcastMetadata(podcast.metadata)
|
||||
this.coverPath = podcast.coverPath
|
||||
this.tags = [...podcast.tags]
|
||||
this.episodes = podcast.episodes.map((e) => {
|
||||
var podcastEpisode = new PodcastEpisode(e)
|
||||
podcastEpisode.libraryItemId = this.libraryItemId
|
||||
return podcastEpisode
|
||||
})
|
||||
this.autoDownloadEpisodes = !!podcast.autoDownloadEpisodes
|
||||
this.autoDownloadSchedule = podcast.autoDownloadSchedule || '0 * * * *' // Added in 2.1.3 so default to hourly
|
||||
this.lastEpisodeCheck = podcast.lastEpisodeCheck || 0
|
||||
this.maxEpisodesToKeep = podcast.maxEpisodesToKeep || 0
|
||||
|
||||
// Default is 3 but 0 is allowed
|
||||
if (typeof podcast.maxNewEpisodesToDownload !== 'number') {
|
||||
this.maxNewEpisodesToDownload = 3
|
||||
} else {
|
||||
this.maxNewEpisodesToDownload = podcast.maxNewEpisodesToDownload
|
||||
}
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
return {
|
||||
id: this.id,
|
||||
libraryItemId: this.libraryItemId,
|
||||
metadata: this.metadata.toJSON(),
|
||||
coverPath: this.coverPath,
|
||||
tags: [...this.tags],
|
||||
episodes: this.episodes.map((e) => e.toJSON()),
|
||||
autoDownloadEpisodes: this.autoDownloadEpisodes,
|
||||
autoDownloadSchedule: this.autoDownloadSchedule,
|
||||
lastEpisodeCheck: this.lastEpisodeCheck,
|
||||
maxEpisodesToKeep: this.maxEpisodesToKeep,
|
||||
maxNewEpisodesToDownload: this.maxNewEpisodesToDownload
|
||||
}
|
||||
}
|
||||
|
||||
toJSONMinified() {
|
||||
return {
|
||||
id: this.id,
|
||||
metadata: this.metadata.toJSONMinified(),
|
||||
coverPath: this.coverPath,
|
||||
tags: [...this.tags],
|
||||
numEpisodes: this.episodes.length,
|
||||
autoDownloadEpisodes: this.autoDownloadEpisodes,
|
||||
autoDownloadSchedule: this.autoDownloadSchedule,
|
||||
lastEpisodeCheck: this.lastEpisodeCheck,
|
||||
maxEpisodesToKeep: this.maxEpisodesToKeep,
|
||||
maxNewEpisodesToDownload: this.maxNewEpisodesToDownload,
|
||||
size: this.size
|
||||
}
|
||||
}
|
||||
|
||||
toJSONForMetadataFile() {
|
||||
return {
|
||||
tags: [...this.tags],
|
||||
title: this.metadata.title,
|
||||
author: this.metadata.author,
|
||||
description: this.metadata.description,
|
||||
releaseDate: this.metadata.releaseDate,
|
||||
genres: [...this.metadata.genres],
|
||||
feedURL: this.metadata.feedUrl,
|
||||
imageURL: this.metadata.imageUrl,
|
||||
itunesPageURL: this.metadata.itunesPageUrl,
|
||||
itunesId: this.metadata.itunesId,
|
||||
itunesArtistId: this.metadata.itunesArtistId,
|
||||
explicit: this.metadata.explicit,
|
||||
language: this.metadata.language,
|
||||
podcastType: this.metadata.type
|
||||
}
|
||||
}
|
||||
|
||||
get size() {
|
||||
var total = 0
|
||||
this.episodes.forEach((ep) => (total += ep.size))
|
||||
return total
|
||||
}
|
||||
get duration() {
|
||||
let total = 0
|
||||
this.episodes.forEach((ep) => (total += ep.duration))
|
||||
return total
|
||||
}
|
||||
get numTracks() {
|
||||
return this.episodes.length
|
||||
}
|
||||
|
||||
update(payload) {
|
||||
var json = this.toJSON()
|
||||
delete json.episodes // do not update media entities here
|
||||
var hasUpdates = false
|
||||
for (const key in json) {
|
||||
if (payload[key] !== undefined) {
|
||||
if (key === 'metadata') {
|
||||
if (this.metadata.update(payload.metadata)) {
|
||||
hasUpdates = true
|
||||
}
|
||||
} else if (!areEquivalent(payload[key], json[key])) {
|
||||
this[key] = copyValue(payload[key])
|
||||
Logger.debug('[Podcast] Key updated', key, this[key])
|
||||
hasUpdates = true
|
||||
}
|
||||
}
|
||||
}
|
||||
return hasUpdates
|
||||
}
|
||||
|
||||
updateEpisode(id, payload) {
|
||||
var episode = this.episodes.find((ep) => ep.id == id)
|
||||
if (!episode) return false
|
||||
return episode.update(payload)
|
||||
}
|
||||
|
||||
updateCover(coverPath) {
|
||||
coverPath = filePathToPOSIX(coverPath)
|
||||
if (this.coverPath === coverPath) return false
|
||||
this.coverPath = coverPath
|
||||
return true
|
||||
}
|
||||
|
||||
getEpisode(episodeId) {
|
||||
if (!episodeId) return null
|
||||
|
||||
// Support old episode ids for mobile downloads
|
||||
if (episodeId.startsWith('ep_')) return this.episodes.find((ep) => ep.oldEpisodeId == episodeId)
|
||||
|
||||
return this.episodes.find((ep) => ep.id == episodeId)
|
||||
}
|
||||
}
|
||||
module.exports = Podcast
|
||||
|
|
@ -1,154 +0,0 @@
|
|||
const Logger = require('../../Logger')
|
||||
const { areEquivalent, copyValue, getTitleIgnorePrefix, getTitlePrefixAtEnd } = require('../../utils/index')
|
||||
const parseNameString = require('../../utils/parsers/parseNameString')
|
||||
class BookMetadata {
|
||||
constructor(metadata) {
|
||||
this.title = null
|
||||
this.subtitle = null
|
||||
this.authors = []
|
||||
this.narrators = [] // Array of strings
|
||||
this.series = []
|
||||
this.genres = [] // Array of strings
|
||||
this.publishedYear = null
|
||||
this.publishedDate = null
|
||||
this.publisher = null
|
||||
this.description = null
|
||||
this.isbn = null
|
||||
this.asin = null
|
||||
this.language = null
|
||||
this.explicit = false
|
||||
this.abridged = false
|
||||
|
||||
if (metadata) {
|
||||
this.construct(metadata)
|
||||
}
|
||||
}
|
||||
|
||||
construct(metadata) {
|
||||
this.title = metadata.title
|
||||
this.subtitle = metadata.subtitle
|
||||
this.authors = metadata.authors?.map ? metadata.authors.map((a) => ({ ...a })) : []
|
||||
this.narrators = metadata.narrators ? [...metadata.narrators].filter((n) => n) : []
|
||||
this.series = metadata.series?.map
|
||||
? metadata.series.map((s) => ({
|
||||
...s,
|
||||
name: s.name || 'No Title'
|
||||
}))
|
||||
: []
|
||||
this.genres = metadata.genres ? [...metadata.genres] : []
|
||||
this.publishedYear = metadata.publishedYear || null
|
||||
this.publishedDate = metadata.publishedDate || null
|
||||
this.publisher = metadata.publisher
|
||||
this.description = metadata.description
|
||||
this.isbn = metadata.isbn
|
||||
this.asin = metadata.asin
|
||||
this.language = metadata.language
|
||||
this.explicit = !!metadata.explicit
|
||||
this.abridged = !!metadata.abridged
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
return {
|
||||
title: this.title,
|
||||
subtitle: this.subtitle,
|
||||
authors: this.authors.map((a) => ({ ...a })), // Author JSONMinimal with name and id
|
||||
narrators: [...this.narrators],
|
||||
series: this.series.map((s) => ({ ...s })), // Series JSONMinimal with name, id and sequence
|
||||
genres: [...this.genres],
|
||||
publishedYear: this.publishedYear,
|
||||
publishedDate: this.publishedDate,
|
||||
publisher: this.publisher,
|
||||
description: this.description,
|
||||
isbn: this.isbn,
|
||||
asin: this.asin,
|
||||
language: this.language,
|
||||
explicit: this.explicit,
|
||||
abridged: this.abridged
|
||||
}
|
||||
}
|
||||
|
||||
toJSONMinified() {
|
||||
return {
|
||||
title: this.title,
|
||||
titleIgnorePrefix: this.titlePrefixAtEnd,
|
||||
subtitle: this.subtitle,
|
||||
authorName: this.authorName,
|
||||
authorNameLF: this.authorNameLF,
|
||||
narratorName: this.narratorName,
|
||||
seriesName: this.seriesName,
|
||||
genres: [...this.genres],
|
||||
publishedYear: this.publishedYear,
|
||||
publishedDate: this.publishedDate,
|
||||
publisher: this.publisher,
|
||||
description: this.description,
|
||||
isbn: this.isbn,
|
||||
asin: this.asin,
|
||||
language: this.language,
|
||||
explicit: this.explicit,
|
||||
abridged: this.abridged
|
||||
}
|
||||
}
|
||||
|
||||
toJSONForMetadataFile() {
|
||||
const json = this.toJSON()
|
||||
json.authors = json.authors.map((au) => au.name)
|
||||
json.series = json.series.map((se) => {
|
||||
if (!se.sequence) return se.name
|
||||
return `${se.name} #${se.sequence}`
|
||||
})
|
||||
return json
|
||||
}
|
||||
|
||||
clone() {
|
||||
return new BookMetadata(this.toJSON())
|
||||
}
|
||||
|
||||
get titleIgnorePrefix() {
|
||||
return getTitleIgnorePrefix(this.title)
|
||||
}
|
||||
get titlePrefixAtEnd() {
|
||||
return getTitlePrefixAtEnd(this.title)
|
||||
}
|
||||
get authorName() {
|
||||
if (!this.authors.length) return ''
|
||||
return this.authors.map((au) => au.name).join(', ')
|
||||
}
|
||||
get authorNameLF() {
|
||||
// Last, First
|
||||
if (!this.authors.length) return ''
|
||||
return this.authors.map((au) => parseNameString.nameToLastFirst(au.name)).join(', ')
|
||||
}
|
||||
get seriesName() {
|
||||
if (!this.series.length) return ''
|
||||
return this.series
|
||||
.map((se) => {
|
||||
if (!se.sequence) return se.name
|
||||
return `${se.name} #${se.sequence}`
|
||||
})
|
||||
.join(', ')
|
||||
}
|
||||
get narratorName() {
|
||||
return this.narrators.join(', ')
|
||||
}
|
||||
|
||||
getSeries(seriesId) {
|
||||
return this.series.find((se) => se.id == seriesId)
|
||||
}
|
||||
|
||||
update(payload) {
|
||||
const json = this.toJSON()
|
||||
let hasUpdates = false
|
||||
|
||||
for (const key in json) {
|
||||
if (payload[key] !== undefined) {
|
||||
if (!areEquivalent(payload[key], json[key])) {
|
||||
this[key] = copyValue(payload[key])
|
||||
Logger.debug('[BookMetadata] Key updated', key, this[key])
|
||||
hasUpdates = true
|
||||
}
|
||||
}
|
||||
}
|
||||
return hasUpdates
|
||||
}
|
||||
}
|
||||
module.exports = BookMetadata
|
||||
|
|
@ -1,105 +0,0 @@
|
|||
const Logger = require('../../Logger')
|
||||
const { areEquivalent, copyValue, getTitleIgnorePrefix, getTitlePrefixAtEnd } = require('../../utils/index')
|
||||
|
||||
class PodcastMetadata {
|
||||
constructor(metadata) {
|
||||
this.title = null
|
||||
this.author = null
|
||||
this.description = null
|
||||
this.releaseDate = null
|
||||
this.genres = []
|
||||
this.feedUrl = null
|
||||
this.imageUrl = null
|
||||
this.itunesPageUrl = null
|
||||
this.itunesId = null
|
||||
this.itunesArtistId = null
|
||||
this.explicit = false
|
||||
this.language = null
|
||||
this.type = null
|
||||
|
||||
if (metadata) {
|
||||
this.construct(metadata)
|
||||
}
|
||||
}
|
||||
|
||||
construct(metadata) {
|
||||
this.title = metadata.title
|
||||
this.author = metadata.author
|
||||
this.description = metadata.description
|
||||
this.releaseDate = metadata.releaseDate
|
||||
this.genres = [...metadata.genres]
|
||||
this.feedUrl = metadata.feedUrl
|
||||
this.imageUrl = metadata.imageUrl
|
||||
this.itunesPageUrl = metadata.itunesPageUrl
|
||||
this.itunesId = metadata.itunesId
|
||||
this.itunesArtistId = metadata.itunesArtistId
|
||||
this.explicit = metadata.explicit
|
||||
this.language = metadata.language || null
|
||||
this.type = metadata.type || 'episodic'
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
return {
|
||||
title: this.title,
|
||||
author: this.author,
|
||||
description: this.description,
|
||||
releaseDate: this.releaseDate,
|
||||
genres: [...this.genres],
|
||||
feedUrl: this.feedUrl,
|
||||
imageUrl: this.imageUrl,
|
||||
itunesPageUrl: this.itunesPageUrl,
|
||||
itunesId: this.itunesId,
|
||||
itunesArtistId: this.itunesArtistId,
|
||||
explicit: this.explicit,
|
||||
language: this.language,
|
||||
type: this.type
|
||||
}
|
||||
}
|
||||
|
||||
toJSONMinified() {
|
||||
return {
|
||||
title: this.title,
|
||||
titleIgnorePrefix: this.titlePrefixAtEnd,
|
||||
author: this.author,
|
||||
description: this.description,
|
||||
releaseDate: this.releaseDate,
|
||||
genres: [...this.genres],
|
||||
feedUrl: this.feedUrl,
|
||||
imageUrl: this.imageUrl,
|
||||
itunesPageUrl: this.itunesPageUrl,
|
||||
itunesId: this.itunesId,
|
||||
itunesArtistId: this.itunesArtistId,
|
||||
explicit: this.explicit,
|
||||
language: this.language,
|
||||
type: this.type
|
||||
}
|
||||
}
|
||||
|
||||
clone() {
|
||||
return new PodcastMetadata(this.toJSON())
|
||||
}
|
||||
|
||||
get titleIgnorePrefix() {
|
||||
return getTitleIgnorePrefix(this.title)
|
||||
}
|
||||
|
||||
get titlePrefixAtEnd() {
|
||||
return getTitlePrefixAtEnd(this.title)
|
||||
}
|
||||
|
||||
update(payload) {
|
||||
const json = this.toJSON()
|
||||
let hasUpdates = false
|
||||
for (const key in json) {
|
||||
if (payload[key] !== undefined) {
|
||||
if (!areEquivalent(payload[key], json[key])) {
|
||||
this[key] = copyValue(payload[key])
|
||||
Logger.debug('[PodcastMetadata] Key updated', key, this[key])
|
||||
hasUpdates = true
|
||||
}
|
||||
}
|
||||
}
|
||||
return hasUpdates
|
||||
}
|
||||
}
|
||||
module.exports = PodcastMetadata
|
||||
Loading…
Add table
Add a link
Reference in a new issue