reelsort 0.2.7 → 0.2.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +358 -359
- package/dist/index.js +282 -290
- package/dist/index.mjs +252 -260
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/actions/clean.ts
|
|
2
2
|
import { existsSync as existsSync2, rmSync } from "fs";
|
|
3
|
-
import { Color } from "termkit";
|
|
3
|
+
import { Color, Spinner } from "termkit";
|
|
4
4
|
|
|
5
5
|
// src/db.ts
|
|
6
6
|
import Database from "better-sqlite3";
|
|
@@ -142,48 +142,8 @@ var getHistory = (limit = 10) => {
|
|
|
142
142
|
}));
|
|
143
143
|
};
|
|
144
144
|
|
|
145
|
-
// src/refs/spinner.ts
|
|
146
|
-
import { Spinner as TermSpinner } from "termkit";
|
|
147
|
-
var Spinner = class {
|
|
148
|
-
spinner;
|
|
149
|
-
constructor() {
|
|
150
|
-
this.spinner = new TermSpinner();
|
|
151
|
-
}
|
|
152
|
-
get text() {
|
|
153
|
-
return this.spinner.text;
|
|
154
|
-
}
|
|
155
|
-
set text(t) {
|
|
156
|
-
this.spinner.text = t;
|
|
157
|
-
}
|
|
158
|
-
start(s) {
|
|
159
|
-
if (s) this.spinner.text = s;
|
|
160
|
-
this.spinner.start();
|
|
161
|
-
return this;
|
|
162
|
-
}
|
|
163
|
-
info(s) {
|
|
164
|
-
this.spinner.info(s);
|
|
165
|
-
return this;
|
|
166
|
-
}
|
|
167
|
-
warn(s) {
|
|
168
|
-
this.spinner.warn(s);
|
|
169
|
-
return this;
|
|
170
|
-
}
|
|
171
|
-
fail(s) {
|
|
172
|
-
this.spinner.fail(s);
|
|
173
|
-
return this;
|
|
174
|
-
}
|
|
175
|
-
succeed(s) {
|
|
176
|
-
this.spinner.succeed(s);
|
|
177
|
-
return this;
|
|
178
|
-
}
|
|
179
|
-
stop() {
|
|
180
|
-
this.spinner.stop();
|
|
181
|
-
return this;
|
|
182
|
-
}
|
|
183
|
-
};
|
|
184
|
-
var spinner_default = new Spinner();
|
|
185
|
-
|
|
186
145
|
// src/actions/clean.ts
|
|
146
|
+
var spinner = new Spinner();
|
|
187
147
|
var parseOlderThan = (s) => {
|
|
188
148
|
const match = s.match(/^(\d+)([dhm])$/);
|
|
189
149
|
if (!match) return null;
|
|
@@ -193,11 +153,11 @@ var parseOlderThan = (s) => {
|
|
|
193
153
|
return ms;
|
|
194
154
|
};
|
|
195
155
|
var clean = async ({ dryRun, olderThan }) => {
|
|
196
|
-
|
|
156
|
+
spinner.start();
|
|
197
157
|
const imports = getCleanableImports();
|
|
198
158
|
if (imports.length === 0) {
|
|
199
|
-
|
|
200
|
-
|
|
159
|
+
spinner.info("nothing to clean");
|
|
160
|
+
spinner.stop();
|
|
201
161
|
return;
|
|
202
162
|
}
|
|
203
163
|
const cutoffMs = olderThan ? parseOlderThan(olderThan) : null;
|
|
@@ -216,29 +176,29 @@ var clean = async ({ dryRun, olderThan }) => {
|
|
|
216
176
|
continue;
|
|
217
177
|
}
|
|
218
178
|
if (dryRun) {
|
|
219
|
-
|
|
179
|
+
spinner.succeed(`[dry] would remove ${Color.white.encoder(imp.sourcePath)}`);
|
|
220
180
|
cleaned++;
|
|
221
181
|
continue;
|
|
222
182
|
}
|
|
223
183
|
try {
|
|
224
184
|
rmSync(imp.sourcePath, { recursive: true, force: true });
|
|
225
185
|
deleteImport(imp.id);
|
|
226
|
-
|
|
186
|
+
spinner.succeed(`removed ${Color.white.encoder(imp.sourcePath)}`);
|
|
227
187
|
cleaned++;
|
|
228
188
|
} catch {
|
|
229
|
-
|
|
189
|
+
spinner.warn(`locked or inaccessible, skipped: ${Color.white.encoder(imp.sourcePath)}`);
|
|
230
190
|
skipped++;
|
|
231
191
|
}
|
|
232
192
|
}
|
|
233
|
-
|
|
234
|
-
if (skipped)
|
|
235
|
-
|
|
193
|
+
spinner.succeed(`cleaned ${cleaned} items`);
|
|
194
|
+
if (skipped) spinner.info(`skipped ${skipped} items`);
|
|
195
|
+
spinner.stop();
|
|
236
196
|
};
|
|
237
197
|
var clean_default = clean;
|
|
238
198
|
|
|
239
199
|
// src/actions/config.ts
|
|
240
200
|
import { resolve } from "path";
|
|
241
|
-
import { Color as Color2, MultiSelect, Select } from "termkit";
|
|
201
|
+
import { Color as Color2, MultiSelect, Select, Spinner as Spinner2 } from "termkit";
|
|
242
202
|
|
|
243
203
|
// src/config.ts
|
|
244
204
|
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync, writeFileSync } from "fs";
|
|
@@ -285,29 +245,30 @@ var formatMovieName = (template, title, year, edition) => {
|
|
|
285
245
|
};
|
|
286
246
|
|
|
287
247
|
// src/actions/config.ts
|
|
248
|
+
var spinner2 = new Spinner2();
|
|
288
249
|
var DEST_TYPES = ["movie", "tv", "ps3", "book"];
|
|
289
250
|
var sourceAdd = async ({ dir }) => {
|
|
290
251
|
const resolved = resolve(dir);
|
|
291
252
|
const config = getConfig();
|
|
292
253
|
if (config.sources.includes(resolved)) {
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
254
|
+
spinner2.start();
|
|
255
|
+
spinner2.info(`source already configured: ${Color2.white.encoder(resolved)}`);
|
|
256
|
+
spinner2.stop();
|
|
296
257
|
return;
|
|
297
258
|
}
|
|
298
259
|
config.sources.push(resolved);
|
|
299
260
|
saveConfig(config);
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
261
|
+
spinner2.start();
|
|
262
|
+
spinner2.succeed(`added source: ${Color2.white.encoder(resolved)}`);
|
|
263
|
+
spinner2.stop();
|
|
303
264
|
};
|
|
304
265
|
var sourceRemove = async ({ dir }) => {
|
|
305
266
|
const config = getConfig();
|
|
306
267
|
if (!dir) {
|
|
307
268
|
if (config.sources.length === 0) {
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
269
|
+
spinner2.start();
|
|
270
|
+
spinner2.warn("no sources configured");
|
|
271
|
+
spinner2.stop();
|
|
311
272
|
return;
|
|
312
273
|
}
|
|
313
274
|
const select = new Select();
|
|
@@ -318,16 +279,16 @@ var sourceRemove = async ({ dir }) => {
|
|
|
318
279
|
const resolved = resolve(dir);
|
|
319
280
|
const index = config.sources.indexOf(resolved);
|
|
320
281
|
if (index === -1) {
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
282
|
+
spinner2.start();
|
|
283
|
+
spinner2.warn(`source not found: ${Color2.white.encoder(resolved)}`);
|
|
284
|
+
spinner2.stop();
|
|
324
285
|
return;
|
|
325
286
|
}
|
|
326
287
|
config.sources.splice(index, 1);
|
|
327
288
|
saveConfig(config);
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
289
|
+
spinner2.start();
|
|
290
|
+
spinner2.succeed(`removed source: ${Color2.white.encoder(resolved)}`);
|
|
291
|
+
spinner2.stop();
|
|
331
292
|
};
|
|
332
293
|
var destAdd = async ({ type, dir }) => {
|
|
333
294
|
if (!DEST_TYPES.includes(type)) {
|
|
@@ -337,18 +298,18 @@ var destAdd = async ({ type, dir }) => {
|
|
|
337
298
|
const config = getConfig();
|
|
338
299
|
config.dest[type] = resolved;
|
|
339
300
|
saveConfig(config);
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
301
|
+
spinner2.start();
|
|
302
|
+
spinner2.succeed(`set ${type} destination: ${Color2.green.encoder(resolved)}`);
|
|
303
|
+
spinner2.stop();
|
|
343
304
|
};
|
|
344
305
|
var destRemove = async ({ type }) => {
|
|
345
306
|
const config = getConfig();
|
|
346
307
|
if (!type) {
|
|
347
308
|
const configured = DEST_TYPES.filter((t) => config.dest[t]);
|
|
348
309
|
if (configured.length === 0) {
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
310
|
+
spinner2.start();
|
|
311
|
+
spinner2.warn("no destinations configured");
|
|
312
|
+
spinner2.stop();
|
|
352
313
|
return;
|
|
353
314
|
}
|
|
354
315
|
const select = new Select();
|
|
@@ -360,33 +321,33 @@ var destRemove = async ({ type }) => {
|
|
|
360
321
|
throw new Error(`unknown type '${type}', expected: ${DEST_TYPES.join(", ")}`);
|
|
361
322
|
}
|
|
362
323
|
if (!config.dest[type]) {
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
324
|
+
spinner2.start();
|
|
325
|
+
spinner2.warn(`no ${type} destination configured`);
|
|
326
|
+
spinner2.stop();
|
|
366
327
|
return;
|
|
367
328
|
}
|
|
368
329
|
delete config.dest[type];
|
|
369
330
|
saveConfig(config);
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
331
|
+
spinner2.start();
|
|
332
|
+
spinner2.succeed(`removed ${type} destination`);
|
|
333
|
+
spinner2.stop();
|
|
373
334
|
};
|
|
374
335
|
var configSet = async ({ key, subkey, value }) => {
|
|
375
336
|
const config = getConfig();
|
|
376
337
|
if (key === "language") {
|
|
377
338
|
config.language = subkey;
|
|
378
339
|
saveConfig(config);
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
340
|
+
spinner2.start();
|
|
341
|
+
spinner2.succeed(`set subtitle language: ${Color2.green.encoder(subkey)}`);
|
|
342
|
+
spinner2.stop();
|
|
382
343
|
return;
|
|
383
344
|
}
|
|
384
345
|
if (key === "tmdb-key") {
|
|
385
346
|
config.tmdbApiKey = subkey;
|
|
386
347
|
saveConfig(config);
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
348
|
+
spinner2.start();
|
|
349
|
+
spinner2.succeed(`set TMDb API key`);
|
|
350
|
+
spinner2.stop();
|
|
390
351
|
return;
|
|
391
352
|
}
|
|
392
353
|
if (key === "format") {
|
|
@@ -406,9 +367,9 @@ var configSet = async ({ key, subkey, value }) => {
|
|
|
406
367
|
throw new Error(`unknown format key '${subkey}', expected: movie, episode, season`);
|
|
407
368
|
}
|
|
408
369
|
saveConfig(config);
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
370
|
+
spinner2.start();
|
|
371
|
+
spinner2.succeed(`set ${subkey} format: ${Color2.green.encoder(value ?? subkey)}`);
|
|
372
|
+
spinner2.stop();
|
|
412
373
|
return;
|
|
413
374
|
}
|
|
414
375
|
throw new Error(`unknown key '${key}', expected: language, tmdb-key, format`);
|
|
@@ -448,12 +409,13 @@ Subtitle language: ${Color2.green.encoder(config.language ?? "eng (default)")}`)
|
|
|
448
409
|
// src/actions/differences.ts
|
|
449
410
|
import { existsSync as existsSync4, readdirSync } from "fs";
|
|
450
411
|
import { resolve as resolve2 } from "path";
|
|
451
|
-
import { Color as Color3 } from "termkit";
|
|
412
|
+
import { Color as Color3, Spinner as Spinner3 } from "termkit";
|
|
413
|
+
var spinner3 = new Spinner3();
|
|
452
414
|
var differences = async ({ dir1: rawDir1, dir2: rawDir2, only, ignore }) => {
|
|
453
415
|
let dir1 = rawDir1;
|
|
454
416
|
let dir2 = rawDir2;
|
|
455
|
-
|
|
456
|
-
|
|
417
|
+
spinner3.update(`checking differences between ${Color3.white.encoder(dir1)} and ${Color3.white.encoder(dir2)}`);
|
|
418
|
+
spinner3.start();
|
|
457
419
|
dir1 = resolve2(dir1);
|
|
458
420
|
dir2 = resolve2(dir2);
|
|
459
421
|
if (!existsSync4(dir1)) throw new Error(`dir1 ${dir1} does not exist`);
|
|
@@ -488,10 +450,10 @@ var differences = async ({ dir1: rawDir1, dir2: rawDir2, only, ignore }) => {
|
|
|
488
450
|
removed.push(l);
|
|
489
451
|
}
|
|
490
452
|
}
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
453
|
+
spinner3.succeed(`checked differences between ${Color3.white.encoder(dir1)} and ${Color3.white.encoder(dir2)}`);
|
|
454
|
+
spinner3.succeed(`found ${added.length} added files`);
|
|
455
|
+
spinner3.succeed(`found ${removed.length} removed files`);
|
|
456
|
+
spinner3.stop();
|
|
495
457
|
for (const i of added) console.log(`${Color3.green.encoder("added")} ${i}`);
|
|
496
458
|
for (const i of removed) console.log(`${Color3.red.encoder("removed")} ${i}`);
|
|
497
459
|
};
|
|
@@ -748,13 +710,14 @@ var list_default = list;
|
|
|
748
710
|
import { spawnSync } from "child_process";
|
|
749
711
|
import { existsSync as existsSync6, lstatSync as lstatSync2, readdirSync as readdirSync4 } from "fs";
|
|
750
712
|
import { resolve as resolve5 } from "path";
|
|
751
|
-
import { Color as Color6 } from "termkit";
|
|
713
|
+
import { Color as Color6, Spinner as Spinner4 } from "termkit";
|
|
752
714
|
|
|
753
715
|
// src/refs/verbose.ts
|
|
754
716
|
var _verbose = false;
|
|
755
717
|
var isVerbose = () => _verbose;
|
|
756
718
|
|
|
757
719
|
// src/actions/probe.ts
|
|
720
|
+
var spinner4 = new Spinner4();
|
|
758
721
|
var DEST_TYPES3 = ["movie", "tv", "ps3"];
|
|
759
722
|
var CODEC_MAP2 = {
|
|
760
723
|
hevc: "x265",
|
|
@@ -817,10 +780,10 @@ var walkVideoFiles = (dir, depth = 0, maxDepth = 3) => {
|
|
|
817
780
|
return results;
|
|
818
781
|
};
|
|
819
782
|
var probe = async ({ type, force }) => {
|
|
820
|
-
|
|
783
|
+
spinner4.start();
|
|
821
784
|
if (!isFfprobeAvailable()) {
|
|
822
|
-
|
|
823
|
-
|
|
785
|
+
spinner4.fail("ffprobe not found \u2014 install ffmpeg to use this command");
|
|
786
|
+
spinner4.stop();
|
|
824
787
|
return;
|
|
825
788
|
}
|
|
826
789
|
const config = getConfig();
|
|
@@ -830,30 +793,30 @@ var probe = async ({ type, force }) => {
|
|
|
830
793
|
for (const t of types) {
|
|
831
794
|
const destRoot = config.dest[t];
|
|
832
795
|
if (!existsSync6(destRoot)) continue;
|
|
833
|
-
|
|
796
|
+
spinner4.update(`scanning ${Color6.white.encoder(destRoot)}`);
|
|
834
797
|
const files = walkVideoFiles(destRoot);
|
|
835
798
|
for (const filePath of files) {
|
|
836
799
|
if (!force && getMediaInfo(filePath)) {
|
|
837
|
-
if (isVerbose())
|
|
800
|
+
if (isVerbose()) spinner4.info(`already probed: ${filePath}`);
|
|
838
801
|
skipped++;
|
|
839
802
|
continue;
|
|
840
803
|
}
|
|
841
|
-
|
|
804
|
+
spinner4.update(`probing ${Color6.white.encoder(filePath)}`);
|
|
842
805
|
const result = runFfprobe(filePath);
|
|
843
806
|
if (!result) {
|
|
844
|
-
if (isVerbose())
|
|
807
|
+
if (isVerbose()) spinner4.warn(`ffprobe failed: ${filePath}`);
|
|
845
808
|
failed++;
|
|
846
809
|
continue;
|
|
847
810
|
}
|
|
848
811
|
upsertMediaInfo(filePath, result.codec, result.resolution, result.width, result.height, result.duration);
|
|
849
|
-
if (isVerbose())
|
|
812
|
+
if (isVerbose()) spinner4.succeed(`${result.resolution ?? "?"} ${result.codec ?? "?"} ${filePath}`);
|
|
850
813
|
probed++;
|
|
851
814
|
}
|
|
852
815
|
}
|
|
853
|
-
|
|
854
|
-
if (skipped)
|
|
855
|
-
if (failed)
|
|
856
|
-
|
|
816
|
+
spinner4.succeed(`probed ${probed} files`);
|
|
817
|
+
if (skipped) spinner4.info(`skipped ${skipped} already indexed`);
|
|
818
|
+
if (failed) spinner4.warn(`failed ${failed} files`);
|
|
819
|
+
spinner4.stop();
|
|
857
820
|
};
|
|
858
821
|
var probe_default = probe;
|
|
859
822
|
|
|
@@ -861,7 +824,7 @@ var probe_default = probe;
|
|
|
861
824
|
import { existsSync as existsSync7, lstatSync as lstatSync3, readdirSync as readdirSync5, renameSync } from "fs";
|
|
862
825
|
import { resolve as resolve6 } from "path";
|
|
863
826
|
import { rimraf } from "rimraf";
|
|
864
|
-
import { Color as Color7 } from "termkit";
|
|
827
|
+
import { Color as Color7, Spinner as Spinner5 } from "termkit";
|
|
865
828
|
|
|
866
829
|
// src/helpers/findSubtitle.ts
|
|
867
830
|
var LANGUAGE_ALIASES = {
|
|
@@ -916,21 +879,22 @@ var titleCase_default = (s) => {
|
|
|
916
879
|
};
|
|
917
880
|
|
|
918
881
|
// src/actions/rename.ts
|
|
882
|
+
var spinner5 = new Spinner5();
|
|
919
883
|
var rename = async ({ dir: inputDir, type }) => {
|
|
920
884
|
const dir = resolve6(inputDir);
|
|
921
885
|
const sessionId = (/* @__PURE__ */ new Date()).toISOString();
|
|
922
886
|
const config = getConfig();
|
|
923
887
|
const language = config.language ?? "eng";
|
|
924
888
|
const movieFormat = config.format?.movie ?? DEFAULT_MOVIE_FORMAT;
|
|
925
|
-
|
|
926
|
-
|
|
889
|
+
spinner5.update(`renaming in ${Color7.white.encoder(dir)}`);
|
|
890
|
+
spinner5.start();
|
|
927
891
|
if (!existsSync7(dir)) throw new Error(`dir ${dir} does not exist`);
|
|
928
892
|
const list2 = readdirSync5(dir);
|
|
929
893
|
let renamed = 0, removed = 0, skipped = 0;
|
|
930
894
|
for (const [index, entry] of list2.entries()) {
|
|
931
|
-
|
|
895
|
+
spinner5.update(`renaming in ${Color7.white.encoder(dir)} ${index + 1}/${list2.length}`);
|
|
932
896
|
if (!lstatSync3(resolve6(dir, entry)).isDirectory()) {
|
|
933
|
-
if (isVerbose())
|
|
897
|
+
if (isVerbose()) spinner5.info(`skipped ${entry}`);
|
|
934
898
|
skipped++;
|
|
935
899
|
continue;
|
|
936
900
|
}
|
|
@@ -940,7 +904,7 @@ var rename = async ({ dir: inputDir, type }) => {
|
|
|
940
904
|
const nameMatch = entry.match(/(?<=\[).+?(?=\])/);
|
|
941
905
|
const id = entry.split("-")[0];
|
|
942
906
|
if (!nameMatch || !id) {
|
|
943
|
-
if (isVerbose())
|
|
907
|
+
if (isVerbose()) spinner5.info(`skipped ${entry}`);
|
|
944
908
|
skipped++;
|
|
945
909
|
continue;
|
|
946
910
|
}
|
|
@@ -948,19 +912,19 @@ var rename = async ({ dir: inputDir, type }) => {
|
|
|
948
912
|
const ps3New = resolve6(dir, `${nameMatch[0]} [${id}]`);
|
|
949
913
|
renameSync(ps3Old, ps3New);
|
|
950
914
|
recordRename(sessionId, ps3Old, ps3New);
|
|
951
|
-
|
|
915
|
+
spinner5.succeed(`${nameMatch[0]} [${id}]`);
|
|
952
916
|
renamed++;
|
|
953
917
|
continue;
|
|
954
918
|
}
|
|
955
919
|
const yearMatch = entry.match(/\([^\d]*(\d+)[^\d]*\)/);
|
|
956
920
|
if (!yearMatch) {
|
|
957
|
-
if (isVerbose())
|
|
921
|
+
if (isVerbose()) spinner5.info(`skipped ${entry}`);
|
|
958
922
|
skipped++;
|
|
959
923
|
continue;
|
|
960
924
|
}
|
|
961
925
|
const year = yearMatch[0];
|
|
962
926
|
if (year.length !== 6) {
|
|
963
|
-
if (isVerbose())
|
|
927
|
+
if (isVerbose()) spinner5.info(`skipped ${entry}`);
|
|
964
928
|
skipped++;
|
|
965
929
|
continue;
|
|
966
930
|
}
|
|
@@ -971,20 +935,20 @@ var rename = async ({ dir: inputDir, type }) => {
|
|
|
971
935
|
return videoExtensions_default.includes(ext2) && title.split(" ").reduce((a, w) => f.toLowerCase().includes(w.toLowerCase()) ? a : false, true);
|
|
972
936
|
});
|
|
973
937
|
if (!video) {
|
|
974
|
-
if (isVerbose())
|
|
938
|
+
if (isVerbose()) spinner5.info(`skipped ${entry}`);
|
|
975
939
|
skipped++;
|
|
976
940
|
continue;
|
|
977
941
|
}
|
|
978
942
|
const ext = video.match(/([^.]+$)/)?.[0];
|
|
979
943
|
if (!ext) {
|
|
980
|
-
if (isVerbose())
|
|
944
|
+
if (isVerbose()) spinner5.info(`skipped ${entry}`);
|
|
981
945
|
skipped++;
|
|
982
946
|
continue;
|
|
983
947
|
}
|
|
984
948
|
const yearNum = parseInt(year.replace(/\D/g, ""));
|
|
985
949
|
const formatted = formatMovieName(movieFormat, title, yearNum);
|
|
986
950
|
if (entry === formatted && video === `${formatted}.${ext}`) {
|
|
987
|
-
if (isVerbose())
|
|
951
|
+
if (isVerbose()) spinner5.info(`skipped ${entry}`);
|
|
988
952
|
skipped++;
|
|
989
953
|
continue;
|
|
990
954
|
}
|
|
@@ -1007,25 +971,26 @@ var rename = async ({ dir: inputDir, type }) => {
|
|
|
1007
971
|
renameSync(folderOld, folderNew);
|
|
1008
972
|
recordRename(sessionId, fileOld, fileNew);
|
|
1009
973
|
recordRename(sessionId, folderOld, folderNew);
|
|
1010
|
-
|
|
974
|
+
spinner5.succeed(formatted);
|
|
1011
975
|
renamed++;
|
|
1012
976
|
}
|
|
1013
|
-
|
|
1014
|
-
if (removed)
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
977
|
+
spinner5.succeed(`renamed ${renamed} files`);
|
|
978
|
+
if (removed) spinner5.info(`removed ${removed} files`);
|
|
979
|
+
spinner5.info(`skipped ${skipped} files`);
|
|
980
|
+
spinner5.succeed(`done in ${Color7.green.encoder(dir)}`);
|
|
981
|
+
spinner5.stop();
|
|
1018
982
|
};
|
|
1019
983
|
var rename_default = rename;
|
|
1020
984
|
|
|
1021
985
|
// src/actions/reset.ts
|
|
1022
986
|
import { existsSync as existsSync8, readdirSync as readdirSync6, renameSync as renameSync2 } from "fs";
|
|
1023
987
|
import { basename as basename2, dirname, resolve as resolve7, sep } from "path";
|
|
1024
|
-
import { Color as Color8 } from "termkit";
|
|
988
|
+
import { Color as Color8, Spinner as Spinner6 } from "termkit";
|
|
989
|
+
var spinner6 = new Spinner6();
|
|
1025
990
|
var reset = async ({ dir: inputDir, double }) => {
|
|
1026
991
|
let dir = inputDir;
|
|
1027
|
-
|
|
1028
|
-
|
|
992
|
+
spinner6.update(`resetting episodes in ${Color8.white.encoder(dir)}`);
|
|
993
|
+
spinner6.start();
|
|
1029
994
|
dir = resolve7(dir);
|
|
1030
995
|
if (!existsSync8(dir)) throw new Error(`dir ${dir} does not exist`);
|
|
1031
996
|
const list2 = readdirSync6(dir).sort();
|
|
@@ -1041,7 +1006,7 @@ var reset = async ({ dir: inputDir, double }) => {
|
|
|
1041
1006
|
if (!seasonNum) throw new Error(`unable to identify season number`);
|
|
1042
1007
|
const parentFolder = basename2(dirname(dir));
|
|
1043
1008
|
const showTitle = parentFolder.match(/^(.+?)\s*(?:\(\d{4}\))?$/)?.[1]?.trim() || void 0;
|
|
1044
|
-
|
|
1009
|
+
spinner6.info(`identified as season ${seasonNum}${showTitle ? ` of ${showTitle}` : ""}`);
|
|
1045
1010
|
const sublist = list2.filter((f) => {
|
|
1046
1011
|
const ext = f.match(/([^.]+$)/)?.[0];
|
|
1047
1012
|
return videoExtensions_default.includes(ext) && f.split(" ").reduce((a, w) => f.toLowerCase().includes(w.toLowerCase()) ? a : false, true);
|
|
@@ -1053,7 +1018,7 @@ var reset = async ({ dir: inputDir, double }) => {
|
|
|
1053
1018
|
const episodeFormat = getConfig().format?.episode;
|
|
1054
1019
|
let renamed = 0, skipped = other.length;
|
|
1055
1020
|
for (const [index, i] of sublist.entries()) {
|
|
1056
|
-
|
|
1021
|
+
spinner6.update(`resetting episodes in ${Color8.white.encoder(dir)} ${index}/${list2.length}`);
|
|
1057
1022
|
const ext = i.match(/([^.]+$)/)?.[0];
|
|
1058
1023
|
const episode = double ? index * 2 + 1 : index + 1;
|
|
1059
1024
|
const name = `${formatEpisode(seasonNum, episode, episodeFormat, double, showTitle)}.${ext}`;
|
|
@@ -1064,17 +1029,17 @@ var reset = async ({ dir: inputDir, double }) => {
|
|
|
1064
1029
|
renameSync2(resolve7(dir, i), resolve7(dir, name));
|
|
1065
1030
|
renamed++;
|
|
1066
1031
|
}
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1032
|
+
spinner6.succeed(`renamed ${renamed} files`);
|
|
1033
|
+
spinner6.info(`skipped ${skipped} files`);
|
|
1034
|
+
spinner6.succeed(`done in ${Color8.green.encoder(dir)}`);
|
|
1035
|
+
spinner6.stop();
|
|
1071
1036
|
};
|
|
1072
1037
|
var reset_default = reset;
|
|
1073
1038
|
|
|
1074
1039
|
// src/actions/scan.ts
|
|
1075
1040
|
import { cpSync, existsSync as existsSync10, linkSync, lstatSync as lstatSync4, mkdirSync as mkdirSync4, readdirSync as readdirSync7, renameSync as renameSync4, rmSync as rmSync3, statSync as statSync2 } from "fs";
|
|
1076
1041
|
import { dirname as dirname2, resolve as resolve8 } from "path";
|
|
1077
|
-
import { Color as Color9, MultiSelect as MultiSelect2, Select as Select2 } from "termkit";
|
|
1042
|
+
import { Color as Color9, MultiSelect as MultiSelect2, Select as Select2, Spinner as Spinner7 } from "termkit";
|
|
1078
1043
|
|
|
1079
1044
|
// src/helpers/detectEdition.ts
|
|
1080
1045
|
var EDITIONS = [
|
|
@@ -1235,6 +1200,7 @@ var searchTv = async (title, apiKey) => {
|
|
|
1235
1200
|
var bookExtensions_default = ["epub", "mobi", "azw3", "azw"];
|
|
1236
1201
|
|
|
1237
1202
|
// src/actions/scan.ts
|
|
1203
|
+
var spinner7 = new Spinner7();
|
|
1238
1204
|
var sameDev = (a, b) => {
|
|
1239
1205
|
try {
|
|
1240
1206
|
let bExisting = b;
|
|
@@ -1367,46 +1333,49 @@ var gatherEntries = (source) => {
|
|
|
1367
1333
|
}
|
|
1368
1334
|
return result;
|
|
1369
1335
|
};
|
|
1370
|
-
var
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1336
|
+
var nTitle = (s) => s.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
1337
|
+
var showDirIndexCache = /* @__PURE__ */ new Map();
|
|
1338
|
+
var getShowDirIndex = (destRoot) => {
|
|
1339
|
+
if (showDirIndexCache.has(destRoot)) return showDirIndexCache.get(destRoot);
|
|
1340
|
+
const idx = /* @__PURE__ */ new Map();
|
|
1341
|
+
showDirIndexCache.set(destRoot, idx);
|
|
1342
|
+
if (!existsSync10(destRoot)) return idx;
|
|
1343
|
+
let dirs;
|
|
1344
|
+
try {
|
|
1345
|
+
dirs = readdirSync7(destRoot);
|
|
1346
|
+
} catch {
|
|
1347
|
+
return idx;
|
|
1348
|
+
}
|
|
1349
|
+
for (const dir of dirs) {
|
|
1375
1350
|
try {
|
|
1376
|
-
|
|
1351
|
+
if (!lstatSync4(resolve8(destRoot, dir)).isDirectory()) continue;
|
|
1377
1352
|
} catch {
|
|
1378
|
-
|
|
1353
|
+
continue;
|
|
1379
1354
|
}
|
|
1380
|
-
|
|
1381
|
-
};
|
|
1382
|
-
var findShowFolderByContent = (destRoot, title) => {
|
|
1383
|
-
if (!existsSync10(destRoot)) return null;
|
|
1384
|
-
const normalize = (s) => s.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
1385
|
-
const target = normalize(title);
|
|
1386
|
-
const matchesTitle = (name) => {
|
|
1387
|
-
if (!isTvEpisodeName(name)) return false;
|
|
1388
|
-
const p = parseDownloadName(name);
|
|
1389
|
-
return !!p && normalize(p.title) === target;
|
|
1390
|
-
};
|
|
1391
|
-
for (const folder of readdirSync7(destRoot)) {
|
|
1355
|
+
idx.set(nTitle(dir), dir);
|
|
1392
1356
|
try {
|
|
1393
|
-
const
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1357
|
+
const children = readdirSync7(resolve8(destRoot, dir));
|
|
1358
|
+
const tryFile = (name) => {
|
|
1359
|
+
if (!isTvEpisodeName(name)) return;
|
|
1360
|
+
const p = parseDownloadName(name);
|
|
1361
|
+
if (p) {
|
|
1362
|
+
const k = nTitle(p.title);
|
|
1363
|
+
if (!idx.has(k)) idx.set(k, dir);
|
|
1364
|
+
}
|
|
1365
|
+
};
|
|
1366
|
+
children.forEach(tryFile);
|
|
1367
|
+
children.forEach((child) => {
|
|
1368
|
+
if (!isSeasonDirName(child)) return;
|
|
1399
1369
|
try {
|
|
1400
|
-
const
|
|
1401
|
-
if (
|
|
1402
|
-
if (readdirSync7(seasonPath).some(matchesTitle)) return folder;
|
|
1370
|
+
const sp = resolve8(destRoot, dir, child);
|
|
1371
|
+
if (lstatSync4(sp).isDirectory()) readdirSync7(sp).forEach(tryFile);
|
|
1403
1372
|
} catch {
|
|
1404
1373
|
}
|
|
1405
|
-
}
|
|
1374
|
+
});
|
|
1406
1375
|
} catch {
|
|
1407
1376
|
}
|
|
1408
1377
|
}
|
|
1409
|
-
return
|
|
1378
|
+
return idx;
|
|
1410
1379
|
};
|
|
1411
1380
|
var findSeasonFolder = (showPath, season, specialsFolder) => {
|
|
1412
1381
|
if (!existsSync10(showPath)) return null;
|
|
@@ -1436,13 +1405,17 @@ var classifyMovieConfidence = (entry) => {
|
|
|
1436
1405
|
return "ambiguous";
|
|
1437
1406
|
};
|
|
1438
1407
|
var typeColor = {
|
|
1439
|
-
movie: (s) => Color9.
|
|
1440
|
-
tv: (s) => Color9.
|
|
1441
|
-
book: (s) => Color9.
|
|
1408
|
+
movie: (s) => Color9.yellow.encoder(s),
|
|
1409
|
+
tv: (s) => Color9.blue.encoder(s),
|
|
1410
|
+
book: (s) => Color9.white.encoder(s),
|
|
1442
1411
|
ps3: (s) => Color9.magenta.encoder(s)
|
|
1443
1412
|
};
|
|
1444
|
-
var typeGlyph = (t) => typeColor[t]("
|
|
1413
|
+
var typeGlyph = (t) => typeColor[t]("?");
|
|
1414
|
+
var checkGlyph = (t) => typeColor[t]("\u2714");
|
|
1415
|
+
var greyGlyph = Color9.white.faint.encoder("\u25CF");
|
|
1416
|
+
var warnGlyph = Color9.yellow.encoder("\u26A0");
|
|
1445
1417
|
var typeTag = (t) => isVerbose() ? Color9.white.faint.encoder(` (${t})`) : "";
|
|
1418
|
+
var sortByEntry = (arr) => arr.sort((a, b) => a.entry.localeCompare(b.entry, void 0, { sensitivity: "base" }));
|
|
1446
1419
|
var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactive }) => {
|
|
1447
1420
|
const config = getConfig();
|
|
1448
1421
|
const sessionId = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -1450,19 +1423,20 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1450
1423
|
const movieFormat = config.format?.movie ?? DEFAULT_MOVIE_FORMAT;
|
|
1451
1424
|
const seasonFormat = config.format?.season ?? DEFAULT_SEASON_FORMAT;
|
|
1452
1425
|
const specialsFolder = config.specialsFolder ?? "Specials";
|
|
1426
|
+
const dryTag = dryRun ? Color9.white.faint.encoder(" [dry]") : "";
|
|
1453
1427
|
const lookupMovie = async (parsed) => {
|
|
1454
1428
|
let tmdbId;
|
|
1455
1429
|
let resolvedTitle = parsed.title;
|
|
1456
1430
|
let resolvedYear = parsed.year;
|
|
1457
1431
|
if (config.tmdbApiKey) {
|
|
1458
|
-
|
|
1432
|
+
spinner7.update(`TMDb: ${parsed.title}`);
|
|
1459
1433
|
const results = await searchMovie(parsed.title, parsed.year, config.tmdbApiKey);
|
|
1460
1434
|
if (results.length === 1) {
|
|
1461
1435
|
tmdbId = results[0].id;
|
|
1462
1436
|
resolvedTitle = results[0].title;
|
|
1463
1437
|
resolvedYear = results[0].year ?? parsed.year;
|
|
1464
1438
|
} else if (results.length > 1) {
|
|
1465
|
-
|
|
1439
|
+
spinner7.stop();
|
|
1466
1440
|
const select = new Select2();
|
|
1467
1441
|
const items = results.map((r) => ({
|
|
1468
1442
|
label: r.year ? `${r.title} (${r.year})` : r.title,
|
|
@@ -1470,7 +1444,7 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1470
1444
|
...r
|
|
1471
1445
|
}));
|
|
1472
1446
|
const picked = await select.ask(`Multiple movies found for "${parsed.title}":`, items);
|
|
1473
|
-
|
|
1447
|
+
spinner7.start();
|
|
1474
1448
|
if (picked) {
|
|
1475
1449
|
tmdbId = picked.id;
|
|
1476
1450
|
resolvedTitle = picked.title;
|
|
@@ -1485,12 +1459,12 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1485
1459
|
const folderName = formatMovieName(movieFormat, resolvedTitle, resolvedYear, edition);
|
|
1486
1460
|
const destFolder = resolve8(destRoot, folderName);
|
|
1487
1461
|
if (existsSync10(destFolder)) {
|
|
1488
|
-
|
|
1462
|
+
spinner7.log(`${typeColor.movie(folderName)}${typeTag("movie")}`, greyGlyph);
|
|
1489
1463
|
return false;
|
|
1490
1464
|
}
|
|
1491
1465
|
const videoFile = isDir ? findVideo(entryPath) : entry;
|
|
1492
1466
|
if (!videoFile) {
|
|
1493
|
-
|
|
1467
|
+
spinner7.log(`${entry}${typeTag("movie")}`, warnGlyph);
|
|
1494
1468
|
return false;
|
|
1495
1469
|
}
|
|
1496
1470
|
const videoExt = videoFile.match(/([^.]+$)/)?.[0];
|
|
@@ -1511,7 +1485,7 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1511
1485
|
linkSync(videoSourcePath, destVideoPath);
|
|
1512
1486
|
mode = "hardlink";
|
|
1513
1487
|
} catch {
|
|
1514
|
-
|
|
1488
|
+
spinner7.warn(`hardlink unavailable \u2014 copying instead: ${folderName}`);
|
|
1515
1489
|
cpSync(videoSourcePath, destVideoPath);
|
|
1516
1490
|
mode = "copy";
|
|
1517
1491
|
}
|
|
@@ -1537,10 +1511,10 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1537
1511
|
recordImport(sessionId, entryPath, destFolder, "move", tmdbId, "movie");
|
|
1538
1512
|
}
|
|
1539
1513
|
}
|
|
1540
|
-
|
|
1514
|
+
spinner7.log(`${typeColor.movie(folderName)}${typeTag("movie")}${dryTag}`, checkGlyph("movie"));
|
|
1541
1515
|
return true;
|
|
1542
1516
|
};
|
|
1543
|
-
|
|
1517
|
+
spinner7.start();
|
|
1544
1518
|
if (config.sources.length === 0) throw new Error("no sources configured \u2014 run: reelsort config add source <dir>");
|
|
1545
1519
|
let imported = 0, skipped = 0;
|
|
1546
1520
|
const pendingMovies = [];
|
|
@@ -1551,15 +1525,19 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1551
1525
|
const seenIgnored = /* @__PURE__ */ new Set();
|
|
1552
1526
|
for (const source of config.sources) {
|
|
1553
1527
|
if (!existsSync10(source)) {
|
|
1554
|
-
|
|
1528
|
+
spinner7.warn(`source not found: ${Color9.white.encoder(source)}`);
|
|
1555
1529
|
continue;
|
|
1556
1530
|
}
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1531
|
+
spinner7.update(`scanning ${Color9.white.encoder(source)}`);
|
|
1532
|
+
const entries = gatherEntries(source).sort(
|
|
1533
|
+
(a, b) => a.entry.localeCompare(b.entry, void 0, { sensitivity: "base" })
|
|
1534
|
+
);
|
|
1535
|
+
for (const { entry, entryPath, isDir } of entries) {
|
|
1536
|
+
spinner7.update(`scanning: ${entry}`);
|
|
1560
1537
|
if (ignoreSet.has(entry)) {
|
|
1561
1538
|
seenIgnored.add(entry);
|
|
1562
|
-
if (isVerbose())
|
|
1539
|
+
if (isVerbose()) spinner7.log(entry, greyGlyph);
|
|
1540
|
+
skipped++;
|
|
1563
1541
|
continue;
|
|
1564
1542
|
}
|
|
1565
1543
|
const ext = entry.match(/([^.]+$)/)?.[0];
|
|
@@ -1579,7 +1557,7 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1579
1557
|
}
|
|
1580
1558
|
const destRoot = config.dest[detectedType];
|
|
1581
1559
|
if (!destRoot) {
|
|
1582
|
-
if (isVerbose())
|
|
1560
|
+
if (isVerbose()) spinner7.log(`${entry}${typeTag(detectedType)}`, greyGlyph);
|
|
1583
1561
|
skipped++;
|
|
1584
1562
|
continue;
|
|
1585
1563
|
}
|
|
@@ -1593,7 +1571,7 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1593
1571
|
const destName = `${nameMatch[0]} [${id}]`;
|
|
1594
1572
|
const destPath = resolve8(destRoot, destName);
|
|
1595
1573
|
if (existsSync10(destPath)) {
|
|
1596
|
-
|
|
1574
|
+
spinner7.log(`${typeColor.ps3(destName)}${typeTag("ps3")}`, greyGlyph);
|
|
1597
1575
|
skipped++;
|
|
1598
1576
|
continue;
|
|
1599
1577
|
}
|
|
@@ -1601,14 +1579,14 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1601
1579
|
moveFolder(entryPath, destPath);
|
|
1602
1580
|
recordImport(sessionId, entryPath, destPath, "move", void 0, "ps3");
|
|
1603
1581
|
}
|
|
1604
|
-
|
|
1582
|
+
spinner7.log(`${typeColor.ps3(destName)}${typeTag("ps3")}${dryTag}`, checkGlyph("ps3"));
|
|
1605
1583
|
imported++;
|
|
1606
1584
|
continue;
|
|
1607
1585
|
}
|
|
1608
1586
|
if (detectedType === "book") {
|
|
1609
1587
|
const destPath = resolve8(destRoot, entry);
|
|
1610
1588
|
if (existsSync10(destPath)) {
|
|
1611
|
-
|
|
1589
|
+
spinner7.log(`${typeColor.book(entry)}${typeTag("book")}`, greyGlyph);
|
|
1612
1590
|
skipped++;
|
|
1613
1591
|
continue;
|
|
1614
1592
|
}
|
|
@@ -1626,7 +1604,7 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1626
1604
|
}
|
|
1627
1605
|
recordImport(sessionId, entryPath, destPath, "move", void 0, "book");
|
|
1628
1606
|
}
|
|
1629
|
-
|
|
1607
|
+
spinner7.log(`${typeColor.book(entry)}${typeTag("book")}${dryTag}`, checkGlyph("book"));
|
|
1630
1608
|
imported++;
|
|
1631
1609
|
continue;
|
|
1632
1610
|
}
|
|
@@ -1635,10 +1613,10 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1635
1613
|
if (videoCount === 0) {
|
|
1636
1614
|
if (containsPdf(entryPath)) {
|
|
1637
1615
|
pendingBooks.push({ entry, entryPath });
|
|
1638
|
-
} else
|
|
1639
|
-
|
|
1616
|
+
} else {
|
|
1617
|
+
if (isVerbose()) spinner7.log(entry, greyGlyph);
|
|
1618
|
+
skipped++;
|
|
1640
1619
|
}
|
|
1641
|
-
skipped++;
|
|
1642
1620
|
continue;
|
|
1643
1621
|
}
|
|
1644
1622
|
if (videoCount >= 2) {
|
|
@@ -1648,14 +1626,14 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1648
1626
|
}
|
|
1649
1627
|
const parsed = parseDownloadName(entry);
|
|
1650
1628
|
if (!parsed) {
|
|
1651
|
-
if (isVerbose())
|
|
1629
|
+
if (isVerbose()) spinner7.log(entry, greyGlyph);
|
|
1652
1630
|
skipped++;
|
|
1653
1631
|
continue;
|
|
1654
1632
|
}
|
|
1655
1633
|
if (detectedType === "movie") {
|
|
1656
1634
|
const confidence = classifyMovieConfidence(entry);
|
|
1657
1635
|
if (confidence === "skip") {
|
|
1658
|
-
if (isVerbose())
|
|
1636
|
+
if (isVerbose()) spinner7.log(entry, greyGlyph);
|
|
1659
1637
|
skipped++;
|
|
1660
1638
|
continue;
|
|
1661
1639
|
}
|
|
@@ -1669,14 +1647,14 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1669
1647
|
let resolvedYear = parsed.year;
|
|
1670
1648
|
if (config.tmdbApiKey) {
|
|
1671
1649
|
if (detectedType === "tv") {
|
|
1672
|
-
|
|
1650
|
+
spinner7.update(`TMDb: ${parsed.title}`);
|
|
1673
1651
|
const results = await searchTv(parsed.title, config.tmdbApiKey);
|
|
1674
1652
|
if (results.length === 1) {
|
|
1675
1653
|
tmdbId = results[0].id;
|
|
1676
1654
|
resolvedTitle = results[0].title;
|
|
1677
1655
|
resolvedYear = results[0].year ?? parsed.year;
|
|
1678
1656
|
} else if (results.length > 1) {
|
|
1679
|
-
|
|
1657
|
+
spinner7.stop();
|
|
1680
1658
|
const select = new Select2();
|
|
1681
1659
|
const items = results.map((r) => ({
|
|
1682
1660
|
label: r.year ? `${r.title} (${r.year})` : r.title,
|
|
@@ -1684,7 +1662,7 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1684
1662
|
...r
|
|
1685
1663
|
}));
|
|
1686
1664
|
const picked = await select.ask(`Multiple shows found for "${parsed.title}":`, items);
|
|
1687
|
-
|
|
1665
|
+
spinner7.start();
|
|
1688
1666
|
if (picked) {
|
|
1689
1667
|
tmdbId = picked.id;
|
|
1690
1668
|
resolvedTitle = picked.title;
|
|
@@ -1700,7 +1678,7 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1700
1678
|
}
|
|
1701
1679
|
if (detectedType === "tv") {
|
|
1702
1680
|
if (parsed.season === void 0) {
|
|
1703
|
-
if (isVerbose())
|
|
1681
|
+
if (isVerbose()) spinner7.log(entry, greyGlyph);
|
|
1704
1682
|
skipped++;
|
|
1705
1683
|
continue;
|
|
1706
1684
|
}
|
|
@@ -1711,7 +1689,7 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1711
1689
|
showPath = registeredShow.path;
|
|
1712
1690
|
showFolderName = showPath.split("/").pop() ?? registeredShow.path;
|
|
1713
1691
|
} else {
|
|
1714
|
-
const existingFolder =
|
|
1692
|
+
const existingFolder = getShowDirIndex(destRoot).get(nTitle(resolvedTitle)) ?? null;
|
|
1715
1693
|
if (existingFolder) {
|
|
1716
1694
|
showFolderName = existingFolder;
|
|
1717
1695
|
showPath = resolve8(destRoot, existingFolder);
|
|
@@ -1719,6 +1697,7 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1719
1697
|
showFolderName = formatMovieName(movieFormat, resolvedTitle, resolvedYear);
|
|
1720
1698
|
showPath = resolve8(destRoot, showFolderName);
|
|
1721
1699
|
if (!dryRun) upsertShow(showPath, tmdbId ?? null, resolvedTitle);
|
|
1700
|
+
getShowDirIndex(destRoot).set(nTitle(resolvedTitle), showFolderName);
|
|
1722
1701
|
} else {
|
|
1723
1702
|
pendingTv.push({ entry, entryPath, isDir, parsed, resolvedTitle, destRoot });
|
|
1724
1703
|
continue;
|
|
@@ -1728,12 +1707,12 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1728
1707
|
const seasonPath = resolve8(showPath, seasonFolderName);
|
|
1729
1708
|
const videoFile = isDir ? findVideo(entryPath) : entry;
|
|
1730
1709
|
if (!videoFile) {
|
|
1731
|
-
|
|
1710
|
+
spinner7.log(`${entry}${typeTag("tv")}`, warnGlyph);
|
|
1732
1711
|
skipped++;
|
|
1733
1712
|
continue;
|
|
1734
1713
|
}
|
|
1735
1714
|
const videoExt = videoFile.match(/([^.]+$)/)?.[0];
|
|
1736
|
-
if (tmdbId && config.tmdbApiKey)
|
|
1715
|
+
if (tmdbId && config.tmdbApiKey) spinner7.update(`TMDb: episode name for ${resolvedTitle}`);
|
|
1737
1716
|
const tmdbEpisodeName = tmdbId && config.tmdbApiKey ? await getEpisodeName(tmdbId, parsed.season, parsed.episode ?? 1, config.tmdbApiKey) : null;
|
|
1738
1717
|
const episodeName = formatEpisode(parsed.season, parsed.episode ?? 1, config.format?.episode, false, resolvedTitle, tmdbEpisodeName ?? void 0);
|
|
1739
1718
|
const destVideoName = `${episodeName}.${videoExt}`;
|
|
@@ -1743,17 +1722,17 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1743
1722
|
const isRepack = /\brepack\d*\b|\bproper\b/i.test(entry);
|
|
1744
1723
|
let shouldReplace = force || isRepack;
|
|
1745
1724
|
if (!shouldReplace && interactive) {
|
|
1746
|
-
|
|
1725
|
+
spinner7.stop();
|
|
1747
1726
|
const select = new Select2();
|
|
1748
1727
|
const picked = await select.ask(`Already exists \u2014 replace?`, [
|
|
1749
1728
|
{ label: `${showFolderName} / ${seasonFolderName} / ${episodeName}`, value: "replace" },
|
|
1750
1729
|
{ label: "Skip", value: "skip" }
|
|
1751
1730
|
]);
|
|
1752
|
-
|
|
1731
|
+
spinner7.start();
|
|
1753
1732
|
shouldReplace = picked?.value === "replace";
|
|
1754
1733
|
}
|
|
1755
1734
|
if (!shouldReplace) {
|
|
1756
|
-
|
|
1735
|
+
spinner7.log(`${typeColor.tv(`${showFolderName} / ${seasonFolderName} / ${episodeName}`)}${typeTag("tv")}`, greyGlyph);
|
|
1757
1736
|
skipped++;
|
|
1758
1737
|
continue;
|
|
1759
1738
|
}
|
|
@@ -1777,7 +1756,7 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1777
1756
|
linkSync(videoSourcePath, destVideoPath);
|
|
1778
1757
|
mode = "hardlink";
|
|
1779
1758
|
} catch {
|
|
1780
|
-
|
|
1759
|
+
spinner7.warn(`hardlink unavailable \u2014 copying instead: ${episodeName}`);
|
|
1781
1760
|
cpSync(videoSourcePath, destVideoPath);
|
|
1782
1761
|
mode = "copy";
|
|
1783
1762
|
}
|
|
@@ -1794,7 +1773,7 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1794
1773
|
}
|
|
1795
1774
|
recordImport(sessionId, entryPath, seasonPath, mode, tmdbId, "tv");
|
|
1796
1775
|
}
|
|
1797
|
-
|
|
1776
|
+
spinner7.log(`${typeColor.tv(`${showFolderName} / ${seasonFolderName} / ${episodeName}`)}${typeTag("tv")}${dryTag}`, checkGlyph("tv"));
|
|
1798
1777
|
imported++;
|
|
1799
1778
|
continue;
|
|
1800
1779
|
}
|
|
@@ -1805,12 +1784,11 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1805
1784
|
}
|
|
1806
1785
|
}
|
|
1807
1786
|
}
|
|
1787
|
+
let uncertainMovies = 0;
|
|
1808
1788
|
if (pendingMovies.length > 0) {
|
|
1809
|
-
spinner_default.warn(`${pendingMovies.length} uncertain movie match${pendingMovies.length > 1 ? "es" : ""} skipped \u2014 use -i to review or -f to import all`);
|
|
1810
|
-
for (const p of pendingMovies) spinner_default.info(` ${typeGlyph("movie")} ${p.entry.replace(/\/$/, "")}${typeTag("movie")}`);
|
|
1811
1789
|
let toProcess = [];
|
|
1812
1790
|
if (interactive) {
|
|
1813
|
-
|
|
1791
|
+
spinner7.stop();
|
|
1814
1792
|
const ms = new MultiSelect2({ allowSkip: true, search: true, maxHeight: 20 });
|
|
1815
1793
|
const items = pendingMovies.map((p) => ({
|
|
1816
1794
|
label: p.entry.replace(/\/$/, ""),
|
|
@@ -1818,12 +1796,15 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1818
1796
|
...p
|
|
1819
1797
|
}));
|
|
1820
1798
|
toProcess = await ms.ask("Select entries to import as movies:", items) ?? [];
|
|
1821
|
-
|
|
1822
|
-
skipped += pendingMovies.length - toProcess.length;
|
|
1799
|
+
spinner7.start();
|
|
1823
1800
|
} else if (force) {
|
|
1824
1801
|
toProcess = pendingMovies;
|
|
1825
|
-
}
|
|
1826
|
-
|
|
1802
|
+
}
|
|
1803
|
+
const toSkip = pendingMovies.filter((p) => !toProcess.includes(p));
|
|
1804
|
+
uncertainMovies = toSkip.length;
|
|
1805
|
+
sortByEntry(toSkip);
|
|
1806
|
+
for (const p of toSkip) {
|
|
1807
|
+
spinner7.log(`${typeColor.movie(p.entry.replace(/\/$/, ""))}${typeTag("movie")}`, typeGlyph("movie"));
|
|
1827
1808
|
}
|
|
1828
1809
|
for (const p of toProcess) {
|
|
1829
1810
|
const { tmdbId, resolvedTitle, resolvedYear } = await lookupMovie(p.parsed);
|
|
@@ -1835,18 +1816,22 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1835
1816
|
}
|
|
1836
1817
|
}
|
|
1837
1818
|
if (pendingTv.length > 0) {
|
|
1838
|
-
|
|
1839
|
-
for (const p of pendingTv)
|
|
1840
|
-
|
|
1819
|
+
pendingTv.sort((a, b) => a.resolvedTitle.localeCompare(b.resolvedTitle, void 0, { sensitivity: "base" }));
|
|
1820
|
+
for (const p of pendingTv) {
|
|
1821
|
+
spinner7.log(`${typeColor.tv(p.resolvedTitle)} \u2014 ${p.entry.replace(/\/$/, "")}${typeTag("tv")}`, typeGlyph("tv"));
|
|
1822
|
+
}
|
|
1841
1823
|
}
|
|
1842
1824
|
if (pendingBooks.length > 0) {
|
|
1843
|
-
|
|
1844
|
-
for (const p of pendingBooks)
|
|
1825
|
+
sortByEntry(pendingBooks);
|
|
1826
|
+
for (const p of pendingBooks) {
|
|
1827
|
+
spinner7.log(`${typeColor.book(p.entry)}${typeTag("book")}`, typeGlyph("book"));
|
|
1828
|
+
}
|
|
1845
1829
|
}
|
|
1846
1830
|
if (pendingAnime.length > 0) {
|
|
1847
|
-
|
|
1848
|
-
for (const p of pendingAnime)
|
|
1849
|
-
|
|
1831
|
+
sortByEntry(pendingAnime);
|
|
1832
|
+
for (const p of pendingAnime) {
|
|
1833
|
+
spinner7.log(`${typeColor.tv(p.entry)} (${p.videoCount} video${p.videoCount > 1 ? "s" : ""})${typeTag("tv")}`, typeGlyph("tv"));
|
|
1834
|
+
}
|
|
1850
1835
|
}
|
|
1851
1836
|
if (ignoreSet.size > 0) {
|
|
1852
1837
|
const stale = [...ignoreSet].filter((name) => !seenIgnored.has(name));
|
|
@@ -1854,25 +1839,31 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1854
1839
|
const updated = config.ignore.filter((name) => !stale.includes(name));
|
|
1855
1840
|
config.ignore = updated;
|
|
1856
1841
|
saveConfig(config);
|
|
1857
|
-
for (const name of stale)
|
|
1842
|
+
for (const name of stale) spinner7.info(`removed from ignore list (not found): ${Color9.white.encoder(name)}`);
|
|
1858
1843
|
}
|
|
1859
1844
|
}
|
|
1860
|
-
|
|
1861
|
-
if (skipped)
|
|
1862
|
-
|
|
1845
|
+
const summaryParts = [`${dryRun ? "would import" : "imported"} ${imported}`];
|
|
1846
|
+
if (skipped) summaryParts.push(`skipped ${skipped}`);
|
|
1847
|
+
if (uncertainMovies) summaryParts.push(`uncertain movie ${uncertainMovies}`);
|
|
1848
|
+
if (pendingTv.length) summaryParts.push(`uncertain tv ${pendingTv.length}`);
|
|
1849
|
+
if (pendingBooks.length) summaryParts.push(`uncertain book ${pendingBooks.length}`);
|
|
1850
|
+
if (pendingAnime.length) summaryParts.push(`uncertain anime ${pendingAnime.length}`);
|
|
1851
|
+
spinner7.succeed(summaryParts.join(", "));
|
|
1852
|
+
spinner7.stop();
|
|
1863
1853
|
};
|
|
1864
1854
|
var scan_default = scan;
|
|
1865
1855
|
|
|
1866
1856
|
// src/actions/undo.ts
|
|
1867
1857
|
import { existsSync as existsSync11, renameSync as renameSync5 } from "fs";
|
|
1868
|
-
import { Color as Color10 } from "termkit";
|
|
1858
|
+
import { Color as Color10, Spinner as Spinner8 } from "termkit";
|
|
1859
|
+
var spinner8 = new Spinner8();
|
|
1869
1860
|
var undo = async () => {
|
|
1870
|
-
|
|
1861
|
+
spinner8.start();
|
|
1871
1862
|
const renameRecords = getLastSession();
|
|
1872
1863
|
const importRecords = getLastImportSession();
|
|
1873
1864
|
if (renameRecords.length === 0 && importRecords.length === 0) {
|
|
1874
|
-
|
|
1875
|
-
|
|
1865
|
+
spinner8.info("nothing to undo");
|
|
1866
|
+
spinner8.stop();
|
|
1876
1867
|
return;
|
|
1877
1868
|
}
|
|
1878
1869
|
const useImports = importRecords.length > 0 && (renameRecords.length === 0 || importRecords[0].sessionId > renameRecords[0].sessionId);
|
|
@@ -1880,40 +1871,40 @@ var undo = async () => {
|
|
|
1880
1871
|
let undone2 = 0;
|
|
1881
1872
|
for (const record of renameRecords) {
|
|
1882
1873
|
renameSync5(record.newPath, record.oldPath);
|
|
1883
|
-
|
|
1874
|
+
spinner8.succeed(`${Color10.green.encoder(record.newPath)} \u2192 ${Color10.white.encoder(record.oldPath)}`);
|
|
1884
1875
|
undone2++;
|
|
1885
1876
|
}
|
|
1886
1877
|
deleteSession(renameRecords[0].sessionId);
|
|
1887
|
-
|
|
1888
|
-
|
|
1878
|
+
spinner8.succeed(`undid ${undone2} renames`);
|
|
1879
|
+
spinner8.stop();
|
|
1889
1880
|
return;
|
|
1890
1881
|
}
|
|
1891
1882
|
let undone = 0;
|
|
1892
1883
|
let skipped = 0;
|
|
1893
1884
|
for (const record of importRecords) {
|
|
1894
1885
|
if (record.mode !== "move") {
|
|
1895
|
-
|
|
1886
|
+
spinner8.info(`skipped ${record.destinationPath} (${record.mode} \u2014 source file unchanged)`);
|
|
1896
1887
|
skipped++;
|
|
1897
1888
|
continue;
|
|
1898
1889
|
}
|
|
1899
1890
|
if (record.type === "tv") {
|
|
1900
|
-
|
|
1891
|
+
spinner8.info(`skipped TV import \u2014 season folder cannot be cleanly reversed: ${record.destinationPath}`);
|
|
1901
1892
|
skipped++;
|
|
1902
1893
|
continue;
|
|
1903
1894
|
}
|
|
1904
1895
|
if (!existsSync11(record.destinationPath)) {
|
|
1905
|
-
|
|
1896
|
+
spinner8.info(`skipped \u2014 destination no longer exists: ${record.destinationPath}`);
|
|
1906
1897
|
skipped++;
|
|
1907
1898
|
continue;
|
|
1908
1899
|
}
|
|
1909
1900
|
renameSync5(record.destinationPath, record.sourcePath);
|
|
1910
|
-
|
|
1901
|
+
spinner8.succeed(`${Color10.green.encoder(record.destinationPath)} \u2192 ${Color10.white.encoder(record.sourcePath)}`);
|
|
1911
1902
|
undone++;
|
|
1912
1903
|
}
|
|
1913
1904
|
deleteImportSession(importRecords[0].sessionId);
|
|
1914
|
-
if (undone > 0)
|
|
1915
|
-
if (skipped > 0)
|
|
1916
|
-
|
|
1905
|
+
if (undone > 0) spinner8.succeed(`undid ${undone} import${undone !== 1 ? "s" : ""}`);
|
|
1906
|
+
if (skipped > 0) spinner8.info(`skipped ${skipped} item${skipped !== 1 ? "s" : ""} (TV or non-move mode)`);
|
|
1907
|
+
spinner8.stop();
|
|
1917
1908
|
};
|
|
1918
1909
|
var undo_default = undo;
|
|
1919
1910
|
|
|
@@ -1921,7 +1912,8 @@ var undo_default = undo;
|
|
|
1921
1912
|
import chokidar from "chokidar";
|
|
1922
1913
|
import { cpSync as cpSync2, existsSync as existsSync12, linkSync as linkSync2, lstatSync as lstatSync5, mkdirSync as mkdirSync5, readdirSync as readdirSync8, renameSync as renameSync6, rmSync as rmSync4, statSync as statSync3 } from "fs";
|
|
1923
1914
|
import { basename as basename4, dirname as dirname3, resolve as resolve9 } from "path";
|
|
1924
|
-
import { Color as Color11 } from "termkit";
|
|
1915
|
+
import { Color as Color11, Spinner as Spinner9 } from "termkit";
|
|
1916
|
+
var spinner9 = new Spinner9();
|
|
1925
1917
|
var sameDev2 = (a, b) => {
|
|
1926
1918
|
try {
|
|
1927
1919
|
let bExisting = b;
|
|
@@ -2061,7 +2053,7 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2061
2053
|
}
|
|
2062
2054
|
const destRoot = config.dest[detectedType];
|
|
2063
2055
|
if (!destRoot) {
|
|
2064
|
-
if (isVerbose())
|
|
2056
|
+
if (isVerbose()) spinner9.info(`no ${detectedType} destination configured, skipped: ${entry}`);
|
|
2065
2057
|
return;
|
|
2066
2058
|
}
|
|
2067
2059
|
if (detectedType === "ps3") {
|
|
@@ -2071,18 +2063,18 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2071
2063
|
const destName = `${nameMatch[0]} [${id}]`;
|
|
2072
2064
|
const destPath = resolve9(destRoot, destName);
|
|
2073
2065
|
if (existsSync12(destPath)) {
|
|
2074
|
-
|
|
2066
|
+
spinner9.warn(`already exists: ${destName}`);
|
|
2075
2067
|
return;
|
|
2076
2068
|
}
|
|
2077
2069
|
moveItem(entryPath, destPath);
|
|
2078
2070
|
recordImport(sessionId, entryPath, destPath, "move", void 0, "ps3");
|
|
2079
|
-
|
|
2071
|
+
spinner9.succeed(`imported ${Color11.green.encoder(destName)}`);
|
|
2080
2072
|
return;
|
|
2081
2073
|
}
|
|
2082
2074
|
if (detectedType === "book") {
|
|
2083
2075
|
const destPath = resolve9(destRoot, entry);
|
|
2084
2076
|
if (existsSync12(destPath)) {
|
|
2085
|
-
|
|
2077
|
+
spinner9.warn(`already exists: ${entry}`);
|
|
2086
2078
|
return;
|
|
2087
2079
|
}
|
|
2088
2080
|
if (isDir || isBookDir) {
|
|
@@ -2097,17 +2089,17 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2097
2089
|
}
|
|
2098
2090
|
}
|
|
2099
2091
|
recordImport(sessionId, entryPath, destPath, "move", void 0, "book");
|
|
2100
|
-
|
|
2092
|
+
spinner9.succeed(`imported ${Color11.green.encoder(entry)}`);
|
|
2101
2093
|
return;
|
|
2102
2094
|
}
|
|
2103
2095
|
const parsed = parseDownloadName(entry);
|
|
2104
2096
|
if (!parsed) {
|
|
2105
|
-
if (isVerbose())
|
|
2097
|
+
if (isVerbose()) spinner9.info(`could not parse: ${entry}`);
|
|
2106
2098
|
return;
|
|
2107
2099
|
}
|
|
2108
2100
|
if (detectedType === "tv") {
|
|
2109
2101
|
if (parsed.season === void 0) {
|
|
2110
|
-
if (isVerbose())
|
|
2102
|
+
if (isVerbose()) spinner9.info(`could not detect season from: ${entry}`);
|
|
2111
2103
|
return;
|
|
2112
2104
|
}
|
|
2113
2105
|
const registeredShow = getShowByTitle(parsed.title);
|
|
@@ -2121,14 +2113,14 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2121
2113
|
showPath = resolve9(destRoot, showFolderName);
|
|
2122
2114
|
upsertShow(showPath, null, parsed.title);
|
|
2123
2115
|
} else {
|
|
2124
|
-
if (isVerbose())
|
|
2116
|
+
if (isVerbose()) spinner9.info(`not registered, skipped: ${parsed.title} \u2014 run: reelsort add "${parsed.title}"`);
|
|
2125
2117
|
return;
|
|
2126
2118
|
}
|
|
2127
2119
|
const seasonFolderName = findSeasonFolder2(showPath, parsed.season) ?? formatSeasonFolder(seasonFormat, parsed.season);
|
|
2128
2120
|
const seasonPath = resolve9(showPath, seasonFolderName);
|
|
2129
2121
|
const videoFile2 = isDir ? findVideo2(entryPath) : entry;
|
|
2130
2122
|
if (!videoFile2) {
|
|
2131
|
-
if (isVerbose())
|
|
2123
|
+
if (isVerbose()) spinner9.info(`no video found in: ${entry}`);
|
|
2132
2124
|
return;
|
|
2133
2125
|
}
|
|
2134
2126
|
const videoExt2 = videoFile2.match(/([^.]+$)/)?.[0];
|
|
@@ -2138,7 +2130,7 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2138
2130
|
const destVideoPath = resolve9(seasonPath, destVideoName2);
|
|
2139
2131
|
const videoSourcePath2 = isDir ? resolve9(entryPath, videoFile2) : entryPath;
|
|
2140
2132
|
if (existsSync12(destVideoPath)) {
|
|
2141
|
-
|
|
2133
|
+
spinner9.warn(`already exists: ${episodeName}`);
|
|
2142
2134
|
return;
|
|
2143
2135
|
}
|
|
2144
2136
|
const dirFiles2 = isDir ? readdirSync8(entryPath) : [];
|
|
@@ -2154,7 +2146,7 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2154
2146
|
linkSync2(videoSourcePath2, destVideoPath);
|
|
2155
2147
|
mode = "hardlink";
|
|
2156
2148
|
} catch {
|
|
2157
|
-
|
|
2149
|
+
spinner9.warn(`hardlink unavailable \u2014 copying instead: ${episodeName}`);
|
|
2158
2150
|
cpSync2(videoSourcePath2, destVideoPath);
|
|
2159
2151
|
mode = "copy";
|
|
2160
2152
|
}
|
|
@@ -2170,19 +2162,19 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2170
2162
|
if (isDir) rmSync4(entryPath, { recursive: true, force: true });
|
|
2171
2163
|
}
|
|
2172
2164
|
recordImport(sessionId, entryPath, seasonPath, mode, void 0, "tv");
|
|
2173
|
-
|
|
2165
|
+
spinner9.succeed(`imported ${Color11.green.encoder(`${showFolderName} / ${seasonFolderName} / ${episodeName}`)}`);
|
|
2174
2166
|
return;
|
|
2175
2167
|
}
|
|
2176
2168
|
const edition = detectEdition(entry);
|
|
2177
2169
|
const folderName = formatMovieName(movieFormat, parsed.title, parsed.year, edition);
|
|
2178
2170
|
const destFolder = resolve9(destRoot, folderName);
|
|
2179
2171
|
if (existsSync12(destFolder)) {
|
|
2180
|
-
|
|
2172
|
+
spinner9.warn(`already exists: ${folderName}`);
|
|
2181
2173
|
return;
|
|
2182
2174
|
}
|
|
2183
2175
|
const videoFile = isDir ? findVideo2(entryPath) : entry;
|
|
2184
2176
|
if (!videoFile) {
|
|
2185
|
-
if (isVerbose())
|
|
2177
|
+
if (isVerbose()) spinner9.info(`no video found in: ${entry}`);
|
|
2186
2178
|
return;
|
|
2187
2179
|
}
|
|
2188
2180
|
const videoExt = videoFile.match(/([^.]+$)/)?.[0];
|
|
@@ -2202,7 +2194,7 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2202
2194
|
linkSync2(videoSourcePath, destVideoPath);
|
|
2203
2195
|
mode = "hardlink";
|
|
2204
2196
|
} catch {
|
|
2205
|
-
|
|
2197
|
+
spinner9.warn(`hardlink unavailable \u2014 copying instead: ${folderName}`);
|
|
2206
2198
|
cpSync2(videoSourcePath, destVideoPath);
|
|
2207
2199
|
mode = "copy";
|
|
2208
2200
|
}
|
|
@@ -2227,7 +2219,7 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2227
2219
|
}
|
|
2228
2220
|
recordImport(sessionId, entryPath, destFolder, "move", void 0, "movie");
|
|
2229
2221
|
}
|
|
2230
|
-
|
|
2222
|
+
spinner9.succeed(`imported ${Color11.green.encoder(folderName)}`);
|
|
2231
2223
|
};
|
|
2232
2224
|
var watch = async ({ hardlink = false, auto = false }) => {
|
|
2233
2225
|
const config = getConfig();
|
|
@@ -2246,7 +2238,7 @@ var watch = async ({ hardlink = false, auto = false }) => {
|
|
|
2246
2238
|
await processItem(entry, hardlink, language, auto);
|
|
2247
2239
|
}
|
|
2248
2240
|
} catch (err) {
|
|
2249
|
-
|
|
2241
|
+
spinner9.fail(`error processing ${path}: ${err.message}`);
|
|
2250
2242
|
}
|
|
2251
2243
|
}, 5e3)
|
|
2252
2244
|
);
|
|
@@ -2258,10 +2250,10 @@ var watch = async ({ hardlink = false, auto = false }) => {
|
|
|
2258
2250
|
});
|
|
2259
2251
|
watcher.on("addDir", handle);
|
|
2260
2252
|
watcher.on("add", handle);
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
for (const s of config.sources)
|
|
2264
|
-
|
|
2253
|
+
spinner9.start();
|
|
2254
|
+
spinner9.succeed(`watching ${config.sources.length} source${config.sources.length !== 1 ? "s" : ""}`);
|
|
2255
|
+
for (const s of config.sources) spinner9.info(` ${Color11.white.encoder(s)}`);
|
|
2256
|
+
spinner9.stop();
|
|
2265
2257
|
process.stdin.resume();
|
|
2266
2258
|
};
|
|
2267
2259
|
var watch_default = watch;
|