Added support for deviceId

This commit is contained in:
Jason Axley 2025-08-22 11:37:04 -07:00
parent aae808544e
commit 423f2d311e
9 changed files with 369 additions and 222 deletions

View file

@ -8,10 +8,11 @@ const libraryItemsBookFilters = require('../utils/queries/libraryItemsBookFilter
/**
* @typedef EBookFileObject
* @property {string} ino
* @property {string} deviceId
* @property {string} ebookFormat
* @property {number} addedAt
* @property {number} updatedAt
* @property {{filename:string, ext:string, path:string, relPath:strFing, size:number, mtimeMs:number, ctimeMs:number, birthtimeMs:number}} metadata
* @property {{filename:string, ext:string, path:string, relPath:string, size:number, mtimeMs:number, ctimeMs:number, birthtimeMs:number}} metadata
*/
/**

View file

@ -2,6 +2,9 @@ const packageJson = require('../../package.json')
const { LogLevel } = require('../utils/constants')
const LibraryItem = require('../models/LibraryItem')
const globals = require('../utils/globals')
const LibraryFile = require('../objects/files/LibraryFile')
const LibraryScan = require('./LibraryScan')
const ScanLogger = require('./ScanLogger')
class LibraryItemScanData {
/**
@ -226,13 +229,7 @@ class LibraryItemScanData {
for (const existingLibraryFile of existingLibraryItem.libraryFiles) {
// Find matching library file using path first and fallback to using inode value
let matchingLibraryFile = this.libraryFiles.find((lf) => lf.metadata.path === existingLibraryFile.metadata.path)
if (!matchingLibraryFile) {
matchingLibraryFile = this.libraryFiles.find((lf) => lf.ino === existingLibraryFile.ino)
if (matchingLibraryFile) {
libraryScan.addLog(LogLevel.INFO, `Library file with path "${existingLibraryFile.metadata.path}" not found, but found file with matching inode value "${existingLibraryFile.ino}" at path "${matchingLibraryFile.metadata.path}"`)
}
}
let matchingLibraryFile = this.findMatchingLibraryFileByPathOrInodeAndDeviceId(existingLibraryFile, libraryScan)
if (!matchingLibraryFile) {
// Library file removed
@ -278,10 +275,9 @@ class LibraryItemScanData {
existingLibraryItem.changed('libraryFiles', true)
}
await existingLibraryItem.save()
return true
}
return false
return this.hasChanges
}
/**
@ -320,6 +316,23 @@ class LibraryItemScanData {
return hasChanges
}
/**
* @returns {LibraryFile | undefined} if [existingLibraryFile] matches an existing libraryFile
* @param {LibraryItem.LibraryFileObject} [existingLibraryFile]
* @param {LibraryScan | ScanLogger} [libraryScan]
*/
findMatchingLibraryFileByPathOrInodeAndDeviceId(existingLibraryFile, libraryScan) {
if (!existingLibraryFile) return
let matchingLibraryFile = this.libraryFiles.find((lf) => lf.metadata.path === existingLibraryFile.metadata.path)
if (!matchingLibraryFile) {
matchingLibraryFile = this.libraryFiles.find((lf) => lf.ino === existingLibraryFile.ino && lf.deviceId === existingLibraryFile.deviceId)
if (matchingLibraryFile) {
libraryScan && libraryScan.addLog(LogLevel.INFO, `Library file with path "${existingLibraryFile.metadata.path}" not found, but found file with matching inode value "${existingLibraryFile.ino}" at path "${matchingLibraryFile.metadata.path}"`)
}
}
return matchingLibraryFile
}
/**
* Check if existing audio file on Book was removed
* @param {import('../models/Book').AudioFileObject} existingAudioFile
@ -341,13 +354,13 @@ class LibraryItemScanData {
* @returns {boolean} true if ebook file was removed
*/
checkEbookFileRemoved(ebookFile) {
if (!this.ebookLibraryFiles.length) return true
if (!this.ebookLibraryFilesRemoved.length) return false
if (this.ebookLibraryFiles.some((lf) => lf.metadata.path === ebookFile.metadata.path)) {
return false
if (this.ebookLibraryFilesRemoved.some((lf) => lf.metadata.path === ebookFile.metadata.path)) {
return true
}
return !this.ebookLibraryFiles.some((lf) => lf.ino === ebookFile.ino)
return this.ebookLibraryFilesRemoved.some((lf) => lf.ino === ebookFile.ino && lf.deviceId === ebookFile.deviceId)
}
/**

View file

@ -344,23 +344,7 @@ class LibraryScanner {
continue
}
items.push(
new LibraryItemScanData({
libraryFolderId: folder.id,
libraryId: folder.libraryId,
mediaType: library.mediaType,
ino: libraryItemFolderStats.ino,
deviceId: libraryItemFolderStats.dev,
mtimeMs: libraryItemFolderStats.mtimeMs || 0,
ctimeMs: libraryItemFolderStats.ctimeMs || 0,
birthtimeMs: libraryItemFolderStats.birthtimeMs || 0,
path: libraryItemData.path,
relPath: libraryItemData.relPath,
isFile,
mediaMetadata: libraryItemData.mediaMetadata || null,
libraryFiles: fileObjs
})
)
items.push(createLibraryItemScanData(folder, library, libraryItemFolderStats, libraryItemData, isFile, fileObjs))
}
return items
}
@ -754,3 +738,30 @@ async function findLibraryItemByFileToItemInoMatch(libraryId, fullPath, isSingle
if (existingLibraryItem) Logger.debug(`[LibraryScanner] Found library item with inode matching one of "${itemFileInos.join(',')}" at path "${existingLibraryItem.path}"`)
return existingLibraryItem
}
/**
* @param {{ id: any; libraryId: any; }} folder
* @param {{ mediaType: any; }} library
* @param {{ ino: any; dev: any; mtimeMs: any; ctimeMs: any; birthtimeMs: any; }} libraryItemFolderStats
* @param {{ path: any; relPath: any; mediaMetadata: any; }} libraryItemData
* @param {any} isFile
* @param {any} fileObjs
* @returns {LibraryItemScanData} new object
*/
function createLibraryItemScanData(folder, library, libraryItemFolderStats, libraryItemData, isFile, fileObjs) {
return new LibraryItemScanData({
libraryFolderId: folder.id,
libraryId: folder.libraryId,
mediaType: library.mediaType,
ino: libraryItemFolderStats.ino,
deviceId: libraryItemFolderStats.dev,
mtimeMs: libraryItemFolderStats.mtimeMs || 0,
ctimeMs: libraryItemFolderStats.ctimeMs || 0,
birthtimeMs: libraryItemFolderStats.birthtimeMs || 0,
path: libraryItemData.path,
relPath: libraryItemData.relPath,
isFile,
mediaMetadata: libraryItemData.mediaMetadata || null,
libraryFiles: fileObjs
})
}

View file

@ -366,7 +366,7 @@ class PodcastScanner {
* @param {PodcastEpisode[]} podcastEpisodes Not the models for new podcasts
* @param {import('./LibraryItemScanData')} libraryItemData
* @param {import('./LibraryScan')} libraryScan
* @param {string} [existingLibraryItemId]
* @param {string | null} [existingLibraryItemId]
* @returns {Promise<PodcastMetadataObject>}
*/
async getPodcastMetadataFromScanData(podcastEpisodes, libraryItemData, libraryScan, existingLibraryItemId = null) {