From e134e1b6f9fd77bdb53766071052b36fa988877f Mon Sep 17 00:00:00 2001 From: the1812 Date: Sat, 7 May 2022 00:02:34 +0800 Subject: [PATCH] Support batch depth option (#5) --- dist/cli/batch.js | 20 +++++++++---- dist/cli/index.js | 2 +- dist/cli/options.js | 9 ++++++ dist/core/metadata/local-mp3/local-mp3.js | 7 +++-- dist/core/readline.js | 24 +++------------- dist/core/writer/flac/flac-writer.js | 34 ++++++----------------- dist/core/writer/mp3/mp3-writer.js | 24 +++------------- src/cli/batch.ts | 22 +++++++++++---- src/cli/index.ts | 2 +- src/cli/options.ts | 9 ++++++ src/core/metadata/local-mp3/local-mp3.ts | 2 +- src/core/readline.ts | 2 +- src/core/writer/flac/flac-writer.ts | 2 +- src/core/writer/mp3/mp3-writer.ts | 2 +- 14 files changed, 77 insertions(+), 84 deletions(-) diff --git a/dist/cli/batch.js b/dist/cli/batch.js index 8d5dee0..c6be9b7 100644 --- a/dist/cli/batch.js +++ b/dist/cli/batch.js @@ -25,16 +25,26 @@ const path_1 = require("path"); const debug_1 = require("../core/debug"); const default_album_name_1 = require("./default-album-name"); const options_1 = require("./options"); -const runBatchTagger = async (folder) => { - const albums = (0, fs_1.readdirSync)(folder, { withFileTypes: true }) +const readFolder = (folder, depth) => { + const currentSubFolders = (0, fs_1.readdirSync)(folder, { withFileTypes: true }) .filter(dir => dir.isDirectory()) - .map(dir => dir.name); + .map(dir => ({ + name: dir.name, + path: (0, path_1.join)(folder, dir.name), + })); + if (depth <= 1) { + return currentSubFolders; + } + return currentSubFolders.flatMap(subFolder => readFolder((0, path_1.join)(folder, subFolder.name), depth - 1)); +}; +const runBatchTagger = async (folder, depth) => { + const albums = readFolder(folder, depth); const albumCount = albums.length; const { CliTagger } = await Promise.resolve().then(() => __importStar(require('./tagger'))); const { default: ora } = await Promise.resolve().then(() => __importStar(require('ora'))); for (let index = 0; index < albumCount; index++) { try { - const album = (0, default_album_name_1.getDefaultAlbumName)(albums[index]); + const album = (0, default_album_name_1.getDefaultAlbumName)(albums[index].name); const spinner = ora({ text: '搜索中', spinner: { @@ -45,7 +55,7 @@ const runBatchTagger = async (folder) => { spinner.prefixText = `[${album}] (${index + 1}/${albumCount})`; (0, debug_1.log)(`start processing album #${index + 1}`); const tagger = new CliTagger(options_1.cliOptions, options_1.metadataConfig, spinner); - tagger.workingDir = (0, path_1.resolve)(options_1.cliOptions.batch, albums[index]); + tagger.workingDir = albums[index].path; await tagger.run(album); (0, debug_1.log)(`processed album #${index + 1}`); } diff --git a/dist/cli/index.js b/dist/cli/index.js index a70556a..651ccc0 100644 --- a/dist/cli/index.js +++ b/dist/cli/index.js @@ -43,7 +43,7 @@ const runTagger = async (album) => { const defaultAlbumName = (0, default_album_name_1.getDefaultAlbumName)(); if (options_1.cliOptions.batch) { Promise.resolve().then(() => __importStar(require('./batch'))).then(({ runBatchTagger }) => { - runBatchTagger(options_1.cliOptions.batch); + runBatchTagger(options_1.cliOptions.batch, options_1.cliOptions.batchDepth); }); } else if (options_1.cliOptions['no-interactive']) { diff --git a/dist/cli/options.js b/dist/cli/options.js index 6d26ba7..936fc3b 100644 --- a/dist/cli/options.js +++ b/dist/cli/options.js @@ -10,6 +10,9 @@ const core_config_1 = require("../core/core-config"); const debug_1 = require("../core/debug"); const config_file_1 = require("./config-file"); const options = (0, yargs_1.default)((0, helpers_1.hideBin)(process.argv)) + .parserConfiguration({ + "short-option-groups": false, +}) .option('cover', { alias: 'c', type: 'boolean', @@ -65,6 +68,12 @@ const options = (0, yargs_1.default)((0, helpers_1.hideBin)(process.argv)) alias: 'b', type: 'string', description: '是否使用批量模式, 参数为开始批量运行的路径', +}) + .option('batch-depth', { + alias: 'bd', + type: 'number', + default: 1, + description: '指定批量模式的文件夹层级', }) .option('separator', { type: 'string', diff --git a/dist/core/metadata/local-mp3/local-mp3.js b/dist/core/metadata/local-mp3/local-mp3.js index 90cbcef..412ee6a 100644 --- a/dist/core/metadata/local-mp3/local-mp3.js +++ b/dist/core/metadata/local-mp3/local-mp3.js @@ -18,13 +18,16 @@ var __importStar = (this && this.__importStar) || function (mod) { __setModuleDefault(result, mod); return result; }; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); exports.localMp3 = exports.LocalMp3 = void 0; const metadata_source_1 = require("../metadata-source"); const fs_1 = require("fs"); const exists_1 = require("../../exists"); const proxy_1 = require("../../proxy"); -const id3 = __importStar(require("../../node-id3")); +const node_id3_1 = __importDefault(require("../../node-id3")); const dirFilter = (path, predicate) => { return (0, fs_1.readdirSync)(path, { withFileTypes: true }) .filter(predicate) @@ -50,7 +53,7 @@ class LocalMp3 extends metadata_source_1.MetadataSource { const metadatas = discs.map((discFiles, index) => { const discNumber = (index + 1).toString(); return discFiles.map(file => { - const tags = (0, proxy_1.defaultsToEmptyString)(id3.read(file)); + const tags = (0, proxy_1.defaultsToEmptyString)(node_id3_1.default.read(file)); const separator = this.config.separator; const metadata = { title: tags.title, diff --git a/dist/core/readline.js b/dist/core/readline.js index cb8daea..10aa12f 100644 --- a/dist/core/readline.js +++ b/dist/core/readline.js @@ -1,27 +1,11 @@ "use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.readline = void 0; -const rl = __importStar(require("readline")); -const reader = rl.createInterface({ +const readline_1 = __importDefault(require("readline")); +const reader = readline_1.default.createInterface({ input: process.stdin, output: process.stdout, }); diff --git a/dist/core/writer/flac/flac-writer.js b/dist/core/writer/flac/flac-writer.js index 0a7ae7e..4261b5d 100644 --- a/dist/core/writer/flac/flac-writer.js +++ b/dist/core/writer/flac/flac-writer.js @@ -1,27 +1,11 @@ "use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.flacWriter = exports.FlacWriter = void 0; const metadata_writer_1 = require("../metadata-writer"); -const flac = __importStar(require("flac-metadata")); +const flac_metadata_1 = __importDefault(require("flac-metadata")); const imageinfo = require("imageinfo"); const fs_1 = require("fs"); const stream_1 = require("stream"); @@ -68,12 +52,12 @@ const getVorbisComments = (metadata) => { }; class FlacWriter extends metadata_writer_1.MetadataWriter { async write(metadata, filePath) { - const commentsProcessor = new flac.Processor({ parseMetaDataBlocks: true }); - const pictureProcessor = new flac.Processor({ parseMetaDataBlocks: true }); + const commentsProcessor = new flac_metadata_1.default.Processor({ parseMetaDataBlocks: true }); + const pictureProcessor = new flac_metadata_1.default.Processor({ parseMetaDataBlocks: true }); const lyricConfig = this.config.lyric; commentsProcessor.on('preprocess', function (mdb) { if (!mdb.isLast) { - if (mdb.type === flac.Processor.MDB_TYPE_VORBIS_COMMENT) { + if (mdb.type === flac_metadata_1.default.Processor.MDB_TYPE_VORBIS_COMMENT) { mdb.remove(); } } @@ -82,13 +66,13 @@ class FlacWriter extends metadata_writer_1.MetadataWriter { if (lyricConfig && lyricConfig.output === 'lrc') { vorbisComments = vorbisComments.filter(c => !c.startsWith('LYRICS=')); } - const mdbVorbis = flac.data.MetaDataBlockVorbisComment.create(!metadata.coverImage, DefaultVendor, vorbisComments); + const mdbVorbis = flac_metadata_1.default.data.MetaDataBlockVorbisComment.create(!metadata.coverImage, DefaultVendor, vorbisComments); this.push(mdbVorbis.publish()); } }); pictureProcessor.on('preprocess', function (mdb) { if (!mdb.isLast) { - if (mdb.type === flac.Processor.MDB_TYPE_PICTURE) { + if (mdb.type === flac_metadata_1.default.Processor.MDB_TYPE_PICTURE) { mdb.remove(); } } @@ -104,7 +88,7 @@ class FlacWriter extends metadata_writer_1.MetadataWriter { format: '', }; } - const mdbPicture = flac.data.MetaDataBlockPicture.create(!metadata.coverImage, 3 /* front cover */, info.mimeType, metadata.album, info.width, info.height, 24, /* bits per pixel: unknown */ 0, /* colors: unknown */ metadata.coverImage); + const mdbPicture = flac_metadata_1.default.data.MetaDataBlockPicture.create(!metadata.coverImage, 3 /* front cover */, info.mimeType, metadata.album, info.width, info.height, 24, /* bits per pixel: unknown */ 0, /* colors: unknown */ metadata.coverImage); this.push(mdbPicture.publish()); } }); diff --git a/dist/core/writer/mp3/mp3-writer.js b/dist/core/writer/mp3/mp3-writer.js index 43aff27..59e2189 100644 --- a/dist/core/writer/mp3/mp3-writer.js +++ b/dist/core/writer/mp3/mp3-writer.js @@ -1,27 +1,11 @@ "use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.mp3Writer = exports.Mp3Writer = void 0; const metadata_writer_1 = require("../metadata-writer"); -const id3 = __importStar(require("../../node-id3")); +const node_id3_1 = __importDefault(require("../../node-id3")); const languageCodeConvert = (code) => { const mapping = { ja: 'jpn', @@ -70,7 +54,7 @@ class Mp3Writer extends metadata_writer_1.MetadataWriter { tag.unsynchronisedLyrics.text = ''; tag.unsynchronisedLyrics.language = undefined; } - const result = id3.write(tag, filePath); + const result = node_id3_1.default.write(tag, filePath); if (result === false) { throw new Error(`Write operation failed. filePath = ${filePath}`); } diff --git a/src/cli/batch.ts b/src/cli/batch.ts index cc1a168..844caf3 100644 --- a/src/cli/batch.ts +++ b/src/cli/batch.ts @@ -1,19 +1,29 @@ import { readdirSync } from 'fs' -import { resolve } from 'path' +import { join } from 'path' import { log } from '../core/debug' import { getDefaultAlbumName } from './default-album-name' import { cliOptions, metadataConfig } from './options' -export const runBatchTagger = async (folder: string) => { - const albums = readdirSync(folder, { withFileTypes: true }) +const readFolder = (folder: string, depth: number): { name: string; path: string }[] => { + const currentSubFolders = readdirSync(folder, { withFileTypes: true }) .filter(dir => dir.isDirectory()) - .map(dir => dir.name) + .map(dir => ({ + name: dir.name, + path: join(folder, dir.name), + })) + if (depth <= 1) { + return currentSubFolders + } + return currentSubFolders.flatMap(subFolder => readFolder(join(folder, subFolder.name), depth - 1)) +} +export const runBatchTagger = async (folder: string, depth: number) => { + const albums = readFolder(folder, depth) const albumCount = albums.length const { CliTagger } = await import('./tagger') const { default: ora } = await import('ora') for (let index = 0; index < albumCount; index++) { try { - const album = getDefaultAlbumName(albums[index]) + const album = getDefaultAlbumName(albums[index].name) const spinner = ora({ text: '搜索中', spinner: { @@ -24,7 +34,7 @@ export const runBatchTagger = async (folder: string) => { spinner.prefixText = `[${album}] (${index + 1}/${albumCount})` log(`start processing album #${index + 1}`) const tagger = new CliTagger(cliOptions, metadataConfig, spinner) - tagger.workingDir = resolve(cliOptions.batch, albums[index]) + tagger.workingDir = albums[index].path await tagger.run(album) log(`processed album #${index + 1}`) } catch (error) { diff --git a/src/cli/index.ts b/src/cli/index.ts index f532438..b4e6fc2 100644 --- a/src/cli/index.ts +++ b/src/cli/index.ts @@ -25,7 +25,7 @@ const runTagger = async (album: string) => { const defaultAlbumName = getDefaultAlbumName() if (cliOptions.batch) { import('./batch').then(({ runBatchTagger }) => { - runBatchTagger(cliOptions.batch) + runBatchTagger(cliOptions.batch, cliOptions.batchDepth) }) } else if (cliOptions['no-interactive']) { runTagger(defaultAlbumName) diff --git a/src/cli/options.ts b/src/cli/options.ts index ad5ee3b..272a626 100644 --- a/src/cli/options.ts +++ b/src/cli/options.ts @@ -5,6 +5,9 @@ import { log, setDebug } from '../core/debug' import { loadConfigFile, saveConfigFile } from './config-file' const options = yargs(hideBin(process.argv)) + .parserConfiguration({ + "short-option-groups": false, + }) .option('cover', { alias: 'c', type: 'boolean', @@ -61,6 +64,12 @@ const options = yargs(hideBin(process.argv)) type: 'string', description: '是否使用批量模式, 参数为开始批量运行的路径', }) + .option('batch-depth', { + alias: 'bd', + type: 'number', + default: 1, + description: '指定批量模式的文件夹层级', + }) .option('separator', { type: 'string', default: DefaultMetadataSeparator, diff --git a/src/core/metadata/local-mp3/local-mp3.ts b/src/core/metadata/local-mp3/local-mp3.ts index 09b4ae8..c11c3a8 100644 --- a/src/core/metadata/local-mp3/local-mp3.ts +++ b/src/core/metadata/local-mp3/local-mp3.ts @@ -3,7 +3,7 @@ import { Metadata } from '../metadata' import { Dirent, readdirSync } from 'fs' import { resolvePath } from '../../exists' import { defaultsToEmptyString } from '../../proxy' -import * as id3 from '../../node-id3' +import id3 from '../../node-id3' const dirFilter = (path: string, predicate: (d: Dirent) => boolean) => { return readdirSync(path, { withFileTypes: true }) diff --git a/src/core/readline.ts b/src/core/readline.ts index 2a8de4f..35dc2ae 100644 --- a/src/core/readline.ts +++ b/src/core/readline.ts @@ -1,4 +1,4 @@ -import * as rl from 'readline' +import rl from 'readline' const reader = rl.createInterface({ input: process.stdin, diff --git a/src/core/writer/flac/flac-writer.ts b/src/core/writer/flac/flac-writer.ts index 8c5d7ed..c7d4eb4 100644 --- a/src/core/writer/flac/flac-writer.ts +++ b/src/core/writer/flac/flac-writer.ts @@ -1,6 +1,6 @@ import { MetadataWriter } from '../metadata-writer' import { Metadata } from '../../metadata/metadata' -import * as flac from 'flac-metadata' +import flac from 'flac-metadata' import imageinfo = require('imageinfo') import { createWriteStream, readFileSync } from 'fs' import { Readable, finished } from 'stream' diff --git a/src/core/writer/mp3/mp3-writer.ts b/src/core/writer/mp3/mp3-writer.ts index d51e406..2c2daf6 100644 --- a/src/core/writer/mp3/mp3-writer.ts +++ b/src/core/writer/mp3/mp3-writer.ts @@ -1,6 +1,6 @@ import { MetadataWriter } from '../metadata-writer' import { Metadata } from '../../metadata/metadata' -import * as id3 from '../../node-id3' +import id3 from '../../node-id3' const languageCodeConvert = (code: string | undefined) => { const mapping = {