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