Add:mtime,ctime,birthtime to audiobook folder and files

This commit is contained in:
advplyr 2022-02-27 18:07:36 -06:00
parent c81b12f459
commit 428a515c6a
7 changed files with 115 additions and 31 deletions

View File

@ -11,6 +11,9 @@ class AudioFile {
this.ext = null
this.path = null
this.fullPath = null
this.mtimeMs = null
this.ctimeMs = null
this.birthtimeMs = null
this.addedAt = null
this.trackNumFromMeta = null
@ -51,6 +54,9 @@ class AudioFile {
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,
@ -82,6 +88,9 @@ class AudioFile {
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
@ -124,6 +133,9 @@ class AudioFile {
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

View File

@ -1,7 +1,7 @@
const Path = require('path')
const fs = require('fs-extra')
const { bytesPretty, readTextFile } = require('../utils/fileUtils')
const { comparePaths, getIno, getId, elapsedPretty } = require('../utils/index')
const { bytesPretty, readTextFile, getIno } = require('../utils/fileUtils')
const { comparePaths, getId, elapsedPretty } = require('../utils/index')
const { parseOpfMetadataXML } = require('../utils/parseOpfMetadata')
const { extractCoverArt } = require('../utils/ffmpegHelpers')
const nfoGenerator = require('../utils/nfoGenerator')
@ -22,6 +22,9 @@ class Audiobook {
this.path = null
this.fullPath = null
this.mtimeMs = null
this.ctimeMs = null
this.birthtimeMs = null
this.addedAt = null
this.lastUpdate = null
this.lastScan = null
@ -57,6 +60,9 @@ class Audiobook {
this.folderId = audiobook.folderId || 'audiobooks'
this.path = audiobook.path
this.fullPath = audiobook.fullPath
this.mtimeMs = audiobook.mtimeMs || 0
this.ctimeMs = audiobook.ctimeMs || 0
this.birthtimeMs = audiobook.birthtimeMs || 0
this.addedAt = audiobook.addedAt
this.lastUpdate = audiobook.lastUpdate || this.addedAt
this.lastScan = audiobook.lastScan || null
@ -179,6 +185,9 @@ class Audiobook {
folderId: this.folderId,
path: this.path,
fullPath: this.fullPath,
mtimeMs: this.mtimeMs,
ctimeMs: this.ctimeMs,
birthtimeMs: this.birthtimeMs,
addedAt: this.addedAt,
lastUpdate: this.lastUpdate,
lastScan: this.lastScan,
@ -205,6 +214,9 @@ class Audiobook {
tags: this.tags,
path: this.path,
fullPath: this.fullPath,
mtimeMs: this.mtimeMs,
ctimeMs: this.ctimeMs,
birthtimeMs: this.birthtimeMs,
addedAt: this.addedAt,
lastUpdate: this.lastUpdate,
duration: this.duration,
@ -228,6 +240,9 @@ class Audiobook {
folderId: this.folderId,
path: this.path,
fullPath: this.fullPath,
mtimeMs: this.mtimeMs,
ctimeMs: this.ctimeMs,
birthtimeMs: this.birthtimeMs,
addedAt: this.addedAt,
lastUpdate: this.lastUpdate,
duration: this.duration,
@ -335,6 +350,9 @@ class Audiobook {
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 = Date.now()
this.lastUpdate = this.addedAt
@ -903,12 +921,6 @@ class Audiobook {
}
}
if (existingFile.filename !== fileFound.filename) {
existingFile.filename = fileFound.filename
existingFile.ext = fileFound.ext
hasUpdated = true
}
if (existingFile.path !== fileFound.path) {
existingFile.path = fileFound.path
existingFile.fullPath = fileFound.fullPath
@ -918,6 +930,14 @@ class Audiobook {
hasUpdated = true
}
var keysToCheck = ['filename', 'ext', 'mtimeMs', 'ctimeMs', 'birthtimeMs', 'size']
keysToCheck.forEach((key) => {
if (existingFile[key] !== fileFound[key]) {
existingFile[key] = fileFound[key]
hasUpdated = true
}
})
if (!isAudioFile && existingFile.filetype !== fileFound.filetype) {
existingFile.filetype = fileFound.filetype
hasUpdated = true
@ -957,6 +977,14 @@ class Audiobook {
hasUpdated = true
}
var keysToCheck = ['mtimeMs', 'ctimeMs', 'birthtimeMs']
keysToCheck.forEach((key) => {
if (dataFound[key] != this[key]) {
this[key] = dataFound[key] || 0
hasUpdated = true
}
})
var newAudioFileData = []
var newOtherFileData = []
var existingAudioFileData = []

View File

@ -6,6 +6,11 @@ class AudiobookFile {
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) {
@ -25,6 +30,10 @@ class AudiobookFile {
ext: this.ext,
path: this.path,
fullPath: this.fullPath,
size: this.size,
mtimeMs: this.mtimeMs,
ctimeMs: this.ctimeMs,
birthtimeMs: this.birthtimeMs,
addedAt: this.addedAt
}
}
@ -36,6 +45,10 @@ class AudiobookFile {
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
}
@ -46,6 +59,10 @@ class AudiobookFile {
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()
}
}

View File

@ -205,6 +205,7 @@ class Scanner {
// Check for existing & removed audiobooks
for (let i = 0; i < audiobooksInLibrary.length; i++) {
var audiobook = audiobooksInLibrary[i]
// Find audiobook folder with matching inode or matching path
var dataFound = audiobookDataFound.find(abd => abd.ino === audiobook.ino || comparePaths(abd.path, audiobook.path))
if (!dataFound) {
libraryScan.addLog(LogLevel.WARN, `Audiobook "${audiobook.title}" is missing`)

View File

@ -20,6 +20,23 @@ async function getFileStat(path) {
}
module.exports.getFileStat = getFileStat
async function getFileTimestampsWithIno(path) {
try {
var stat = await fs.stat(path, { bigint: true })
return {
size: Number(stat.size),
mtimeMs: Number(stat.mtimeMs),
ctimeMs: Number(stat.ctimeMs),
birthtimeMs: Number(stat.birthtimeMs),
ino: String(stat.ino)
}
} catch (err) {
console.error('Failed to getFileTimestampsWithIno', err)
return false
}
}
module.exports.getFileTimestampsWithIno = getFileTimestampsWithIno
async function getFileSize(path) {
var stat = await getFileStat(path)
if (!stat) return 0
@ -27,6 +44,15 @@ async function getFileSize(path) {
}
module.exports.getFileSize = getFileSize
function getIno(path) {
return fs.stat(path, { bigint: true }).then((data => String(data.ino))).catch((err) => {
Logger.error('[Utils] Failed to get ino for path', path, err)
return null
})
}
module.exports.getIno = getIno
async function readTextFile(path) {
try {
var data = await fs.readFile(path)

View File

@ -38,13 +38,6 @@ module.exports.comparePaths = (path1, path2) => {
return path1 === path2 || Path.normalize(path1) === Path.normalize(path2)
}
module.exports.getIno = (path) => {
return fs.promises.stat(path, { bigint: true }).then((data => String(data.ino))).catch((err) => {
Logger.error('[Utils] Failed to get ino for path', path, err)
return null
})
}
module.exports.isNullOrNaN = (num) => {
return num === null || isNaN(num)
}

View File

@ -1,8 +1,7 @@
const Path = require('path')
const fs = require('fs-extra')
const Logger = require('../Logger')
const { getIno } = require('./index')
const { recurseFiles } = require('./fileUtils')
const { recurseFiles, getFileTimestampsWithIno } = require('./fileUtils')
const globals = require('./globals')
function isBookFile(path) {
@ -114,16 +113,20 @@ function groupFileItemsIntoBooks(fileItems) {
}
function cleanFileObjects(basepath, abrelpath, files) {
return files.map((file) => {
return Promise.all(files.map(async (file) => {
var fullPath = Path.posix.join(basepath, file)
var fileTsData = await getFileTimestampsWithIno(fullPath)
var ext = Path.extname(file)
return {
filetype: getFileType(ext),
filename: Path.basename(file),
path: Path.posix.join(abrelpath, file), // /AUDIOBOOK/PATH/filename.mp3
fullPath: Path.posix.join(basepath, file), // /audiobooks/AUDIOBOOK/PATH/filename.mp3
ext: ext
fullPath, // /audiobooks/AUDIOBOOK/PATH/filename.mp3
ext: ext,
...fileTsData
}
})
}))
}
function getFileType(ext) {
@ -162,15 +165,15 @@ async function scanRootDir(folder, serverSettings = {}) {
for (const audiobookPath in audiobookGrouping) {
var audiobookData = getAudiobookDataFromDir(folderPath, audiobookPath, parseSubtitle)
var fileObjs = cleanFileObjects(audiobookData.fullPath, audiobookPath, audiobookGrouping[audiobookPath])
for (let i = 0; i < fileObjs.length; i++) {
fileObjs[i].ino = await getIno(fileObjs[i].fullPath)
}
var audiobookIno = await getIno(audiobookData.fullPath)
var fileObjs = await cleanFileObjects(audiobookData.fullPath, audiobookPath, audiobookGrouping[audiobookPath])
var audiobookFolderStats = await getFileTimestampsWithIno(audiobookData.fullPath)
audiobooks.push({
folderId: folder.id,
libraryId: folder.libraryId,
ino: audiobookIno,
ino: audiobookFolderStats.ino,
mtimeMs: audiobookFolderStats.mtimeMs || 0,
ctimeMs: audiobookFolderStats.ctimeMs || 0,
birthtimeMs: audiobookFolderStats.birthtimeMs || 0,
...audiobookData,
audioFiles: fileObjs.filter(f => f.filetype === 'audio'),
otherFiles: fileObjs.filter(f => f.filetype !== 'audio')
@ -282,8 +285,12 @@ async function getAudiobookFileData(folder, audiobookPath, serverSettings = {})
var audiobookDir = audiobookPath.replace(folderFullPath, '').slice(1)
var audiobookData = getAudiobookDataFromDir(folderFullPath, audiobookDir, parseSubtitle)
var audiobookFolderStats = await getFileTimestampsWithIno(audiobookData.fullPath)
var audiobook = {
ino: await getIno(audiobookData.fullPath),
ino: audiobookFolderStats.ino,
mtimeMs: audiobookFolderStats.mtimeMs || 0,
ctimeMs: audiobookFolderStats.ctimeMs || 0,
birthtimeMs: audiobookFolderStats.birthtimeMs || 0,
folderId: folder.id,
libraryId: folder.libraryId,
...audiobookData,
@ -294,14 +301,14 @@ async function getAudiobookFileData(folder, audiobookPath, serverSettings = {})
for (let i = 0; i < fileItems.length; i++) {
var fileItem = fileItems[i]
var ino = await getIno(fileItem.fullpath)
var fileStatData = await getFileTimestampsWithIno(fileItem.fullpath)
var fileObj = {
ino,
filetype: getFileType(fileItem.extension),
filename: fileItem.name,
path: fileItem.path,
fullPath: fileItem.fullpath,
ext: fileItem.extension
ext: fileItem.extension,
...fileStatData
}
if (fileObj.filetype === 'audio') {
audiobook.audioFiles.push(fileObj)