@@ -11,12 +11,11 @@ import chalk from 'chalk';
11
11
import { sify } from 'chinese-conv' ;
12
12
import fs from 'fs-extra' ;
13
13
import inquirer from "inquirer" ;
14
+ import { cpus } from "os" ;
15
+ import pMap from 'p-map' ;
14
16
import path from "path" ;
15
-
16
-
17
17
import { asyncFilter } from '../lib/core.js' ;
18
18
import * as log from '../lib/debug.js' ;
19
- import * as enc from '../lib/encoding.js' ;
20
19
import * as mf from '../lib/file.js' ;
21
20
import * as helper from '../lib/helper.js' ;
22
21
import { renameFiles } from "./cmd_shared.js" ;
@@ -26,8 +25,6 @@ const MODE_DIR = "dirname";
26
25
const MODE_PREFIX = "prefix" ;
27
26
const MODE_MEDIA = "media" ;
28
27
const MODE_CLEAN = 'clean' ;
29
- const MODE_TC2SC = "tc2sc" ; // 繁体转简体
30
- const MODE_FIXENC = "fixenc" ; // 乱码还原
31
28
32
29
const NAME_LENGTH = 32 ;
33
30
@@ -67,7 +64,7 @@ const builder = function addOptions(ya, helpOrVersionSet) {
67
64
type : "string" ,
68
65
default : MODE_AUTO ,
69
66
description : "filename prefix mode for output " ,
70
- choices : [ MODE_AUTO , MODE_DIR , MODE_PREFIX , MODE_MEDIA , MODE_CLEAN , MODE_TC2SC , MODE_FIXENC ] ,
67
+ choices : [ MODE_AUTO , MODE_DIR , MODE_PREFIX , MODE_MEDIA , MODE_CLEAN ] ,
71
68
} )
72
69
. option ( "auto" , {
73
70
type : "boolean" ,
@@ -93,14 +90,6 @@ const builder = function addOptions(ya, helpOrVersionSet) {
93
90
type : "boolean" ,
94
91
description : "mode clean only" ,
95
92
} )
96
- . option ( "tc-to-sc" , {
97
- type : "boolean" ,
98
- description : "mode tc to sc" ,
99
- } )
100
- . option ( "fix-encoding" , {
101
- type : "boolean" ,
102
- description : "mode fix encoding messy chars" ,
103
- } )
104
93
// 清理文件名中的特殊字符和非法字符
105
94
. option ( "clean" , {
106
95
alias : "c" ,
@@ -158,7 +147,7 @@ const reImageName = /更新|合集|画师|图片|视频|插画|视图|作品|订
158
147
// \p{ASCII} ASCII字符
159
148
// \uFE10-\uFE1F 中文全角标点
160
149
// \uFF01-\uFF11 中文全角标点
161
- const reNonChars = / [ ^ \p{ Unified_Ideograph} \p{ P } \p{ sc= Hira } 0 - z ] / ugi;
150
+ const reNonChars = / [ ^ \p{ Unified_Ideograph} \p{ sc = Hira } \p{ sc= Kana } \w ] / ugi;
162
151
// 匹配空白字符和特殊字符
163
152
// https://www.unicode.org/charts/PDF/U3000.pdf
164
153
// https://www.asciitable.com/
@@ -177,7 +166,7 @@ const reMediaDirName = /^图片|视频|电影|电视剧|Image|Video|Thumbs$/gi;
177
166
// https://github.com/fujaru/aromanize-js
178
167
// https://www.npmjs.com/package/aromanize
179
168
// https://www.npmjs.com/package/@lazy -cjk/japanese
180
- function cleanAlbumName ( nameString , sep , filename ) {
169
+ function cleanFileName ( nameString , sep , filename , keepNumber = false ) {
181
170
let nameStr = nameString ;
182
171
// 去掉方括号 [xxx] 的内容
183
172
// nameStr = nameStr.replaceAll(/\[.+?\]/gi, "");
@@ -186,19 +175,21 @@ function cleanAlbumName(nameString, sep, filename) {
186
175
// 去掉视频说明文字
187
176
nameStr = nameStr . replaceAll ( reVideoName , "" ) ;
188
177
// 去掉日期字符串
189
- nameStr = nameStr . replaceAll ( / \d + 年 \d + 月 / gi, "" ) ;
190
- nameStr = nameStr . replaceAll ( / \d { 4 } - \d { 2 } - \d { 2 } / gi, "" ) ;
178
+ if ( ! keepNumber ) {
179
+ nameStr = nameStr . replaceAll ( / \d + 年 \d + 月 / ugi, "" ) ;
180
+ nameStr = nameStr . replaceAll ( / \d { 4 } - \d { 2 } - \d { 2 } / ugi, "" ) ;
181
+ }
191
182
// 去掉 [100P5V 2.25GB] No.46 这种图片集说明
192
- nameStr = nameStr . replaceAll ( / \[ \d + P .* ( \d + V ) ? .* ?\] / gi , "" ) ;
193
- nameStr = nameStr . replaceAll ( / N o \. \d + | \d + \. ? \d + G B ? | \d + P | \d + V | N O \. ( \d + ) / gi , "$1" ) ;
183
+ nameStr = nameStr . replaceAll ( / \[ \d + P .* ( \d + V ) ? .* ?\] / ugi , "" ) ;
184
+ nameStr = nameStr . replaceAll ( / N o \. \d + | \d + \. ? \d + G B ? | \d + P | \d + V | N O \. ( \d + ) / ugi , "$1" ) ;
194
185
if ( helper . isImageFile ( filename ) ) {
195
186
// 去掉 2024.03.22 这种格式的日期
196
- nameStr = nameStr . replaceAll ( / \d { 4 } \. \d { 2 } \. \d { 2 } / gi , "" ) ;
187
+ nameStr = nameStr . replaceAll ( / \d { 4 } \. \d { 2 } \. \d { 2 } / ugi , "" ) ;
197
188
}
198
189
// 去掉中文标点特殊符号
199
- nameStr = nameStr . replaceAll ( / [ \u3000 - \u303F \uFE10 - \uFE1F \uFF01 - \uFF11 ] / gi , "" ) ;
190
+ nameStr = nameStr . replaceAll ( / [ \u3000 - \u303F \uFE10 - \uFE2F ] / ugi , "" ) ;
200
191
// () [] {} <> . - 改为下划线
201
- nameStr = nameStr . replaceAll ( / [ \( \) \[ \] { } < > \. \- ] / gi , sep ) ;
192
+ nameStr = nameStr . replaceAll ( / [ \( \) \[ \] { } < > \. \- ] / ugi , sep ) ;
202
193
// 日文转罗马字母
203
194
// nameStr = hepburn.fromKana(nameStr);
204
195
// nameStr = wanakana.toRomaji(nameStr);
@@ -232,20 +223,16 @@ function parseNameMode(argv) {
232
223
if ( argv . dirname ) { mode = MODE_DIR ; }
233
224
if ( argv . media ) { mode = MODE_MEDIA ; }
234
225
if ( argv . cleanOnly ) { mode = MODE_CLEAN ; }
235
- if ( argv . tcToSc ) { mode = MODE_TC2SC ; }
236
- if ( argv . fixEncoding ) { mode = MODE_FIXENC ; }
237
226
return mode ;
238
227
}
239
228
240
- let badUnicodeCount = 0 ;
241
229
// 重复文件名Set,检测重复,防止覆盖
242
230
const nameDuplicateSet = new Set ( ) ;
243
- function createNewNameByMode ( f , argv ) {
231
+ async function createNewNameByMode ( f ) {
232
+ const argv = f . argv ;
244
233
const mode = parseNameMode ( argv ) ;
245
234
const nameLength = ( mode === MODE_MEDIA
246
- || mode === MODE_CLEAN
247
- || mode === MODE_TC2SC
248
- || mode === MODE_FIXENC ) ?
235
+ || mode === MODE_CLEAN ) ?
249
236
200 : argv . length || NAME_LENGTH ;
250
237
const nameSlice = nameLength * - 1 ;
251
238
const [ dir , base , ext ] = helper . pathSplit ( f . path ) ;
@@ -264,8 +251,6 @@ function createNewNameByMode(f, argv) {
264
251
let oldBase = base ;
265
252
switch ( mode ) {
266
253
case MODE_CLEAN :
267
- case MODE_TC2SC :
268
- case MODE_FIXENC :
269
254
{
270
255
sep = "." ;
271
256
prefix = "" ;
@@ -311,68 +296,45 @@ function createNewNameByMode(f, argv) {
311
296
break ;
312
297
default :
313
298
throw new Error ( `Invalid mode: ${ mode } ${ argv . mode } ` )
314
- break ;
315
299
}
316
300
317
- if ( mode !== MODE_CLEAN && mode !== MODE_TC2SC && mode !== MODE_FIXENC ) {
301
+ if ( mode !== MODE_CLEAN ) {
318
302
// 无有效前缀,报错退出
319
303
if ( ! prefix || prefix . length == 0 ) {
320
304
log . warn ( logTag , `Invalid Prefix: ${ helper . pathShort ( f . path ) } ${ mode } ` ) ;
321
305
throw new Error ( `No prefix supplied!` ) ;
322
306
}
323
307
}
308
+ log . show ( prefix )
324
309
let newPathFixed = null ;
325
- // 此模式仅执行简繁转换,不进行其它操作
326
- if ( mode === MODE_TC2SC ) {
327
- oldBase = sify ( oldBase ) ;
328
- } else if ( mode == MODE_FIXENC ) {
329
- const strPath = path . resolve ( f . path ) . split ( path . sep ) . join ( ' ' )
330
- if ( enc . hasBadUnicode ( strPath ) ) {
331
- log . show ( logTag , `Bad:${ ++ badUnicodeCount } ` , f . path )
332
- }
333
- // 当模式为MODE_FIXENC时,对文件路径进行特定的编码修复处理
334
- // 对旧基础路径进行中日韩文字编码修复
335
- let [ fs , ft ] = enc . fixCJKEnc ( oldBase ) ;
336
- oldBase = fs . trim ( ) ;
337
- // 将目录路径分割,并对每个部分进行编码修复
338
- const dirNamesFixed = dir . split ( path . sep ) . map ( s => {
339
- let [ rs , rt ] = enc . fixCJKEnc ( s )
340
- return rs . trim ( ) ;
341
- } ) ;
342
- // 重新组合修复后的目录路径
343
- const dirFixed = path . join ( ...dirNamesFixed ) ;
344
- // 生成修复后的新路径,包括旧基础路径和文件扩展名
345
- newPathFixed = path . join ( dirFixed , `${ oldBase } ${ ext } ` ) ;
346
- }
347
310
// 是否净化文件名,去掉各种特殊字符
348
- else if ( argv . clean || mode === MODE_CLEAN ) {
349
- prefix = cleanAlbumName ( prefix , sep , oldName ) ;
350
- oldBase = cleanAlbumName ( oldBase , sep , oldName ) ;
311
+ if ( argv . clean || mode === MODE_CLEAN ) {
312
+ prefix = cleanFileName ( prefix , sep , oldName , false ) ;
313
+ oldBase = cleanFileName ( oldBase , sep , oldName , true ) ;
351
314
}
352
315
// 不添加重复前缀
353
316
if ( oldBase . includes ( prefix ) ) {
354
317
log . info ( logTag , `IgnorePrefix: ${ ipx } ${ helper . pathShort ( f . path ) } ` ) ;
355
318
prefix = "" ;
356
319
}
357
320
let fullBase = prefix . length > 0 ? ( prefix + sep + oldBase ) : oldBase ;
358
- if ( mode !== MODE_TC2SC && mode !== MODE_FIXENC ) {
359
- // 去除首位空白和特殊字符
360
- fullBase = fullBase . replaceAll ( reStripUglyChars , "" ) ;
361
- // 多余空白和字符替换为一个字符 _或.
362
- fullBase = fullBase . replaceAll ( reUglyChars , sep ) ;
363
- // 去掉重复词组,如目录名和人名
364
- fullBase = Array . from ( new Set ( fullBase . split ( sep ) ) ) . join ( sep )
365
- fullBase = unicodeStrLength ( fullBase ) > nameLength ? fullBase . slice ( nameSlice ) : fullBase ;
366
- // 再次去掉首位的特殊字符和空白字符
367
- fullBase = fullBase . replaceAll ( reStripUglyChars , "" ) ;
368
- }
321
+ // 去除首位空白和特殊字符
322
+ fullBase = fullBase . replaceAll ( reStripUglyChars , "" ) ;
323
+ // 多余空白和字符替换为一个字符 _或.
324
+ fullBase = fullBase . replaceAll ( reUglyChars , sep ) ;
325
+ // 去掉重复词组,如目录名和人名
326
+ fullBase = Array . from ( new Set ( fullBase . split ( sep ) ) ) . join ( sep )
327
+ fullBase = unicodeStrLength ( fullBase ) > nameLength ? fullBase . slice ( nameSlice ) : fullBase ;
328
+ // 再次去掉首位的特殊字符和空白字符
329
+ fullBase = fullBase . replaceAll ( reStripUglyChars , "" ) ;
330
+
369
331
const newName = `${ fullBase } ${ ext } ` ;
370
332
const newPath = newPathFixed ?? path . join ( dir , newName ) ;
371
333
if ( newPath === f . path ) {
372
334
log . info ( logTag , `Same: ${ ipx } ${ helper . pathShort ( newPath ) } ` ) ;
373
335
f . skipped = true ;
374
336
}
375
- else if ( fs . existsSync ( newPath ) ) {
337
+ else if ( await fs . pathExists ( newPath ) ) {
376
338
log . info ( logTag , `Exists: ${ ipx } ${ helper . pathShort ( newPath ) } ` ) ;
377
339
f . skipped = true ;
378
340
}
@@ -444,12 +406,17 @@ const handler = async function cmdPrefix(argv) {
444
406
files = files . map ( ( f , i ) => {
445
407
return {
446
408
...f ,
409
+ argv : argv ,
447
410
index : i ,
448
411
total : files . length ,
449
412
}
450
413
} )
451
414
const fCount = files . length ;
452
- const tasks = files . map ( f => createNewNameByMode ( f , argv ) ) . filter ( f => f ?. outName )
415
+ //const tasks = files.map(f => createNewNameByMode(f, argv)).filter(f => f?.outName)
416
+
417
+ let tasks = await pMap ( files , createNewNameByMode , { concurrency : cpus ( ) . length * 4 } )
418
+ tasks = tasks . filter ( f => f ?. outName )
419
+
453
420
const tCount = tasks . length ;
454
421
log . showYellow (
455
422
logTag , `Total ${ fCount - tCount } files are skipped.`
0 commit comments