mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-03-05 00:18:30 +01:00
Merge branch 'master' into watcher-update-api
This commit is contained in:
commit
3bccd52196
@ -42,6 +42,7 @@ export default {
|
|||||||
rendition: null,
|
rendition: null,
|
||||||
ereaderSettings: {
|
ereaderSettings: {
|
||||||
theme: 'dark',
|
theme: 'dark',
|
||||||
|
font: 'serif',
|
||||||
fontScale: 100,
|
fontScale: 100,
|
||||||
lineSpacing: 115,
|
lineSpacing: 115,
|
||||||
spread: 'auto'
|
spread: 'auto'
|
||||||
@ -130,6 +131,7 @@ export default {
|
|||||||
|
|
||||||
const fontScale = settings.fontScale || 100
|
const fontScale = settings.fontScale || 100
|
||||||
this.rendition.themes.fontSize(`${fontScale}%`)
|
this.rendition.themes.fontSize(`${fontScale}%`)
|
||||||
|
this.rendition.themes.font(settings.font)
|
||||||
this.rendition.spread(settings.spread || 'auto')
|
this.rendition.spread(settings.spread || 'auto')
|
||||||
},
|
},
|
||||||
prev() {
|
prev() {
|
||||||
|
@ -63,7 +63,13 @@
|
|||||||
<div class="w-40">
|
<div class="w-40">
|
||||||
<p class="text-lg">{{ $strings.LabelTheme }}:</p>
|
<p class="text-lg">{{ $strings.LabelTheme }}:</p>
|
||||||
</div>
|
</div>
|
||||||
<ui-toggle-btns v-model="ereaderSettings.theme" :items="themeItems" @input="settingsUpdated" />
|
<ui-toggle-btns v-model="ereaderSettings.theme" :items="themeItems.theme" @input="settingsUpdated" />
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center mb-4">
|
||||||
|
<div class="w-40">
|
||||||
|
<p class="text-lg">{{ $strings.LabelFontFamily }}:</p>
|
||||||
|
</div>
|
||||||
|
<ui-toggle-btns v-model="ereaderSettings.font" :items="themeItems.font" @input="settingsUpdated" />
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center mb-4">
|
<div class="flex items-center mb-4">
|
||||||
<div class="w-40">
|
<div class="w-40">
|
||||||
@ -103,6 +109,7 @@ export default {
|
|||||||
showSettings: false,
|
showSettings: false,
|
||||||
ereaderSettings: {
|
ereaderSettings: {
|
||||||
theme: 'dark',
|
theme: 'dark',
|
||||||
|
font: 'serif',
|
||||||
fontScale: 100,
|
fontScale: 100,
|
||||||
lineSpacing: 115,
|
lineSpacing: 115,
|
||||||
spread: 'auto'
|
spread: 'auto'
|
||||||
@ -142,16 +149,28 @@ export default {
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
themeItems() {
|
themeItems() {
|
||||||
return [
|
return {
|
||||||
{
|
theme: [
|
||||||
text: this.$strings.LabelThemeDark,
|
{
|
||||||
value: 'dark'
|
text: this.$strings.LabelThemeDark,
|
||||||
},
|
value: 'dark'
|
||||||
{
|
},
|
||||||
text: this.$strings.LabelThemeLight,
|
{
|
||||||
value: 'light'
|
text: this.$strings.LabelThemeLight,
|
||||||
}
|
value: 'light'
|
||||||
]
|
}
|
||||||
|
],
|
||||||
|
font: [
|
||||||
|
{
|
||||||
|
text: 'Sans',
|
||||||
|
value: 'sans-serif',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'Serif',
|
||||||
|
value: 'serif',
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
},
|
},
|
||||||
componentName() {
|
componentName() {
|
||||||
if (this.ebookType === 'epub') return 'readers-epub-reader'
|
if (this.ebookType === 'epub') return 'readers-epub-reader'
|
||||||
|
@ -260,6 +260,7 @@
|
|||||||
"LabelFinished": "Færdig",
|
"LabelFinished": "Færdig",
|
||||||
"LabelFolder": "Mappe",
|
"LabelFolder": "Mappe",
|
||||||
"LabelFolders": "Mapper",
|
"LabelFolders": "Mapper",
|
||||||
|
"LabelFontFamily": "Fontfamilie",
|
||||||
"LabelFontScale": "Skriftstørrelse",
|
"LabelFontScale": "Skriftstørrelse",
|
||||||
"LabelFormat": "Format",
|
"LabelFormat": "Format",
|
||||||
"LabelGenre": "Genre",
|
"LabelGenre": "Genre",
|
||||||
|
@ -260,6 +260,7 @@
|
|||||||
"LabelFinished": "beendet",
|
"LabelFinished": "beendet",
|
||||||
"LabelFolder": "Ordner",
|
"LabelFolder": "Ordner",
|
||||||
"LabelFolders": "Verzeichnisse",
|
"LabelFolders": "Verzeichnisse",
|
||||||
|
"LabelFontFamily": "Schriftfamilie",
|
||||||
"LabelFontScale": "Schriftgröße",
|
"LabelFontScale": "Schriftgröße",
|
||||||
"LabelFormat": "Format",
|
"LabelFormat": "Format",
|
||||||
"LabelGenre": "Kategorie",
|
"LabelGenre": "Kategorie",
|
||||||
|
@ -260,6 +260,7 @@
|
|||||||
"LabelFinished": "Finished",
|
"LabelFinished": "Finished",
|
||||||
"LabelFolder": "Folder",
|
"LabelFolder": "Folder",
|
||||||
"LabelFolders": "Folders",
|
"LabelFolders": "Folders",
|
||||||
|
"LabelFontFamily": "Font family",
|
||||||
"LabelFontScale": "Font scale",
|
"LabelFontScale": "Font scale",
|
||||||
"LabelFormat": "Format",
|
"LabelFormat": "Format",
|
||||||
"LabelGenre": "Genre",
|
"LabelGenre": "Genre",
|
||||||
|
@ -260,6 +260,7 @@
|
|||||||
"LabelFinished": "Terminado",
|
"LabelFinished": "Terminado",
|
||||||
"LabelFolder": "Carpeta",
|
"LabelFolder": "Carpeta",
|
||||||
"LabelFolders": "Carpetas",
|
"LabelFolders": "Carpetas",
|
||||||
|
"LabelFontFamily": "Familia tipográfica",
|
||||||
"LabelFontScale": "Tamaño de Fuente",
|
"LabelFontScale": "Tamaño de Fuente",
|
||||||
"LabelFormat": "Formato",
|
"LabelFormat": "Formato",
|
||||||
"LabelGenre": "Genero",
|
"LabelGenre": "Genero",
|
||||||
|
@ -260,6 +260,7 @@
|
|||||||
"LabelFinished": "Fini(e)",
|
"LabelFinished": "Fini(e)",
|
||||||
"LabelFolder": "Dossier",
|
"LabelFolder": "Dossier",
|
||||||
"LabelFolders": "Dossiers",
|
"LabelFolders": "Dossiers",
|
||||||
|
"LabelFontFamily": "Famille de polices",
|
||||||
"LabelFontScale": "Taille de la police de caractère",
|
"LabelFontScale": "Taille de la police de caractère",
|
||||||
"LabelFormat": "Format",
|
"LabelFormat": "Format",
|
||||||
"LabelGenre": "Genre",
|
"LabelGenre": "Genre",
|
||||||
|
@ -260,6 +260,7 @@
|
|||||||
"LabelFinished": "Finished",
|
"LabelFinished": "Finished",
|
||||||
"LabelFolder": "Folder",
|
"LabelFolder": "Folder",
|
||||||
"LabelFolders": "Folders",
|
"LabelFolders": "Folders",
|
||||||
|
"LabelFontFamily": "ફોન્ટ કુટુંબ",
|
||||||
"LabelFontScale": "Font scale",
|
"LabelFontScale": "Font scale",
|
||||||
"LabelFormat": "Format",
|
"LabelFormat": "Format",
|
||||||
"LabelGenre": "Genre",
|
"LabelGenre": "Genre",
|
||||||
|
@ -260,6 +260,7 @@
|
|||||||
"LabelFinished": "Finished",
|
"LabelFinished": "Finished",
|
||||||
"LabelFolder": "Folder",
|
"LabelFolder": "Folder",
|
||||||
"LabelFolders": "Folders",
|
"LabelFolders": "Folders",
|
||||||
|
"LabelFontFamily": "फुहारा परिवार",
|
||||||
"LabelFontScale": "Font scale",
|
"LabelFontScale": "Font scale",
|
||||||
"LabelFormat": "Format",
|
"LabelFormat": "Format",
|
||||||
"LabelGenre": "Genre",
|
"LabelGenre": "Genre",
|
||||||
|
@ -260,6 +260,7 @@
|
|||||||
"LabelFinished": "Finished",
|
"LabelFinished": "Finished",
|
||||||
"LabelFolder": "Folder",
|
"LabelFolder": "Folder",
|
||||||
"LabelFolders": "Folderi",
|
"LabelFolders": "Folderi",
|
||||||
|
"LabelFontFamily": "Font family",
|
||||||
"LabelFontScale": "Font scale",
|
"LabelFontScale": "Font scale",
|
||||||
"LabelFormat": "Format",
|
"LabelFormat": "Format",
|
||||||
"LabelGenre": "Genre",
|
"LabelGenre": "Genre",
|
||||||
|
@ -260,6 +260,7 @@
|
|||||||
"LabelFinished": "Finita",
|
"LabelFinished": "Finita",
|
||||||
"LabelFolder": "Cartella",
|
"LabelFolder": "Cartella",
|
||||||
"LabelFolders": "Cartelle",
|
"LabelFolders": "Cartelle",
|
||||||
|
"LabelFontFamily": "Font family",
|
||||||
"LabelFontScale": "Dimensione Font",
|
"LabelFontScale": "Dimensione Font",
|
||||||
"LabelFormat": "Formato",
|
"LabelFormat": "Formato",
|
||||||
"LabelGenre": "Genere",
|
"LabelGenre": "Genere",
|
||||||
|
@ -260,6 +260,7 @@
|
|||||||
"LabelFinished": "Baigta",
|
"LabelFinished": "Baigta",
|
||||||
"LabelFolder": "Aplankas",
|
"LabelFolder": "Aplankas",
|
||||||
"LabelFolders": "Aplankai",
|
"LabelFolders": "Aplankai",
|
||||||
|
"LabelFontFamily": "Famiglia di font",
|
||||||
"LabelFontScale": "Šrifto mastelis",
|
"LabelFontScale": "Šrifto mastelis",
|
||||||
"LabelFormat": "Formatas",
|
"LabelFormat": "Formatas",
|
||||||
"LabelGenre": "Žanras",
|
"LabelGenre": "Žanras",
|
||||||
|
@ -260,6 +260,7 @@
|
|||||||
"LabelFinished": "Voltooid",
|
"LabelFinished": "Voltooid",
|
||||||
"LabelFolder": "Map",
|
"LabelFolder": "Map",
|
||||||
"LabelFolders": "Mappen",
|
"LabelFolders": "Mappen",
|
||||||
|
"LabelFontFamily": "Lettertypefamilie",
|
||||||
"LabelFontScale": "Lettertype schaal",
|
"LabelFontScale": "Lettertype schaal",
|
||||||
"LabelFormat": "Formaat",
|
"LabelFormat": "Formaat",
|
||||||
"LabelGenre": "Genre",
|
"LabelGenre": "Genre",
|
||||||
|
@ -260,6 +260,7 @@
|
|||||||
"LabelFinished": "Fullført",
|
"LabelFinished": "Fullført",
|
||||||
"LabelFolder": "Mappe",
|
"LabelFolder": "Mappe",
|
||||||
"LabelFolders": "Mapper",
|
"LabelFolders": "Mapper",
|
||||||
|
"LabelFontFamily": "Fontfamilie",
|
||||||
"LabelFontScale": "Font størrelse",
|
"LabelFontScale": "Font størrelse",
|
||||||
"LabelFormat": "Format",
|
"LabelFormat": "Format",
|
||||||
"LabelGenre": "Sjanger",
|
"LabelGenre": "Sjanger",
|
||||||
|
@ -260,6 +260,7 @@
|
|||||||
"LabelFinished": "Zakończone",
|
"LabelFinished": "Zakończone",
|
||||||
"LabelFolder": "Folder",
|
"LabelFolder": "Folder",
|
||||||
"LabelFolders": "Foldery",
|
"LabelFolders": "Foldery",
|
||||||
|
"LabelFontFamily": "Rodzina czcionek",
|
||||||
"LabelFontScale": "Font scale",
|
"LabelFontScale": "Font scale",
|
||||||
"LabelFormat": "Format",
|
"LabelFormat": "Format",
|
||||||
"LabelGenre": "Gatunek",
|
"LabelGenre": "Gatunek",
|
||||||
|
@ -260,6 +260,7 @@
|
|||||||
"LabelFinished": "Закончен",
|
"LabelFinished": "Закончен",
|
||||||
"LabelFolder": "Папка",
|
"LabelFolder": "Папка",
|
||||||
"LabelFolders": "Папки",
|
"LabelFolders": "Папки",
|
||||||
|
"LabelFontFamily": "Семейство шрифтов",
|
||||||
"LabelFontScale": "Масштаб шрифта",
|
"LabelFontScale": "Масштаб шрифта",
|
||||||
"LabelFormat": "Формат",
|
"LabelFormat": "Формат",
|
||||||
"LabelGenre": "Жанр",
|
"LabelGenre": "Жанр",
|
||||||
|
@ -260,6 +260,7 @@
|
|||||||
"LabelFinished": "已听完",
|
"LabelFinished": "已听完",
|
||||||
"LabelFolder": "文件夹",
|
"LabelFolder": "文件夹",
|
||||||
"LabelFolders": "文件夹",
|
"LabelFolders": "文件夹",
|
||||||
|
"LabelFontFamily": "字体系列",
|
||||||
"LabelFontScale": "字体比例",
|
"LabelFontScale": "字体比例",
|
||||||
"LabelFormat": "编码格式",
|
"LabelFormat": "编码格式",
|
||||||
"LabelGenre": "流派",
|
"LabelGenre": "流派",
|
||||||
|
@ -6,7 +6,7 @@ const LibraryScanner = require('./scanner/LibraryScanner')
|
|||||||
const Task = require('./objects/Task')
|
const Task = require('./objects/Task')
|
||||||
const TaskManager = require('./managers/TaskManager')
|
const TaskManager = require('./managers/TaskManager')
|
||||||
|
|
||||||
const { filePathToPOSIX, isSameOrSubPath } = require('./utils/fileUtils')
|
const { filePathToPOSIX, isSameOrSubPath, getFileMTimeMs } = require('./utils/fileUtils')
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef PendingFileUpdate
|
* @typedef PendingFileUpdate
|
||||||
@ -29,6 +29,8 @@ class FolderWatcher extends EventEmitter {
|
|||||||
/** @type {Task} */
|
/** @type {Task} */
|
||||||
this.pendingTask = null
|
this.pendingTask = null
|
||||||
|
|
||||||
|
this.filesBeingAdded = new Set()
|
||||||
|
|
||||||
/** @type {string[]} */
|
/** @type {string[]} */
|
||||||
this.ignoreDirs = []
|
this.ignoreDirs = []
|
||||||
/** @type {string[]} */
|
/** @type {string[]} */
|
||||||
@ -64,14 +66,13 @@ class FolderWatcher extends EventEmitter {
|
|||||||
})
|
})
|
||||||
watcher
|
watcher
|
||||||
.on('add', (path) => {
|
.on('add', (path) => {
|
||||||
this.onNewFile(library.id, path)
|
this.onFileAdded(library.id, filePathToPOSIX(path))
|
||||||
}).on('change', (path) => {
|
}).on('change', (path) => {
|
||||||
// This is triggered from metadata changes, not what we want
|
// This is triggered from metadata changes, not what we want
|
||||||
// this.onFileUpdated(path)
|
|
||||||
}).on('unlink', path => {
|
}).on('unlink', path => {
|
||||||
this.onFileRemoved(library.id, path)
|
this.onFileRemoved(library.id, filePathToPOSIX(path))
|
||||||
}).on('rename', (path, pathNext) => {
|
}).on('rename', (path, pathNext) => {
|
||||||
this.onRename(library.id, path, pathNext)
|
this.onFileRename(library.id, filePathToPOSIX(path), filePathToPOSIX(pathNext))
|
||||||
}).on('error', (error) => {
|
}).on('error', (error) => {
|
||||||
Logger.error(`[Watcher] ${error}`)
|
Logger.error(`[Watcher] ${error}`)
|
||||||
}).on('ready', () => {
|
}).on('ready', () => {
|
||||||
@ -137,14 +138,31 @@ class FolderWatcher extends EventEmitter {
|
|||||||
return this.libraryWatchers.map(lib => lib.watcher.close())
|
return this.libraryWatchers.map(lib => lib.watcher.close())
|
||||||
}
|
}
|
||||||
|
|
||||||
onNewFile(libraryId, path) {
|
/**
|
||||||
|
* Watcher detected file added
|
||||||
|
*
|
||||||
|
* @param {string} libraryId
|
||||||
|
* @param {string} path
|
||||||
|
*/
|
||||||
|
onFileAdded(libraryId, path) {
|
||||||
if (this.checkShouldIgnorePath(path)) {
|
if (this.checkShouldIgnorePath(path)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
Logger.debug('[Watcher] File Added', path)
|
Logger.debug('[Watcher] File Added', path)
|
||||||
this.addFileUpdate(libraryId, path, 'added')
|
this.addFileUpdate(libraryId, path, 'added')
|
||||||
|
|
||||||
|
if (!this.filesBeingAdded.has(path)) {
|
||||||
|
this.filesBeingAdded.add(path)
|
||||||
|
this.waitForFileToAdd(path)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Watcher detected file removed
|
||||||
|
*
|
||||||
|
* @param {string} libraryId
|
||||||
|
* @param {string} path
|
||||||
|
*/
|
||||||
onFileRemoved(libraryId, path) {
|
onFileRemoved(libraryId, path) {
|
||||||
if (this.checkShouldIgnorePath(path)) {
|
if (this.checkShouldIgnorePath(path)) {
|
||||||
return
|
return
|
||||||
@ -153,11 +171,13 @@ class FolderWatcher extends EventEmitter {
|
|||||||
this.addFileUpdate(libraryId, path, 'deleted')
|
this.addFileUpdate(libraryId, path, 'deleted')
|
||||||
}
|
}
|
||||||
|
|
||||||
onFileUpdated(path) {
|
/**
|
||||||
Logger.debug('[Watcher] Updated File', path)
|
* Watcher detected file renamed
|
||||||
}
|
*
|
||||||
|
* @param {string} libraryId
|
||||||
onRename(libraryId, pathFrom, pathTo) {
|
* @param {string} path
|
||||||
|
*/
|
||||||
|
onFileRename(libraryId, pathFrom, pathTo) {
|
||||||
if (this.checkShouldIgnorePath(pathTo)) {
|
if (this.checkShouldIgnorePath(pathTo)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -166,13 +186,41 @@ class FolderWatcher extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* File update detected from watcher
|
* Get mtimeMs from an added file every second until it is no longer changing
|
||||||
|
* Times out after 180s
|
||||||
|
*
|
||||||
|
* @param {string} path
|
||||||
|
* @param {number} [lastMTimeMs=0]
|
||||||
|
* @param {number} [loop=0]
|
||||||
|
*/
|
||||||
|
async waitForFileToAdd(path, lastMTimeMs = 0, loop = 0) {
|
||||||
|
// Safety to catch infinite loop (180s)
|
||||||
|
if (loop >= 180) {
|
||||||
|
Logger.warn(`[Watcher] Waiting to add file at "${path}" timeout (loop ${loop}) - proceeding`)
|
||||||
|
return this.filesBeingAdded.delete(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
const mtimeMs = await getFileMTimeMs(path)
|
||||||
|
if (mtimeMs === lastMTimeMs) {
|
||||||
|
if (lastMTimeMs) Logger.debug(`[Watcher] File finished adding at "${path}"`)
|
||||||
|
return this.filesBeingAdded.delete(path)
|
||||||
|
}
|
||||||
|
if (lastMTimeMs % 5 === 0) {
|
||||||
|
Logger.debug(`[Watcher] Waiting to add file at "${path}". mtimeMs=${mtimeMs} lastMTimeMs=${lastMTimeMs} (loop ${loop})`)
|
||||||
|
}
|
||||||
|
// Wait 1 second
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 1000))
|
||||||
|
this.waitForFileToAdd(path, mtimeMs, ++loop)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Queue file update
|
||||||
|
*
|
||||||
* @param {string} libraryId
|
* @param {string} libraryId
|
||||||
* @param {string} path
|
* @param {string} path
|
||||||
* @param {string} type
|
* @param {string} type
|
||||||
*/
|
*/
|
||||||
addFileUpdate(libraryId, path, type) {
|
addFileUpdate(libraryId, path, type) {
|
||||||
path = filePathToPOSIX(path)
|
|
||||||
if (this.pendingFilePaths.includes(path)) return
|
if (this.pendingFilePaths.includes(path)) return
|
||||||
|
|
||||||
// Get file library
|
// Get file library
|
||||||
@ -222,12 +270,26 @@ class FolderWatcher extends EventEmitter {
|
|||||||
type
|
type
|
||||||
})
|
})
|
||||||
|
|
||||||
// Notify server of update after "pendingDelay"
|
this.handlePendingFileUpdatesTimeout()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait X seconds before notifying scanner that files changed
|
||||||
|
* reset timer if files are still copying
|
||||||
|
*/
|
||||||
|
handlePendingFileUpdatesTimeout() {
|
||||||
clearTimeout(this.pendingTimeout)
|
clearTimeout(this.pendingTimeout)
|
||||||
this.pendingTimeout = setTimeout(() => {
|
this.pendingTimeout = setTimeout(() => {
|
||||||
|
// Check that files are not still being added
|
||||||
|
if (this.pendingFileUpdates.some(pfu => this.filesBeingAdded.has(pfu.path))) {
|
||||||
|
Logger.debug(`[Watcher] Still waiting for pending files "${[...this.filesBeingAdded].join(', ')}"`)
|
||||||
|
return this.handlePendingFileUpdatesTimeout()
|
||||||
|
}
|
||||||
|
|
||||||
LibraryScanner.scanFilesChanged(this.pendingFileUpdates, this.pendingTask)
|
LibraryScanner.scanFilesChanged(this.pendingFileUpdates, this.pendingTask)
|
||||||
this.pendingTask = null
|
this.pendingTask = null
|
||||||
this.pendingFileUpdates = []
|
this.pendingFileUpdates = []
|
||||||
|
this.filesBeingAdded.clear()
|
||||||
}, this.pendingDelay)
|
}, this.pendingDelay)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -621,7 +621,7 @@ class LibraryController {
|
|||||||
model: Database.bookModel,
|
model: Database.bookModel,
|
||||||
attributes: ['id', 'tags', 'explicit'],
|
attributes: ['id', 'tags', 'explicit'],
|
||||||
where: bookWhere,
|
where: bookWhere,
|
||||||
required: false,
|
required: !req.user.isAdminOrUp, // Only show authors with 0 books for admin users or up
|
||||||
through: {
|
through: {
|
||||||
attributes: []
|
attributes: []
|
||||||
}
|
}
|
||||||
|
@ -38,22 +38,14 @@ function isSameOrSubPath(parentPath, childPath) {
|
|||||||
}
|
}
|
||||||
module.exports.isSameOrSubPath = isSameOrSubPath
|
module.exports.isSameOrSubPath = isSameOrSubPath
|
||||||
|
|
||||||
async function getFileStat(path) {
|
function getFileStat(path) {
|
||||||
try {
|
try {
|
||||||
var stat = await fs.stat(path)
|
return fs.stat(path)
|
||||||
return {
|
|
||||||
size: stat.size,
|
|
||||||
atime: stat.atime,
|
|
||||||
mtime: stat.mtime,
|
|
||||||
ctime: stat.ctime,
|
|
||||||
birthtime: stat.birthtime
|
|
||||||
}
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
Logger.error('[fileUtils] Failed to stat', err)
|
Logger.error('[fileUtils] Failed to stat', err)
|
||||||
return false
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
module.exports.getFileStat = getFileStat
|
|
||||||
|
|
||||||
async function getFileTimestampsWithIno(path) {
|
async function getFileTimestampsWithIno(path) {
|
||||||
try {
|
try {
|
||||||
@ -72,12 +64,25 @@ async function getFileTimestampsWithIno(path) {
|
|||||||
}
|
}
|
||||||
module.exports.getFileTimestampsWithIno = getFileTimestampsWithIno
|
module.exports.getFileTimestampsWithIno = getFileTimestampsWithIno
|
||||||
|
|
||||||
async function getFileSize(path) {
|
/**
|
||||||
var stat = await getFileStat(path)
|
* Get file size
|
||||||
if (!stat) return 0
|
*
|
||||||
return stat.size || 0
|
* @param {string} path
|
||||||
|
* @returns {Promise<number>}
|
||||||
|
*/
|
||||||
|
module.exports.getFileSize = async (path) => {
|
||||||
|
return (await getFileStat(path))?.size || 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get file mtimeMs
|
||||||
|
*
|
||||||
|
* @param {string} path
|
||||||
|
* @returns {Promise<number>} epoch timestamp
|
||||||
|
*/
|
||||||
|
module.exports.getFileMTimeMs = async (path) => {
|
||||||
|
return (await getFileStat(path))?.mtimeMs || 0
|
||||||
}
|
}
|
||||||
module.exports.getFileSize = getFileSize
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
@ -308,6 +308,8 @@ module.exports = {
|
|||||||
async getNewestAuthors(library, user, limit) {
|
async getNewestAuthors(library, user, limit) {
|
||||||
if (library.mediaType !== 'book') return { authors: [], count: 0 }
|
if (library.mediaType !== 'book') return { authors: [], count: 0 }
|
||||||
|
|
||||||
|
const { bookWhere, replacements } = libraryItemsBookFilters.getUserPermissionBookWhereQuery(user)
|
||||||
|
|
||||||
const { rows: authors, count } = await Database.authorModel.findAndCountAll({
|
const { rows: authors, count } = await Database.authorModel.findAndCountAll({
|
||||||
where: {
|
where: {
|
||||||
libraryId: library.id,
|
libraryId: library.id,
|
||||||
@ -315,9 +317,15 @@ module.exports = {
|
|||||||
[Sequelize.Op.gte]: new Date(new Date() - (60 * 24 * 60 * 60 * 1000)) // 60 days ago
|
[Sequelize.Op.gte]: new Date(new Date() - (60 * 24 * 60 * 60 * 1000)) // 60 days ago
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
replacements,
|
||||||
include: {
|
include: {
|
||||||
model: Database.bookAuthorModel,
|
model: Database.bookModel,
|
||||||
required: true // Must belong to a book
|
attributes: ['id', 'tags', 'explicit'],
|
||||||
|
where: bookWhere,
|
||||||
|
required: true, // Must belong to a book
|
||||||
|
through: {
|
||||||
|
attributes: []
|
||||||
|
}
|
||||||
},
|
},
|
||||||
limit,
|
limit,
|
||||||
distinct: true,
|
distinct: true,
|
||||||
@ -328,7 +336,7 @@ module.exports = {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
authors: authors.map((au) => {
|
authors: authors.map((au) => {
|
||||||
const numBooks = au.bookAuthors?.length || 0
|
const numBooks = au.books.length || 0
|
||||||
return au.getOldAuthor().toJSONExpanded(numBooks)
|
return au.getOldAuthor().toJSONExpanded(numBooks)
|
||||||
}),
|
}),
|
||||||
count
|
count
|
||||||
|
Loading…
Reference in New Issue
Block a user