audiobookshelf/server/utils/scandir.js

157 lines
4.3 KiB
JavaScript

const Path = require('path')
const dir = require('node-dir')
const Logger = require('../Logger')
const { cleanString } = require('./index')
const AUDIO_FORMATS = ['m4b', 'mp3', 'm4a']
const INFO_FORMATS = ['nfo']
const IMAGE_FORMATS = ['png', 'jpg', 'jpeg', 'webp']
const EBOOK_FORMATS = ['epub', 'pdf']
function getPaths(path) {
return new Promise((resolve) => {
dir.paths(path, function (err, res) {
if (err) {
console.error(err)
resolve(false)
}
resolve(res)
})
})
}
function getFileType(ext) {
var ext_cleaned = ext.toLowerCase()
if (ext_cleaned.startsWith('.')) ext_cleaned = ext_cleaned.slice(1)
if (AUDIO_FORMATS.includes(ext_cleaned)) return 'audio'
if (INFO_FORMATS.includes(ext_cleaned)) return 'info'
if (IMAGE_FORMATS.includes(ext_cleaned)) return 'image'
if (EBOOK_FORMATS.includes(ext_cleaned)) return 'ebook'
return 'unknown'
}
// Input relative filepath, output all details that can be parsed
function getAudiobookDataFromFilepath(abRootPath, relpath, parseSubtitle = false) {
var pathformat = Path.parse(relpath)
var path = pathformat.dir
if (!path) {
Logger.error('Ignoring file in root dir', relpath)
return null
}
// If relative file directory has 3 folders, then the middle folder will be series
var splitDir = path.split(Path.sep)
var author = null
if (splitDir.length > 1) author = splitDir.shift()
var series = null
if (splitDir.length > 1) series = splitDir.shift()
var title = splitDir.shift()
var publishYear = null
var subtitle = null
// If Title is of format 1999 - Title, then use 1999 as publish year
var publishYearMatch = title.match(/^([0-9]{4}) - (.+)/)
if (publishYearMatch && publishYearMatch.length > 2) {
if (!isNaN(publishYearMatch[1])) {
publishYear = publishYearMatch[1]
title = publishYearMatch[2]
}
}
if (parseSubtitle && title.includes(' - ')) {
var splitOnSubtitle = title.split(' - ')
title = splitOnSubtitle.shift()
subtitle = splitOnSubtitle.join(' - ')
}
return {
author,
title,
subtitle,
series,
publishYear,
path, // relative audiobook path i.e. /Author Name/Book Name/..
fullPath: Path.join(abRootPath, path) // i.e. /audiobook/Author Name/Book Name/..
}
}
async function getAllAudiobookFileData(abRootPath, serverSettings = {}) {
var parseSubtitle = !!serverSettings.scannerParseSubtitle
var paths = await getPaths(abRootPath)
var audiobooks = {}
paths.files.forEach((filepath) => {
var relpath = Path.normalize(filepath).replace(abRootPath, '').slice(1)
var parsed = Path.parse(relpath)
var path = parsed.dir
if (!audiobooks[path]) {
var audiobookData = getAudiobookDataFromFilepath(abRootPath, relpath, parseSubtitle)
if (!audiobookData) return
audiobooks[path] = {
...audiobookData,
audioFiles: [],
otherFiles: []
}
}
var fileObj = {
filetype: getFileType(parsed.ext),
filename: parsed.base,
path: relpath,
fullPath: filepath,
ext: parsed.ext
}
if (fileObj.filetype === 'audio') {
audiobooks[path].audioFiles.push(fileObj)
} else {
audiobooks[path].otherFiles.push(fileObj)
}
})
return Object.values(audiobooks)
}
module.exports.getAllAudiobookFileData = getAllAudiobookFileData
async function getAudiobookFileData(abRootPath, audiobookPath, serverSettings = {}) {
var parseSubtitle = !!serverSettings.scannerParseSubtitle
var paths = await getPaths(audiobookPath)
var audiobook = null
paths.files.forEach((filepath) => {
var relpath = Path.normalize(filepath).replace(abRootPath, '').slice(1)
if (!audiobook) {
var audiobookData = getAudiobookDataFromFilepath(abRootPath, relpath, parseSubtitle)
if (!audiobookData) return
audiobook = {
...audiobookData,
audioFiles: [],
otherFiles: []
}
}
var extname = Path.extname(filepath)
var basename = Path.basename(filepath)
var fileObj = {
filetype: getFileType(extname),
filename: basename,
path: relpath,
fullPath: filepath,
ext: extname
}
if (fileObj.filetype === 'audio') {
audiobook.audioFiles.push(fileObj)
} else {
audiobook.otherFiles.push(fileObj)
}
})
return audiobook
}
module.exports.getAudiobookFileData = getAudiobookFileData