mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2026-03-01 05:29:41 +00:00
feat: add script to organize audiobooks by metadata tags
This commit is contained in:
parent
c2693e2460
commit
aaae3ba951
1 changed files with 92 additions and 0 deletions
92
scripts/group_by_metadata.js
Normal file
92
scripts/group_by_metadata.js
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const prober = require('../server/utils/prober')
|
||||
|
||||
// Mocking some environment variables that might be needed
|
||||
process.env.FFPROBE_PATH = process.env.FFPROBE_PATH || 'ffprobe'
|
||||
|
||||
async function organize(directory, dryRun = true) {
|
||||
if (!fs.existsSync(directory)) {
|
||||
console.error(`Error: Directory ${directory} does not exist.`)
|
||||
return
|
||||
}
|
||||
|
||||
const files = fs.readdirSync(directory).filter(f => f.toLowerCase().endsWith('.mp3'))
|
||||
|
||||
if (files.length === 0) {
|
||||
console.log(`No MP3 files found in ${directory}`)
|
||||
return
|
||||
}
|
||||
|
||||
console.log(`Found ${files.length} MP3 files. Probing metadata...`)
|
||||
|
||||
const groups = {}
|
||||
|
||||
for (const file of files) {
|
||||
const filePath = path.join(directory, file)
|
||||
try {
|
||||
const probeData = await prober.probe(filePath)
|
||||
|
||||
if (probeData.error) {
|
||||
console.error(`Error probing ${file}: ${probeData.error}`)
|
||||
continue
|
||||
}
|
||||
|
||||
// album is often used for Audiobook title
|
||||
// artist is author
|
||||
const album = probeData.audioMetaTags?.tagAlbum || 'Unknown Album'
|
||||
const artist = probeData.audioMetaTags?.tagArtist || 'Unknown Author'
|
||||
|
||||
const folderName = `${artist} - ${album}`.replace(/[<>:"/\\|?*]/g, '_') // Sanitize folder name
|
||||
|
||||
if (!groups[folderName]) {
|
||||
groups[folderName] = []
|
||||
}
|
||||
groups[folderName].push(file)
|
||||
} catch (err) {
|
||||
console.error(`Failed to process ${file}:`, err)
|
||||
}
|
||||
}
|
||||
|
||||
console.log('\nOrganization Plan:')
|
||||
for (const [folderName, folderFiles] of Object.entries(groups)) {
|
||||
console.log(`\nFolder: ${folderName} (${folderFiles.length} files)`)
|
||||
folderFiles.forEach(f => console.log(` - ${f}`))
|
||||
}
|
||||
|
||||
if (dryRun) {
|
||||
console.log('\n[DRY RUN] No changes were made. Run with --apply to reorganize files.')
|
||||
return
|
||||
}
|
||||
|
||||
console.log('\nApplying changes...')
|
||||
for (const [folderName, folderFiles] of Object.entries(groups)) {
|
||||
const targetDir = path.join(directory, folderName)
|
||||
if (!fs.existsSync(targetDir)) {
|
||||
fs.mkdirSync(targetDir, { recursive: true })
|
||||
}
|
||||
|
||||
for (const file of folderFiles) {
|
||||
const oldPath = path.join(directory, file)
|
||||
const newPath = path.join(targetDir, file)
|
||||
try {
|
||||
fs.renameSync(oldPath, newPath)
|
||||
console.log(`Moved: ${file} -> ${folderName}/`)
|
||||
} catch (err) {
|
||||
console.error(`Error moving ${file}:`, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
console.log('\nOrganization complete.')
|
||||
}
|
||||
|
||||
const args = process.argv.slice(2)
|
||||
const dir = args[0]
|
||||
const apply = args.includes('--apply')
|
||||
|
||||
if (!dir) {
|
||||
console.log('Usage: node scripts/group_by_metadata.js <directory> [--apply]')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
organize(path.resolve(dir), !apply)
|
||||
Loading…
Add table
Add a link
Reference in a new issue