mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-04-02 01:16:54 +02:00
Add:Support for single book files to be detected by Watcher #610, Fix:Single media file in library folder root is only supported for books not podcasts
This commit is contained in:
parent
a8d5b543d7
commit
0ad7a98fc7
@ -162,13 +162,6 @@ class FolderWatcher extends EventEmitter {
|
|||||||
}
|
}
|
||||||
var folderFullPath = folder.fullPath.replace(/\\/g, '/')
|
var folderFullPath = folder.fullPath.replace(/\\/g, '/')
|
||||||
|
|
||||||
// Check if file was added to root directory
|
|
||||||
var dir = Path.dirname(path)
|
|
||||||
if (dir === folderFullPath) {
|
|
||||||
Logger.warn(`[Watcher] New file "${Path.basename(path)}" added to folder root - ignoring it`)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var relPath = path.replace(folderFullPath, '')
|
var relPath = path.replace(folderFullPath, '')
|
||||||
|
|
||||||
var hasDotPath = relPath.split('/').find(p => p.startsWith('.'))
|
var hasDotPath = relPath.split('/').find(p => p.startsWith('.'))
|
||||||
|
@ -62,7 +62,8 @@ class Scanner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async scanLibraryItem(libraryMediaType, folder, libraryItem) {
|
async scanLibraryItem(libraryMediaType, folder, libraryItem) {
|
||||||
var libraryItemData = await getLibraryItemFileData(libraryMediaType, folder, libraryItem.path, this.db.serverSettings)
|
// TODO: Support for single media item
|
||||||
|
var libraryItemData = await getLibraryItemFileData(libraryMediaType, folder, libraryItem.path, false, this.db.serverSettings)
|
||||||
if (!libraryItemData) {
|
if (!libraryItemData) {
|
||||||
return ScanResult.NOTHING
|
return ScanResult.NOTHING
|
||||||
}
|
}
|
||||||
@ -499,7 +500,11 @@ class Scanner {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var relFilePaths = folderGroups[folderId].fileUpdates.map(fileUpdate => fileUpdate.relPath)
|
var relFilePaths = folderGroups[folderId].fileUpdates.map(fileUpdate => fileUpdate.relPath)
|
||||||
var fileUpdateGroup = groupFilesIntoLibraryItemPaths(relFilePaths, true)
|
var fileUpdateGroup = groupFilesIntoLibraryItemPaths(library.mediaType, relFilePaths)
|
||||||
|
if (!Object.keys(fileUpdateGroup).length) {
|
||||||
|
Logger.info(`[Scanner] No important changes to scan for in folder "${folderId}"`)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
var folderScanResults = await this.scanFolderUpdates(library, folder, fileUpdateGroup)
|
var folderScanResults = await this.scanFolderUpdates(library, folder, fileUpdateGroup)
|
||||||
Logger.debug(`[Scanner] Folder scan results`, folderScanResults)
|
Logger.debug(`[Scanner] Folder scan results`, folderScanResults)
|
||||||
}
|
}
|
||||||
@ -513,6 +518,8 @@ class Scanner {
|
|||||||
// Test Case: Moving audio files from library item folder to author folder should trigger a re-scan of the item
|
// Test Case: Moving audio files from library item folder to author folder should trigger a re-scan of the item
|
||||||
var updateGroup = { ...fileUpdateGroup }
|
var updateGroup = { ...fileUpdateGroup }
|
||||||
for (const itemDir in updateGroup) {
|
for (const itemDir in updateGroup) {
|
||||||
|
if (itemDir == fileUpdateGroup[itemDir]) continue; // Media in root path
|
||||||
|
|
||||||
var itemDirNestedFiles = fileUpdateGroup[itemDir].filter(b => b.includes('/'))
|
var itemDirNestedFiles = fileUpdateGroup[itemDir].filter(b => b.includes('/'))
|
||||||
if (!itemDirNestedFiles.length) continue;
|
if (!itemDirNestedFiles.length) continue;
|
||||||
|
|
||||||
@ -582,7 +589,8 @@ class Scanner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Logger.debug(`[Scanner] Folder update group must be a new item "${itemDir}" in library "${library.name}"`)
|
Logger.debug(`[Scanner] Folder update group must be a new item "${itemDir}" in library "${library.name}"`)
|
||||||
var newLibraryItem = await this.scanPotentialNewLibraryItem(library.mediaType, folder, fullPath)
|
var isSingleMediaItem = itemDir === fileUpdateGroup[itemDir]
|
||||||
|
var newLibraryItem = await this.scanPotentialNewLibraryItem(library.mediaType, folder, fullPath, isSingleMediaItem)
|
||||||
if (newLibraryItem) {
|
if (newLibraryItem) {
|
||||||
await this.createNewAuthorsAndSeries(newLibraryItem)
|
await this.createNewAuthorsAndSeries(newLibraryItem)
|
||||||
await this.db.insertLibraryItem(newLibraryItem)
|
await this.db.insertLibraryItem(newLibraryItem)
|
||||||
@ -594,8 +602,8 @@ class Scanner {
|
|||||||
return itemGroupingResults
|
return itemGroupingResults
|
||||||
}
|
}
|
||||||
|
|
||||||
async scanPotentialNewLibraryItem(libraryMediaType, folder, fullPath) {
|
async scanPotentialNewLibraryItem(libraryMediaType, folder, fullPath, isSingleMediaItem = false) {
|
||||||
var libraryItemData = await getLibraryItemFileData(libraryMediaType, folder, fullPath, this.db.serverSettings)
|
var libraryItemData = await getLibraryItemFileData(libraryMediaType, folder, fullPath, isSingleMediaItem, this.db.serverSettings)
|
||||||
if (!libraryItemData) return null
|
if (!libraryItemData) return null
|
||||||
var serverSettings = this.db.serverSettings
|
var serverSettings = this.db.serverSettings
|
||||||
return this.scanNewLibraryItem(libraryItemData, libraryMediaType, serverSettings.scannerPreferAudioMetadata, serverSettings.scannerPreferOpfMetadata, serverSettings.scannerFindCovers)
|
return this.scanNewLibraryItem(libraryItemData, libraryMediaType, serverSettings.scannerPreferAudioMetadata, serverSettings.scannerPreferOpfMetadata, serverSettings.scannerFindCovers)
|
||||||
|
@ -17,11 +17,14 @@ function isMediaFile(mediaType, ext) {
|
|||||||
// TODO: Function needs to be re-done
|
// TODO: Function needs to be re-done
|
||||||
// Input: array of relative file paths
|
// Input: array of relative file paths
|
||||||
// Output: map of files grouped into potential item dirs
|
// Output: map of files grouped into potential item dirs
|
||||||
function groupFilesIntoLibraryItemPaths(paths) {
|
function groupFilesIntoLibraryItemPaths(mediaType, paths) {
|
||||||
// Step 1: Clean path, Remove leading "/", Filter out files in root dir
|
// Step 1: Clean path, Remove leading "/", Filter out non-media files in root dir
|
||||||
var pathsFiltered = paths.map(path => {
|
var pathsFiltered = paths.map(path => {
|
||||||
return path.startsWith('/') ? path.slice(1) : path
|
return path.startsWith('/') ? path.slice(1) : path
|
||||||
}).filter(path => Path.parse(path).dir)
|
}).filter(path => {
|
||||||
|
let parsedPath = Path.parse(path)
|
||||||
|
return parsedPath.dir || (mediaType === 'book' && isMediaFile(mediaType, parsedPath.ext))
|
||||||
|
})
|
||||||
|
|
||||||
// Step 2: Sort by least number of directories
|
// Step 2: Sort by least number of directories
|
||||||
pathsFiltered.sort((a, b) => {
|
pathsFiltered.sort((a, b) => {
|
||||||
@ -33,25 +36,30 @@ function groupFilesIntoLibraryItemPaths(paths) {
|
|||||||
// Step 3: Group files in dirs
|
// Step 3: Group files in dirs
|
||||||
var itemGroup = {}
|
var itemGroup = {}
|
||||||
pathsFiltered.forEach((path) => {
|
pathsFiltered.forEach((path) => {
|
||||||
var dirparts = Path.dirname(path).split('/')
|
var dirparts = Path.dirname(path).split('/').filter(p => !!p && p !== '.') // dirname returns . if no directory
|
||||||
var numparts = dirparts.length
|
var numparts = dirparts.length
|
||||||
var _path = ''
|
var _path = ''
|
||||||
|
|
||||||
// Iterate over directories in path
|
if (!numparts) {
|
||||||
for (let i = 0; i < numparts; i++) {
|
// Media file in root
|
||||||
var dirpart = dirparts.shift()
|
itemGroup[path] = path
|
||||||
_path = Path.posix.join(_path, dirpart)
|
} else {
|
||||||
|
// Iterate over directories in path
|
||||||
|
for (let i = 0; i < numparts; i++) {
|
||||||
|
var dirpart = dirparts.shift()
|
||||||
|
_path = Path.posix.join(_path, dirpart)
|
||||||
|
|
||||||
if (itemGroup[_path]) { // Directory already has files, add file
|
if (itemGroup[_path]) { // Directory already has files, add file
|
||||||
var relpath = Path.posix.join(dirparts.join('/'), Path.basename(path))
|
var relpath = Path.posix.join(dirparts.join('/'), Path.basename(path))
|
||||||
itemGroup[_path].push(relpath)
|
itemGroup[_path].push(relpath)
|
||||||
return
|
return
|
||||||
} else if (!dirparts.length) { // This is the last directory, create group
|
} else if (!dirparts.length) { // This is the last directory, create group
|
||||||
itemGroup[_path] = [Path.basename(path)]
|
itemGroup[_path] = [Path.basename(path)]
|
||||||
return
|
return
|
||||||
} else if (dirparts.length === 1 && /^cd\d{1,3}$/i.test(dirparts[0])) { // Next directory is the last and is a CD dir, create group
|
} else if (dirparts.length === 1 && /^cd\d{1,3}$/i.test(dirparts[0])) { // Next directory is the last and is a CD dir, create group
|
||||||
itemGroup[_path] = [Path.posix.join(dirparts[0], Path.basename(path))]
|
itemGroup[_path] = [Path.posix.join(dirparts[0], Path.basename(path))]
|
||||||
return
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -62,9 +70,9 @@ module.exports.groupFilesIntoLibraryItemPaths = groupFilesIntoLibraryItemPaths
|
|||||||
// Input: array of relative file items (see recurseFiles)
|
// Input: array of relative file items (see recurseFiles)
|
||||||
// Output: map of files grouped into potential libarary item dirs
|
// Output: map of files grouped into potential libarary item dirs
|
||||||
function groupFileItemsIntoLibraryItemDirs(mediaType, fileItems) {
|
function groupFileItemsIntoLibraryItemDirs(mediaType, fileItems) {
|
||||||
// Step 1: Filter out non-media files in root dir (with depth of 0)
|
// Step 1: Filter out non-book-media files in root dir (with depth of 0)
|
||||||
var itemsFiltered = fileItems.filter(i => {
|
var itemsFiltered = fileItems.filter(i => {
|
||||||
return i.deep > 0 || isMediaFile(mediaType, i.extension)
|
return i.deep > 0 || (mediaType === 'book' && isMediaFile(mediaType, i.extension))
|
||||||
})
|
})
|
||||||
|
|
||||||
// Step 2: Seperate media files and other files
|
// Step 2: Seperate media files and other files
|
||||||
@ -147,16 +155,6 @@ async function scanFolder(libraryMediaType, folder, serverSettings = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var fileItems = await recurseFiles(folderPath)
|
var fileItems = await recurseFiles(folderPath)
|
||||||
var basePath = folderPath
|
|
||||||
|
|
||||||
const isOpenAudibleFolder = fileItems.find(fi => fi.deep === 0 && fi.name === 'books.json')
|
|
||||||
if (isOpenAudibleFolder) {
|
|
||||||
Logger.info(`[scandir] Detected Open Audible Folder, looking in books folder`)
|
|
||||||
basePath = Path.posix.join(folderPath, 'books')
|
|
||||||
fileItems = await recurseFiles(basePath)
|
|
||||||
Logger.debug(`[scandir] ${fileItems.length} files found in books folder`)
|
|
||||||
}
|
|
||||||
|
|
||||||
var libraryItemGrouping = groupFileItemsIntoLibraryItemDirs(libraryMediaType, fileItems)
|
var libraryItemGrouping = groupFileItemsIntoLibraryItemDirs(libraryMediaType, fileItems)
|
||||||
|
|
||||||
if (!Object.keys(libraryItemGrouping).length) {
|
if (!Object.keys(libraryItemGrouping).length) {
|
||||||
@ -175,10 +173,10 @@ async function scanFolder(libraryMediaType, folder, serverSettings = {}) {
|
|||||||
mediaMetadata: {
|
mediaMetadata: {
|
||||||
title: Path.basename(libraryItemPath, Path.extname(libraryItemPath))
|
title: Path.basename(libraryItemPath, Path.extname(libraryItemPath))
|
||||||
},
|
},
|
||||||
path: Path.posix.join(basePath, libraryItemPath),
|
path: Path.posix.join(folderPath, libraryItemPath),
|
||||||
relPath: libraryItemPath
|
relPath: libraryItemPath
|
||||||
}
|
}
|
||||||
fileObjs = await cleanFileObjects(basePath, [libraryItemPath])
|
fileObjs = await cleanFileObjects(folderPath, [libraryItemPath])
|
||||||
isFile = true
|
isFile = true
|
||||||
} else {
|
} else {
|
||||||
libraryItemData = getDataFromMediaDir(libraryMediaType, folderPath, libraryItemPath, serverSettings)
|
libraryItemData = getDataFromMediaDir(libraryMediaType, folderPath, libraryItemPath, serverSettings)
|
||||||
@ -335,14 +333,34 @@ function getDataFromMediaDir(libraryMediaType, folderPath, relPath, serverSettin
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Called from Scanner.js
|
// Called from Scanner.js
|
||||||
async function getLibraryItemFileData(libraryMediaType, folder, libraryItemPath, serverSettings = {}) {
|
async function getLibraryItemFileData(libraryMediaType, folder, libraryItemPath, isSingleMediaItem, serverSettings = {}) {
|
||||||
var fileItems = await recurseFiles(libraryItemPath)
|
|
||||||
|
|
||||||
libraryItemPath = libraryItemPath.replace(/\\/g, '/')
|
libraryItemPath = libraryItemPath.replace(/\\/g, '/')
|
||||||
var folderFullPath = folder.fullPath.replace(/\\/g, '/')
|
var folderFullPath = folder.fullPath.replace(/\\/g, '/')
|
||||||
|
|
||||||
var libraryItemDir = libraryItemPath.replace(folderFullPath, '').slice(1)
|
var libraryItemDir = libraryItemPath.replace(folderFullPath, '').slice(1)
|
||||||
var libraryItemData = getDataFromMediaDir(libraryMediaType, folderFullPath, libraryItemDir, serverSettings)
|
var libraryItemData = {}
|
||||||
|
|
||||||
|
var fileItems = []
|
||||||
|
|
||||||
|
if (isSingleMediaItem) { // Single media item in root of folder
|
||||||
|
fileItems = [
|
||||||
|
{
|
||||||
|
fullpath: libraryItemPath,
|
||||||
|
path: libraryItemDir // actually the relPath (only filename here)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
libraryItemData = {
|
||||||
|
path: libraryItemPath, // full path
|
||||||
|
relPath: libraryItemDir, // only filename
|
||||||
|
mediaMetadata: {
|
||||||
|
title: Path.basename(libraryItemDir, Path.extname(libraryItemDir))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fileItems = await recurseFiles(libraryItemPath)
|
||||||
|
libraryItemData = getDataFromMediaDir(libraryMediaType, folderFullPath, libraryItemDir, serverSettings)
|
||||||
|
}
|
||||||
|
|
||||||
var libraryItemDirStats = await getFileTimestampsWithIno(libraryItemData.path)
|
var libraryItemDirStats = await getFileTimestampsWithIno(libraryItemData.path)
|
||||||
var libraryItem = {
|
var libraryItem = {
|
||||||
ino: libraryItemDirStats.ino,
|
ino: libraryItemDirStats.ino,
|
||||||
@ -353,6 +371,7 @@ async function getLibraryItemFileData(libraryMediaType, folder, libraryItemPath,
|
|||||||
libraryId: folder.libraryId,
|
libraryId: folder.libraryId,
|
||||||
path: libraryItemData.path,
|
path: libraryItemData.path,
|
||||||
relPath: libraryItemData.relPath,
|
relPath: libraryItemData.relPath,
|
||||||
|
isFile: isSingleMediaItem,
|
||||||
media: {
|
media: {
|
||||||
metadata: libraryItemData.mediaMetadata || null
|
metadata: libraryItemData.mediaMetadata || null
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user