reelsort 0.2.0 → 0.2.1

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/index.js CHANGED
@@ -280,17 +280,17 @@ var clean = async ({ dryRun, olderThan }) => {
280
280
  continue;
281
281
  }
282
282
  if (dryRun) {
283
- spinner_default.succeed(`[dry] would remove ${import_termkit2.Color.blue.encoder(imp.sourcePath)}`);
283
+ spinner_default.succeed(`[dry] would remove ${import_termkit2.Color.white.encoder(imp.sourcePath)}`);
284
284
  cleaned++;
285
285
  continue;
286
286
  }
287
287
  try {
288
288
  (0, import_fs2.rmSync)(imp.sourcePath, { recursive: true, force: true });
289
289
  deleteImport(imp.id);
290
- spinner_default.succeed(`removed ${import_termkit2.Color.blue.encoder(imp.sourcePath)}`);
290
+ spinner_default.succeed(`removed ${import_termkit2.Color.white.encoder(imp.sourcePath)}`);
291
291
  cleaned++;
292
292
  } catch {
293
- spinner_default.warn(`locked or inaccessible, skipped: ${import_termkit2.Color.blue.encoder(imp.sourcePath)}`);
293
+ spinner_default.warn(`locked or inaccessible, skipped: ${import_termkit2.Color.white.encoder(imp.sourcePath)}`);
294
294
  skipped++;
295
295
  }
296
296
  }
@@ -349,20 +349,20 @@ var formatMovieName = (template, title, year, edition) => {
349
349
  };
350
350
 
351
351
  // src/actions/config.ts
352
- var DEST_TYPES = ["movie", "tv", "ps3"];
352
+ var DEST_TYPES = ["movie", "tv", "ps3", "book"];
353
353
  var sourceAdd = async ({ dir }) => {
354
354
  const resolved = (0, import_path3.resolve)(dir);
355
355
  const config = getConfig();
356
356
  if (config.sources.includes(resolved)) {
357
357
  spinner_default.start();
358
- spinner_default.info(`source already configured: ${import_termkit3.Color.blue.encoder(resolved)}`);
358
+ spinner_default.info(`source already configured: ${import_termkit3.Color.white.encoder(resolved)}`);
359
359
  spinner_default.stop();
360
360
  return;
361
361
  }
362
362
  config.sources.push(resolved);
363
363
  saveConfig(config);
364
364
  spinner_default.start();
365
- spinner_default.succeed(`added source: ${import_termkit3.Color.blue.encoder(resolved)}`);
365
+ spinner_default.succeed(`added source: ${import_termkit3.Color.white.encoder(resolved)}`);
366
366
  spinner_default.stop();
367
367
  };
368
368
  var sourceRemove = async ({ dir }) => {
@@ -371,14 +371,14 @@ var sourceRemove = async ({ dir }) => {
371
371
  const index = config.sources.indexOf(resolved);
372
372
  if (index === -1) {
373
373
  spinner_default.start();
374
- spinner_default.warn(`source not found: ${import_termkit3.Color.blue.encoder(resolved)}`);
374
+ spinner_default.warn(`source not found: ${import_termkit3.Color.white.encoder(resolved)}`);
375
375
  spinner_default.stop();
376
376
  return;
377
377
  }
378
378
  config.sources.splice(index, 1);
379
379
  saveConfig(config);
380
380
  spinner_default.start();
381
- spinner_default.succeed(`removed source: ${import_termkit3.Color.blue.encoder(resolved)}`);
381
+ spinner_default.succeed(`removed source: ${import_termkit3.Color.white.encoder(resolved)}`);
382
382
  spinner_default.stop();
383
383
  };
384
384
  var destAdd = async ({ type, dir }) => {
@@ -390,7 +390,7 @@ var destAdd = async ({ type, dir }) => {
390
390
  config.dest[type] = resolved;
391
391
  saveConfig(config);
392
392
  spinner_default.start();
393
- spinner_default.succeed(`set ${type} destination: ${import_termkit3.Color.cyan.encoder(resolved)}`);
393
+ spinner_default.succeed(`set ${type} destination: ${import_termkit3.Color.green.encoder(resolved)}`);
394
394
  spinner_default.stop();
395
395
  };
396
396
  var destRemove = async ({ type }) => {
@@ -416,7 +416,7 @@ var configSet = async ({ key, subkey, value }) => {
416
416
  config.language = subkey;
417
417
  saveConfig(config);
418
418
  spinner_default.start();
419
- spinner_default.succeed(`set subtitle language: ${import_termkit3.Color.cyan.encoder(subkey)}`);
419
+ spinner_default.succeed(`set subtitle language: ${import_termkit3.Color.green.encoder(subkey)}`);
420
420
  spinner_default.stop();
421
421
  return;
422
422
  }
@@ -446,7 +446,7 @@ var configSet = async ({ key, subkey, value }) => {
446
446
  }
447
447
  saveConfig(config);
448
448
  spinner_default.start();
449
- spinner_default.succeed(`set ${subkey} format: ${import_termkit3.Color.cyan.encoder(value ?? subkey)}`);
449
+ spinner_default.succeed(`set ${subkey} format: ${import_termkit3.Color.green.encoder(value ?? subkey)}`);
450
450
  spinner_default.stop();
451
451
  return;
452
452
  }
@@ -458,7 +458,7 @@ var configShow = async () => {
458
458
  if (config.sources.length === 0) {
459
459
  console.log(" (none)");
460
460
  } else {
461
- for (const s of config.sources) console.log(` ${import_termkit3.Color.blue.encoder(s)}`);
461
+ for (const s of config.sources) console.log(` ${import_termkit3.Color.white.encoder(s)}`);
462
462
  }
463
463
  console.log("\nDestinations:");
464
464
  const entries = DEST_TYPES.map((t) => ({ type: t, path: config.dest[t] })).filter((e) => e.path);
@@ -466,15 +466,15 @@ var configShow = async () => {
466
466
  console.log(" (none)");
467
467
  } else {
468
468
  for (const { type, path } of entries) {
469
- console.log(` ${type.padEnd(6)} ${import_termkit3.Color.cyan.encoder(path)}`);
469
+ console.log(` ${type.padEnd(6)} ${import_termkit3.Color.green.encoder(path)}`);
470
470
  }
471
471
  }
472
472
  console.log(`
473
- Subtitle language: ${import_termkit3.Color.cyan.encoder(config.language ?? "eng (default)")}`);
473
+ Subtitle language: ${import_termkit3.Color.green.encoder(config.language ?? "eng (default)")}`);
474
474
  console.log(`TMDb API key: ${config.tmdbApiKey ? import_termkit3.Color.green.encoder("configured") : import_termkit3.Color.red.encoder("not set")}`);
475
- console.log(`Movie format: ${import_termkit3.Color.cyan.encoder(config.format?.movie ?? DEFAULT_MOVIE_FORMAT + " (default)")}`);
476
- console.log(`Episode format: ${import_termkit3.Color.cyan.encoder(config.format?.episode ?? DEFAULT_EPISODE_FORMAT + " (default)")}`);
477
- console.log(`Season folder: ${import_termkit3.Color.cyan.encoder(config.format?.season ?? DEFAULT_SEASON_FORMAT + " (default)")}`);
475
+ console.log(`Movie format: ${import_termkit3.Color.green.encoder(config.format?.movie ?? DEFAULT_MOVIE_FORMAT + " (default)")}`);
476
+ console.log(`Episode format: ${import_termkit3.Color.green.encoder(config.format?.episode ?? DEFAULT_EPISODE_FORMAT + " (default)")}`);
477
+ console.log(`Season folder: ${import_termkit3.Color.green.encoder(config.format?.season ?? DEFAULT_SEASON_FORMAT + " (default)")}`);
478
478
  console.log();
479
479
  };
480
480
 
@@ -485,7 +485,7 @@ var import_termkit4 = require("termkit");
485
485
  var differences = async ({ dir1: rawDir1, dir2: rawDir2, only, ignore }) => {
486
486
  let dir1 = rawDir1;
487
487
  let dir2 = rawDir2;
488
- spinner_default.text = `checking differences between ${import_termkit4.Color.blue.encoder(dir1)} and ${import_termkit4.Color.blue.encoder(dir2)}`;
488
+ spinner_default.text = `checking differences between ${import_termkit4.Color.white.encoder(dir1)} and ${import_termkit4.Color.white.encoder(dir2)}`;
489
489
  spinner_default.start();
490
490
  dir1 = (0, import_path4.resolve)(dir1);
491
491
  dir2 = (0, import_path4.resolve)(dir2);
@@ -521,7 +521,7 @@ var differences = async ({ dir1: rawDir1, dir2: rawDir2, only, ignore }) => {
521
521
  removed.push(l);
522
522
  }
523
523
  }
524
- spinner_default.succeed(`checked differences between ${import_termkit4.Color.blue.encoder(dir1)} and ${import_termkit4.Color.blue.encoder(dir2)}`);
524
+ spinner_default.succeed(`checked differences between ${import_termkit4.Color.white.encoder(dir1)} and ${import_termkit4.Color.white.encoder(dir2)}`);
525
525
  spinner_default.succeed(`found ${added.length} added files`);
526
526
  spinner_default.succeed(`found ${removed.length} removed files`);
527
527
  spinner_default.stop();
@@ -547,8 +547,8 @@ var history = async ({ limit, imports }) => {
547
547
  ${import_termkit5.Color.yellow.encoder(label)} (${session.records.length} item${session.records.length !== 1 ? "s" : ""})`);
548
548
  for (const r of session.records) {
549
549
  const src = (0, import_path5.basename)(r.sourcePath);
550
- const dest = import_termkit5.Color.cyan.encoder(r.destinationPath);
551
- const mode = r.mode !== "move" ? ` ${import_termkit5.Color.blue.encoder(`[${r.mode}]`)}` : "";
550
+ const dest = import_termkit5.Color.green.encoder(r.destinationPath);
551
+ const mode = r.mode !== "move" ? ` ${import_termkit5.Color.white.encoder(`[${r.mode}]`)}` : "";
552
552
  console.log(` ${src} \u2192 ${dest}${mode}`);
553
553
  }
554
554
  }
@@ -567,7 +567,7 @@ ${import_termkit5.Color.yellow.encoder(label)} (${folders.length} item${folders
567
567
  for (const r of folders) {
568
568
  const oldName = (0, import_path5.basename)(r.oldPath);
569
569
  const newName = (0, import_path5.basename)(r.newPath);
570
- console.log(` ${import_termkit5.Color.blue.encoder(oldName)} \u2192 ${import_termkit5.Color.cyan.encoder(newName)}`);
570
+ console.log(` ${import_termkit5.Color.white.encoder(oldName)} \u2192 ${import_termkit5.Color.green.encoder(newName)}`);
571
571
  }
572
572
  }
573
573
  }
@@ -718,7 +718,7 @@ var list = async ({ type, missingSubs, codec: codecFilter, resolution: resFilter
718
718
  const destRoot = config.dest[t];
719
719
  if (!(0, import_fs6.existsSync)(destRoot)) {
720
720
  console.log(`
721
- ${t.toUpperCase()} ${import_termkit6.Color.blue.encoder(destRoot)} (not found)`);
721
+ ${t.toUpperCase()} ${import_termkit6.Color.white.encoder(destRoot)} (not found)`);
722
722
  continue;
723
723
  }
724
724
  const folders = (0, import_fs6.readdirSync)(destRoot).filter((f) => {
@@ -749,7 +749,7 @@ ${t.toUpperCase()} ${import_termkit6.Color.blue.encoder(destRoot)} (not found)
749
749
  return yearDiff !== 0 ? yearDiff : a.title.localeCompare(b.title);
750
750
  });
751
751
  console.log(`
752
- ${import_termkit6.Color.yellow.encoder(t.toUpperCase())} ${import_termkit6.Color.blue.encoder(destRoot)}`);
752
+ ${import_termkit6.Color.yellow.encoder(t.toUpperCase())} ${import_termkit6.Color.white.encoder(destRoot)}`);
753
753
  new import_termkit6.Table(
754
754
  filtered.map((e) => ({
755
755
  title: e.title,
@@ -857,7 +857,7 @@ var probe = async ({ type, force, verbose }) => {
857
857
  for (const t of types) {
858
858
  const destRoot = config.dest[t];
859
859
  if (!(0, import_fs7.existsSync)(destRoot)) continue;
860
- spinner_default.text = `scanning ${import_termkit7.Color.blue.encoder(destRoot)}`;
860
+ spinner_default.text = `scanning ${import_termkit7.Color.white.encoder(destRoot)}`;
861
861
  const files = walkVideoFiles(destRoot);
862
862
  for (const filePath of files) {
863
863
  if (!force && getMediaInfo(filePath)) {
@@ -865,7 +865,7 @@ var probe = async ({ type, force, verbose }) => {
865
865
  skipped++;
866
866
  continue;
867
867
  }
868
- spinner_default.text = `probing ${import_termkit7.Color.blue.encoder(filePath)}`;
868
+ spinner_default.text = `probing ${import_termkit7.Color.white.encoder(filePath)}`;
869
869
  const result = runFfprobe(filePath);
870
870
  if (!result) {
871
871
  if (verbose) spinner_default.warn(`ffprobe failed: ${filePath}`);
@@ -949,13 +949,13 @@ var rename = async ({ dir: inputDir, type, verbose }) => {
949
949
  const config = getConfig();
950
950
  const language = config.language ?? "eng";
951
951
  const movieFormat = config.format?.movie ?? DEFAULT_MOVIE_FORMAT;
952
- spinner_default.text = `renaming in ${import_termkit8.Color.blue.encoder(dir)}`;
952
+ spinner_default.text = `renaming in ${import_termkit8.Color.white.encoder(dir)}`;
953
953
  spinner_default.start();
954
954
  if (!(0, import_fs8.existsSync)(dir)) throw new Error(`dir ${dir} does not exist`);
955
955
  const list2 = (0, import_fs8.readdirSync)(dir);
956
956
  let renamed = 0, removed = 0, skipped = 0;
957
957
  for (const [index, entry] of list2.entries()) {
958
- spinner_default.text = `renaming in ${import_termkit8.Color.blue.encoder(dir)} ${index + 1}/${list2.length}`;
958
+ spinner_default.text = `renaming in ${import_termkit8.Color.white.encoder(dir)} ${index + 1}/${list2.length}`;
959
959
  if (!(0, import_fs8.lstatSync)((0, import_path9.resolve)(dir, entry)).isDirectory()) {
960
960
  if (verbose) spinner_default.info(`skipped ${entry}`);
961
961
  skipped++;
@@ -1040,7 +1040,7 @@ var rename = async ({ dir: inputDir, type, verbose }) => {
1040
1040
  spinner_default.succeed(`renamed ${renamed} files`);
1041
1041
  if (removed) spinner_default.info(`removed ${removed} files`);
1042
1042
  spinner_default.info(`skipped ${skipped} files`);
1043
- spinner_default.succeed(`done in ${import_termkit8.Color.cyan.encoder(dir)}`);
1043
+ spinner_default.succeed(`done in ${import_termkit8.Color.green.encoder(dir)}`);
1044
1044
  spinner_default.stop();
1045
1045
  };
1046
1046
  var rename_default = rename;
@@ -1051,7 +1051,7 @@ var import_path10 = require("path");
1051
1051
  var import_termkit9 = require("termkit");
1052
1052
  var reset = async ({ dir: inputDir, double }) => {
1053
1053
  let dir = inputDir;
1054
- spinner_default.text = `resetting episodes in ${import_termkit9.Color.blue.encoder(dir)}`;
1054
+ spinner_default.text = `resetting episodes in ${import_termkit9.Color.white.encoder(dir)}`;
1055
1055
  spinner_default.start();
1056
1056
  dir = (0, import_path10.resolve)(dir);
1057
1057
  if (!(0, import_fs9.existsSync)(dir)) throw new Error(`dir ${dir} does not exist`);
@@ -1080,7 +1080,7 @@ var reset = async ({ dir: inputDir, double }) => {
1080
1080
  const episodeFormat = getConfig().format?.episode;
1081
1081
  let renamed = 0, skipped = other.length;
1082
1082
  for (const [index, i] of sublist.entries()) {
1083
- spinner_default.text = `resetting episodes in ${import_termkit9.Color.blue.encoder(dir)} ${index}/${list2.length}`;
1083
+ spinner_default.text = `resetting episodes in ${import_termkit9.Color.white.encoder(dir)} ${index}/${list2.length}`;
1084
1084
  const ext = i.match(/([^.]+$)/)?.[0];
1085
1085
  const episode = double ? index * 2 + 1 : index + 1;
1086
1086
  const name = `${formatEpisode(seasonNum, episode, episodeFormat, double, showTitle)}.${ext}`;
@@ -1093,7 +1093,7 @@ var reset = async ({ dir: inputDir, double }) => {
1093
1093
  }
1094
1094
  spinner_default.succeed(`renamed ${renamed} files`);
1095
1095
  spinner_default.info(`skipped ${skipped} files`);
1096
- spinner_default.succeed(`done in ${import_termkit9.Color.cyan.encoder(dir)}`);
1096
+ spinner_default.succeed(`done in ${import_termkit9.Color.green.encoder(dir)}`);
1097
1097
  spinner_default.stop();
1098
1098
  };
1099
1099
  var reset_default = reset;
@@ -1174,18 +1174,17 @@ var searchMovie = async (title, year, apiKey) => {
1174
1174
  if (year) url.searchParams.set("year", String(year));
1175
1175
  try {
1176
1176
  const res = await fetch(url.toString());
1177
- if (!res.ok) return null;
1177
+ if (!res.ok) return [];
1178
1178
  const data = await res.json();
1179
- const first = data.results[0];
1180
- if (!first) return null;
1181
- return {
1182
- id: first.id,
1183
- title: first.title,
1184
- year: first.release_date ? parseInt(first.release_date.slice(0, 4)) : void 0,
1185
- url: `${TMDB_WEB}/movie/${first.id}`
1186
- };
1179
+ return data.results.slice(0, 5).map((r) => ({
1180
+ id: r.id,
1181
+ title: r.title,
1182
+ year: r.release_date ? parseInt(r.release_date.slice(0, 4)) : void 0,
1183
+ overview: r.overview || void 0,
1184
+ url: `${TMDB_WEB}/movie/${r.id}`
1185
+ }));
1187
1186
  } catch {
1188
- return null;
1187
+ return [];
1189
1188
  }
1190
1189
  };
1191
1190
  var getEpisodeName = async (seriesId, season, episode, apiKey) => {
@@ -1220,6 +1219,9 @@ var searchTv = async (title, apiKey) => {
1220
1219
  }
1221
1220
  };
1222
1221
 
1222
+ // src/refs/bookExtensions.json
1223
+ var bookExtensions_default = ["epub", "mobi", "azw3", "azw"];
1224
+
1223
1225
  // src/actions/scan.ts
1224
1226
  var sameDev = (a, b) => {
1225
1227
  try {
@@ -1242,6 +1244,10 @@ var findVideo = (dir) => (0, import_fs10.readdirSync)(dir).find((f) => {
1242
1244
  const ext = f.match(/([^.]+$)/)?.[0];
1243
1245
  return ext && videoExtensions_default.includes(ext);
1244
1246
  }) ?? null;
1247
+ var containsBook = (dir) => (0, import_fs10.readdirSync)(dir).some((f) => {
1248
+ const ext = f.match(/([^.]+$)/)?.[0];
1249
+ return ext && bookExtensions_default.includes(ext);
1250
+ });
1245
1251
  var findSeasonFolder = (showPath, season) => {
1246
1252
  if (!(0, import_fs10.existsSync)(showPath)) return null;
1247
1253
  const folders = (0, import_fs10.readdirSync)(showPath).filter((f) => {
@@ -1256,27 +1262,128 @@ var findSeasonFolder = (showPath, season) => {
1256
1262
  return match && parseInt(match[1]) === season;
1257
1263
  }) ?? null;
1258
1264
  };
1259
- var scan = async ({ type, hardlink: useHardlink, dryRun, verbose, auto }) => {
1265
+ var classifyMovieConfidence = (entry) => {
1266
+ if (/^\[(?:Team|Group)\s/i.test(entry)) return "skip";
1267
+ if (/\bepisodes?\s+\d+[-–]\d+/i.test(entry)) return "skip";
1268
+ if (/\b(?:patch|keygen|crack)\b|\bkeys?\s*\{/i.test(entry)) return "skip";
1269
+ if (/\[YTS[.\-]/i.test(entry)) return "auto";
1270
+ if (/\(\d{4}\)/.test(entry) && /\[(?:2160p|1080p|720p|480p|576p|BluRay|BDRip|BDRemux|WEBRip|WEB-DL|HDRip|DVDRip|HDTV)/i.test(entry)) return "auto";
1271
+ if (/\.\d{4}\..*?(?:2160p|1080p|720p|BluRay|WEBRip|WEB-DL|HDTV)/i.test(entry)) return "auto";
1272
+ return "ambiguous";
1273
+ };
1274
+ var scan = async ({ type, hardlink: useHardlink, dryRun, verbose, auto, force, interactive }) => {
1260
1275
  const config = getConfig();
1261
1276
  const sessionId = (/* @__PURE__ */ new Date()).toISOString();
1262
1277
  const language = config.language ?? "eng";
1263
1278
  const movieFormat = config.format?.movie ?? DEFAULT_MOVIE_FORMAT;
1264
1279
  const seasonFormat = config.format?.season ?? DEFAULT_SEASON_FORMAT;
1280
+ const lookupMovie = async (parsed) => {
1281
+ let tmdbId;
1282
+ let resolvedTitle = parsed.title;
1283
+ let resolvedYear = parsed.year;
1284
+ if (config.tmdbApiKey) {
1285
+ const results = await searchMovie(parsed.title, parsed.year, config.tmdbApiKey);
1286
+ if (results.length === 1) {
1287
+ tmdbId = results[0].id;
1288
+ resolvedTitle = results[0].title;
1289
+ resolvedYear = results[0].year ?? parsed.year;
1290
+ } else if (results.length > 1) {
1291
+ spinner_default.stop();
1292
+ const select = new import_termkit10.Select();
1293
+ const items = results.map((r) => ({
1294
+ label: r.year ? `${r.title} (${r.year})` : r.title,
1295
+ description: [r.overview?.slice(0, 60), hyperlink(r.url, "themoviedb.org")].filter(Boolean).join(" \xB7 "),
1296
+ ...r
1297
+ }));
1298
+ const picked = await select.ask(`Multiple movies found for "${parsed.title}":`, items);
1299
+ spinner_default.start();
1300
+ if (picked) {
1301
+ tmdbId = picked.id;
1302
+ resolvedTitle = picked.title;
1303
+ resolvedYear = picked.year ?? parsed.year;
1304
+ }
1305
+ }
1306
+ }
1307
+ return { tmdbId, resolvedTitle, resolvedYear };
1308
+ };
1309
+ const importMovie = async (entry, entryPath, isDir, resolvedTitle, resolvedYear, tmdbId, destRoot) => {
1310
+ const edition = detectEdition(entry);
1311
+ const folderName = formatMovieName(movieFormat, resolvedTitle, resolvedYear, edition);
1312
+ const destFolder = (0, import_path11.resolve)(destRoot, folderName);
1313
+ if ((0, import_fs10.existsSync)(destFolder)) {
1314
+ spinner_default.warn(`already exists: ${folderName}`);
1315
+ return false;
1316
+ }
1317
+ const videoFile = isDir ? findVideo(entryPath) : entry;
1318
+ if (!videoFile) {
1319
+ if (verbose) spinner_default.info(`no video found in: ${entry}`);
1320
+ return false;
1321
+ }
1322
+ const videoExt = videoFile.match(/([^.]+$)/)?.[0];
1323
+ const destVideoName = `${folderName}.${videoExt}`;
1324
+ const videoSourcePath = isDir ? (0, import_path11.resolve)(entryPath, videoFile) : entryPath;
1325
+ const dirFiles = isDir ? (0, import_fs10.readdirSync)(entryPath) : [];
1326
+ const subtitle = isDir ? findSubtitle(dirFiles, language) : null;
1327
+ const subtitleExt = subtitle?.match(/([^.]+$)/)?.[0];
1328
+ const subtitleSourcePath = subtitle ? (0, import_path11.resolve)(entryPath, subtitle) : null;
1329
+ const destSubtitleName = subtitle && subtitleExt ? `${folderName}.${subtitleExt}` : null;
1330
+ if (!dryRun) {
1331
+ if (useHardlink) {
1332
+ (0, import_fs10.mkdirSync)(destFolder, { recursive: true });
1333
+ const destVideoPath = (0, import_path11.resolve)(destFolder, destVideoName);
1334
+ let mode;
1335
+ try {
1336
+ if (!sameDev(videoSourcePath, destRoot)) throw new Error("cross-filesystem");
1337
+ (0, import_fs10.linkSync)(videoSourcePath, destVideoPath);
1338
+ mode = "hardlink";
1339
+ } catch {
1340
+ spinner_default.warn(`hardlink unavailable \u2014 copying instead: ${folderName}`);
1341
+ (0, import_fs10.cpSync)(videoSourcePath, destVideoPath);
1342
+ mode = "copy";
1343
+ }
1344
+ if (subtitleSourcePath && destSubtitleName) (0, import_fs10.cpSync)(subtitleSourcePath, (0, import_path11.resolve)(destFolder, destSubtitleName));
1345
+ recordImport(sessionId, entryPath, destFolder, mode, tmdbId);
1346
+ } else {
1347
+ if (isDir) {
1348
+ const keep = new Set([videoFile, subtitle].filter(Boolean));
1349
+ for (const f of dirFiles.filter((f2) => !keep.has(f2))) (0, import_fs10.rmSync)((0, import_path11.resolve)(entryPath, f), { recursive: true, force: true });
1350
+ (0, import_fs10.renameSync)(videoSourcePath, (0, import_path11.resolve)(entryPath, destVideoName));
1351
+ if (subtitleSourcePath && destSubtitleName) (0, import_fs10.renameSync)(subtitleSourcePath, (0, import_path11.resolve)(entryPath, destSubtitleName));
1352
+ moveFolder(entryPath, destFolder);
1353
+ } else {
1354
+ (0, import_fs10.mkdirSync)(destFolder, { recursive: true });
1355
+ const destVideoPath = (0, import_path11.resolve)(destFolder, destVideoName);
1356
+ if (sameDev(videoSourcePath, destRoot)) {
1357
+ (0, import_fs10.renameSync)(videoSourcePath, destVideoPath);
1358
+ } else {
1359
+ (0, import_fs10.cpSync)(videoSourcePath, destVideoPath);
1360
+ (0, import_fs10.rmSync)(videoSourcePath);
1361
+ }
1362
+ }
1363
+ recordImport(sessionId, entryPath, destFolder, "move", tmdbId);
1364
+ }
1365
+ }
1366
+ spinner_default.succeed(`${dryRun ? "[dry] " : ""}${folderName}`);
1367
+ return true;
1368
+ };
1265
1369
  spinner_default.start();
1266
1370
  if (config.sources.length === 0) throw new Error("no sources configured \u2014 run: reelsort config add source <dir>");
1267
1371
  let imported = 0, skipped = 0;
1372
+ const pendingMovies = [];
1268
1373
  for (const source of config.sources) {
1269
1374
  if (!(0, import_fs10.existsSync)(source)) {
1270
- spinner_default.warn(`source not found: ${import_termkit10.Color.blue.encoder(source)}`);
1375
+ spinner_default.warn(`source not found: ${import_termkit10.Color.white.encoder(source)}`);
1271
1376
  continue;
1272
1377
  }
1273
- spinner_default.text = `scanning ${import_termkit10.Color.blue.encoder(source)}`;
1378
+ spinner_default.text = `scanning ${import_termkit10.Color.white.encoder(source)}`;
1274
1379
  for (const entry of (0, import_fs10.readdirSync)(source)) {
1275
1380
  const entryPath = (0, import_path11.resolve)(source, entry);
1276
1381
  const isDir = (0, import_fs10.lstatSync)(entryPath).isDirectory();
1277
1382
  const ext = entry.match(/([^.]+$)/)?.[0];
1278
1383
  const isVideo = !isDir && ext && videoExtensions_default.includes(ext);
1279
- if (!isDir && !isVideo) {
1384
+ const isBook = !isDir && ext && bookExtensions_default.includes(ext);
1385
+ const isBookDir = isDir && containsBook(entryPath);
1386
+ if (!isDir && !isVideo && !isBook) {
1280
1387
  if (verbose) spinner_default.info(`skipped ${entry}`);
1281
1388
  skipped++;
1282
1389
  continue;
@@ -1284,6 +1391,8 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, verbose, auto }) => {
1284
1391
  let detectedType;
1285
1392
  if (type) {
1286
1393
  detectedType = type;
1394
+ } else if (isBook || isBookDir) {
1395
+ detectedType = "book";
1287
1396
  } else if (isDir && /(?<=\[).+?(?=\])/.test(entry)) {
1288
1397
  detectedType = "ps3";
1289
1398
  } else if (/S\d{2,3}E\d{2,3}/i.test(entry) || /\d+x\d{2,3}/i.test(entry)) {
@@ -1319,12 +1428,49 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, verbose, auto }) => {
1319
1428
  imported++;
1320
1429
  continue;
1321
1430
  }
1431
+ if (detectedType === "book") {
1432
+ const destPath = (0, import_path11.resolve)(destRoot, entry);
1433
+ if ((0, import_fs10.existsSync)(destPath)) {
1434
+ spinner_default.warn(`already exists: ${entry}`);
1435
+ skipped++;
1436
+ continue;
1437
+ }
1438
+ if (!dryRun) {
1439
+ if (isDir || isBookDir) {
1440
+ moveFolder(entryPath, destPath);
1441
+ } else {
1442
+ (0, import_fs10.mkdirSync)(destRoot, { recursive: true });
1443
+ if (sameDev(entryPath, destRoot)) {
1444
+ (0, import_fs10.renameSync)(entryPath, destPath);
1445
+ } else {
1446
+ (0, import_fs10.cpSync)(entryPath, destPath);
1447
+ (0, import_fs10.rmSync)(entryPath);
1448
+ }
1449
+ }
1450
+ recordImport(sessionId, entryPath, destPath, "move");
1451
+ }
1452
+ spinner_default.succeed(`${dryRun ? "[dry] " : ""}${entry}`);
1453
+ imported++;
1454
+ continue;
1455
+ }
1322
1456
  const parsed = parseDownloadName(entry);
1323
1457
  if (!parsed) {
1324
1458
  if (verbose) spinner_default.info(`could not parse: ${entry}`);
1325
1459
  skipped++;
1326
1460
  continue;
1327
1461
  }
1462
+ if (detectedType === "movie") {
1463
+ const confidence = classifyMovieConfidence(entry);
1464
+ if (confidence === "skip") {
1465
+ if (verbose) spinner_default.info(`skipped (uncertain): ${entry}`);
1466
+ skipped++;
1467
+ continue;
1468
+ }
1469
+ if (confidence === "ambiguous") {
1470
+ pendingMovies.push({ entry, entryPath, isDir, parsed, destRoot });
1471
+ continue;
1472
+ }
1473
+ }
1328
1474
  let tmdbId;
1329
1475
  let resolvedTitle = parsed.title;
1330
1476
  let resolvedYear = parsed.year;
@@ -1352,12 +1498,10 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, verbose, auto }) => {
1352
1498
  }
1353
1499
  }
1354
1500
  } else {
1355
- const tmdb = await searchMovie(parsed.title, parsed.year, config.tmdbApiKey);
1356
- if (tmdb) {
1357
- tmdbId = tmdb.id;
1358
- resolvedTitle = tmdb.title;
1359
- resolvedYear = tmdb.year ?? parsed.year;
1360
- }
1501
+ const result = await lookupMovie(parsed);
1502
+ tmdbId = result.tmdbId;
1503
+ resolvedTitle = result.resolvedTitle;
1504
+ resolvedYear = result.resolvedYear;
1361
1505
  }
1362
1506
  }
1363
1507
  if (detectedType === "tv") {
@@ -1383,50 +1527,68 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, verbose, auto }) => {
1383
1527
  }
1384
1528
  const seasonFolderName = findSeasonFolder(showPath, parsed.season) ?? formatSeasonFolder(seasonFormat, parsed.season);
1385
1529
  const seasonPath = (0, import_path11.resolve)(showPath, seasonFolderName);
1386
- const videoFile2 = isDir ? findVideo(entryPath) : entry;
1387
- if (!videoFile2) {
1530
+ const videoFile = isDir ? findVideo(entryPath) : entry;
1531
+ if (!videoFile) {
1388
1532
  if (verbose) spinner_default.info(`no video found in: ${entry}`);
1389
1533
  skipped++;
1390
1534
  continue;
1391
1535
  }
1392
- const videoExt2 = videoFile2.match(/([^.]+$)/)?.[0];
1536
+ const videoExt = videoFile.match(/([^.]+$)/)?.[0];
1393
1537
  const tmdbEpisodeName = tmdbId && config.tmdbApiKey ? await getEpisodeName(tmdbId, parsed.season, parsed.episode ?? 1, config.tmdbApiKey) : null;
1394
1538
  const episodeName = formatEpisode(parsed.season, parsed.episode ?? 1, config.format?.episode, false, resolvedTitle, tmdbEpisodeName ?? void 0);
1395
- const destVideoName2 = `${episodeName}.${videoExt2}`;
1396
- const destVideoPath = (0, import_path11.resolve)(seasonPath, destVideoName2);
1397
- const videoSourcePath2 = isDir ? (0, import_path11.resolve)(entryPath, videoFile2) : entryPath;
1539
+ const destVideoName = `${episodeName}.${videoExt}`;
1540
+ const destVideoPath = (0, import_path11.resolve)(seasonPath, destVideoName);
1541
+ const videoSourcePath = isDir ? (0, import_path11.resolve)(entryPath, videoFile) : entryPath;
1398
1542
  if ((0, import_fs10.existsSync)(destVideoPath)) {
1399
- spinner_default.warn(`already exists: ${episodeName}`);
1400
- skipped++;
1401
- continue;
1543
+ let shouldReplace = force;
1544
+ if (!shouldReplace && interactive) {
1545
+ spinner_default.stop();
1546
+ const select = new import_termkit10.Select();
1547
+ const picked = await select.ask(`Already exists \u2014 replace?`, [
1548
+ { label: `${showFolderName} / ${seasonFolderName} / ${episodeName}`, value: "replace" },
1549
+ { label: "Skip", value: "skip" }
1550
+ ]);
1551
+ spinner_default.start();
1552
+ shouldReplace = picked?.value === "replace";
1553
+ }
1554
+ if (!shouldReplace) {
1555
+ spinner_default.warn(`already exists: ${episodeName}`);
1556
+ skipped++;
1557
+ continue;
1558
+ }
1559
+ if (!dryRun) {
1560
+ for (const f of (0, import_fs10.readdirSync)(seasonPath)) {
1561
+ if (f.startsWith(`${episodeName}.`)) (0, import_fs10.rmSync)((0, import_path11.resolve)(seasonPath, f));
1562
+ }
1563
+ }
1402
1564
  }
1403
- const dirFiles2 = isDir ? (0, import_fs10.readdirSync)(entryPath) : [];
1404
- const subtitle2 = isDir ? findSubtitle(dirFiles2, language) : null;
1405
- const subtitleExt2 = subtitle2?.match(/([^.]+$)/)?.[0];
1406
- const subtitleSourcePath2 = subtitle2 ? (0, import_path11.resolve)(entryPath, subtitle2) : null;
1407
- const destSubtitleName2 = subtitle2 && subtitleExt2 ? `${episodeName}.${subtitleExt2}` : null;
1565
+ const dirFiles = isDir ? (0, import_fs10.readdirSync)(entryPath) : [];
1566
+ const subtitle = isDir ? findSubtitle(dirFiles, language) : null;
1567
+ const subtitleExt = subtitle?.match(/([^.]+$)/)?.[0];
1568
+ const subtitleSourcePath = subtitle ? (0, import_path11.resolve)(entryPath, subtitle) : null;
1569
+ const destSubtitleName = subtitle && subtitleExt ? `${episodeName}.${subtitleExt}` : null;
1408
1570
  if (!dryRun) {
1409
1571
  (0, import_fs10.mkdirSync)(seasonPath, { recursive: true });
1410
1572
  let mode = "move";
1411
1573
  if (useHardlink) {
1412
1574
  try {
1413
- if (!sameDev(videoSourcePath2, seasonPath)) throw new Error("cross-filesystem");
1414
- (0, import_fs10.linkSync)(videoSourcePath2, destVideoPath);
1575
+ if (!sameDev(videoSourcePath, seasonPath)) throw new Error("cross-filesystem");
1576
+ (0, import_fs10.linkSync)(videoSourcePath, destVideoPath);
1415
1577
  mode = "hardlink";
1416
1578
  } catch {
1417
1579
  spinner_default.warn(`hardlink unavailable \u2014 copying instead: ${episodeName}`);
1418
- (0, import_fs10.cpSync)(videoSourcePath2, destVideoPath);
1580
+ (0, import_fs10.cpSync)(videoSourcePath, destVideoPath);
1419
1581
  mode = "copy";
1420
1582
  }
1421
- if (subtitleSourcePath2 && destSubtitleName2) (0, import_fs10.cpSync)(subtitleSourcePath2, (0, import_path11.resolve)(seasonPath, destSubtitleName2));
1583
+ if (subtitleSourcePath && destSubtitleName) (0, import_fs10.cpSync)(subtitleSourcePath, (0, import_path11.resolve)(seasonPath, destSubtitleName));
1422
1584
  } else {
1423
- if (sameDev(videoSourcePath2, seasonPath)) {
1424
- (0, import_fs10.renameSync)(videoSourcePath2, destVideoPath);
1585
+ if (sameDev(videoSourcePath, seasonPath)) {
1586
+ (0, import_fs10.renameSync)(videoSourcePath, destVideoPath);
1425
1587
  } else {
1426
- (0, import_fs10.cpSync)(videoSourcePath2, destVideoPath);
1427
- (0, import_fs10.rmSync)(videoSourcePath2);
1588
+ (0, import_fs10.cpSync)(videoSourcePath, destVideoPath);
1589
+ (0, import_fs10.rmSync)(videoSourcePath);
1428
1590
  }
1429
- if (subtitleSourcePath2 && destSubtitleName2) (0, import_fs10.renameSync)(subtitleSourcePath2, (0, import_path11.resolve)(seasonPath, destSubtitleName2));
1591
+ if (subtitleSourcePath && destSubtitleName) (0, import_fs10.renameSync)(subtitleSourcePath, (0, import_path11.resolve)(seasonPath, destSubtitleName));
1430
1592
  if (isDir) (0, import_fs10.rmSync)(entryPath, { recursive: true, force: true });
1431
1593
  }
1432
1594
  recordImport(sessionId, entryPath, seasonPath, mode, tmdbId);
@@ -1435,66 +1597,40 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, verbose, auto }) => {
1435
1597
  imported++;
1436
1598
  continue;
1437
1599
  }
1438
- const edition = detectEdition(entry);
1439
- const folderName = formatMovieName(movieFormat, resolvedTitle, resolvedYear, edition);
1440
- const destFolder = (0, import_path11.resolve)(destRoot, folderName);
1441
- if ((0, import_fs10.existsSync)(destFolder)) {
1442
- spinner_default.warn(`already exists: ${folderName}`);
1600
+ if (await importMovie(entry, entryPath, isDir, resolvedTitle, resolvedYear, tmdbId, destRoot)) {
1601
+ imported++;
1602
+ } else {
1443
1603
  skipped++;
1444
- continue;
1445
1604
  }
1446
- const videoFile = isDir ? findVideo(entryPath) : entry;
1447
- if (!videoFile) {
1448
- if (verbose) spinner_default.info(`no video found in: ${entry}`);
1605
+ }
1606
+ }
1607
+ if (pendingMovies.length > 0) {
1608
+ spinner_default.warn(`${pendingMovies.length} uncertain movie match${pendingMovies.length > 1 ? "es" : ""} skipped \u2014 use -i to review or -f to import all`);
1609
+ for (const p of pendingMovies) spinner_default.info(` ? ${p.entry.replace(/\/$/, "")}`);
1610
+ let toProcess = [];
1611
+ if (interactive) {
1612
+ spinner_default.stop();
1613
+ const ms = new import_termkit10.MultiSelect({ allowSkip: true, search: true, maxHeight: 20 });
1614
+ const items = pendingMovies.map((p) => ({
1615
+ label: p.entry.replace(/\/$/, ""),
1616
+ description: p.parsed.year ? `parsed: ${p.parsed.title} \xB7 ${p.parsed.year}` : `parsed: ${p.parsed.title}`,
1617
+ ...p
1618
+ }));
1619
+ toProcess = await ms.ask("Select entries to import as movies:", items) ?? [];
1620
+ spinner_default.start();
1621
+ skipped += pendingMovies.length - toProcess.length;
1622
+ } else if (force) {
1623
+ toProcess = pendingMovies;
1624
+ } else {
1625
+ skipped += pendingMovies.length;
1626
+ }
1627
+ for (const p of toProcess) {
1628
+ const { tmdbId, resolvedTitle, resolvedYear } = await lookupMovie(p.parsed);
1629
+ if (await importMovie(p.entry, p.entryPath, p.isDir, resolvedTitle, resolvedYear, tmdbId, p.destRoot)) {
1630
+ imported++;
1631
+ } else {
1449
1632
  skipped++;
1450
- continue;
1451
1633
  }
1452
- const videoExt = videoFile.match(/([^.]+$)/)?.[0];
1453
- const destVideoName = `${folderName}.${videoExt}`;
1454
- const videoSourcePath = isDir ? (0, import_path11.resolve)(entryPath, videoFile) : entryPath;
1455
- const dirFiles = isDir ? (0, import_fs10.readdirSync)(entryPath) : [];
1456
- const subtitle = isDir ? findSubtitle(dirFiles, language) : null;
1457
- const subtitleExt = subtitle?.match(/([^.]+$)/)?.[0];
1458
- const subtitleSourcePath = subtitle ? (0, import_path11.resolve)(entryPath, subtitle) : null;
1459
- const destSubtitleName = subtitle && subtitleExt ? `${folderName}.${subtitleExt}` : null;
1460
- if (!dryRun) {
1461
- if (useHardlink) {
1462
- (0, import_fs10.mkdirSync)(destFolder, { recursive: true });
1463
- const destVideoPath = (0, import_path11.resolve)(destFolder, destVideoName);
1464
- let mode;
1465
- try {
1466
- if (!sameDev(videoSourcePath, destRoot)) throw new Error("cross-filesystem");
1467
- (0, import_fs10.linkSync)(videoSourcePath, destVideoPath);
1468
- mode = "hardlink";
1469
- } catch {
1470
- spinner_default.warn(`hardlink unavailable \u2014 copying instead: ${folderName}`);
1471
- (0, import_fs10.cpSync)(videoSourcePath, destVideoPath);
1472
- mode = "copy";
1473
- }
1474
- if (subtitleSourcePath && destSubtitleName) (0, import_fs10.cpSync)(subtitleSourcePath, (0, import_path11.resolve)(destFolder, destSubtitleName));
1475
- recordImport(sessionId, entryPath, destFolder, mode, tmdbId);
1476
- } else {
1477
- if (isDir) {
1478
- const keep = new Set([videoFile, subtitle].filter(Boolean));
1479
- for (const f of dirFiles.filter((f2) => !keep.has(f2))) (0, import_fs10.rmSync)((0, import_path11.resolve)(entryPath, f), { recursive: true, force: true });
1480
- (0, import_fs10.renameSync)(videoSourcePath, (0, import_path11.resolve)(entryPath, destVideoName));
1481
- if (subtitleSourcePath && destSubtitleName) (0, import_fs10.renameSync)(subtitleSourcePath, (0, import_path11.resolve)(entryPath, destSubtitleName));
1482
- moveFolder(entryPath, destFolder);
1483
- } else {
1484
- (0, import_fs10.mkdirSync)(destFolder, { recursive: true });
1485
- const destVideoPath = (0, import_path11.resolve)(destFolder, destVideoName);
1486
- if (sameDev(videoSourcePath, destRoot)) {
1487
- (0, import_fs10.renameSync)(videoSourcePath, destVideoPath);
1488
- } else {
1489
- (0, import_fs10.cpSync)(videoSourcePath, destVideoPath);
1490
- (0, import_fs10.rmSync)(videoSourcePath);
1491
- }
1492
- }
1493
- recordImport(sessionId, entryPath, destFolder, "move", tmdbId);
1494
- }
1495
- }
1496
- spinner_default.succeed(`${dryRun ? "[dry] " : ""}${folderName}`);
1497
- imported++;
1498
1634
  }
1499
1635
  }
1500
1636
  spinner_default.succeed(`imported ${imported} items`);
@@ -1517,7 +1653,7 @@ var undo = async () => {
1517
1653
  let undone = 0;
1518
1654
  for (const record of records) {
1519
1655
  (0, import_fs11.renameSync)(record.newPath, record.oldPath);
1520
- spinner_default.succeed(`${import_termkit11.Color.cyan.encoder(record.newPath)} \u2192 ${import_termkit11.Color.blue.encoder(record.oldPath)}`);
1656
+ spinner_default.succeed(`${import_termkit11.Color.green.encoder(record.newPath)} \u2192 ${import_termkit11.Color.white.encoder(record.oldPath)}`);
1521
1657
  undone++;
1522
1658
  }
1523
1659
  deleteSession(records[0].sessionId);
@@ -1552,6 +1688,10 @@ var findVideo2 = (dir) => (0, import_fs12.readdirSync)(dir).find((f) => {
1552
1688
  const ext = f.match(/([^.]+$)/)?.[0];
1553
1689
  return ext && videoExtensions_default.includes(ext);
1554
1690
  }) ?? null;
1691
+ var containsBook2 = (dir) => (0, import_fs12.readdirSync)(dir).some((f) => {
1692
+ const ext = f.match(/([^.]+$)/)?.[0];
1693
+ return ext && bookExtensions_default.includes(ext);
1694
+ });
1555
1695
  var findSeasonFolder2 = (showPath, season) => {
1556
1696
  if (!(0, import_fs12.existsSync)(showPath)) return null;
1557
1697
  const folders = (0, import_fs12.readdirSync)(showPath).filter((f) => {
@@ -1575,9 +1715,13 @@ var processItem = async (entryPath, useHardlink, verbose, language, auto) => {
1575
1715
  const isDir = (0, import_fs12.lstatSync)(entryPath).isDirectory();
1576
1716
  const ext = entry.match(/([^.]+$)/)?.[0];
1577
1717
  const isVideo = !isDir && ext && videoExtensions_default.includes(ext);
1578
- if (!isDir && !isVideo) return;
1718
+ const isBook = !isDir && ext && bookExtensions_default.includes(ext);
1719
+ const isBookDir = isDir && containsBook2(entryPath);
1720
+ if (!isDir && !isVideo && !isBook) return;
1579
1721
  let detectedType;
1580
- if (isDir && /(?<=\[).+?(?=\])/.test(entry)) {
1722
+ if (isBook || isBookDir) {
1723
+ detectedType = "book";
1724
+ } else if (isDir && /(?<=\[).+?(?=\])/.test(entry)) {
1581
1725
  detectedType = "ps3";
1582
1726
  } else if (/S\d{2,3}E\d{2,3}/i.test(entry) || /\d+x\d{2,3}/i.test(entry)) {
1583
1727
  detectedType = "tv";
@@ -1601,7 +1745,28 @@ var processItem = async (entryPath, useHardlink, verbose, language, auto) => {
1601
1745
  }
1602
1746
  moveItem(entryPath, destPath);
1603
1747
  recordImport(sessionId, entryPath, destPath, "move");
1604
- spinner_default.succeed(`imported ${import_termkit12.Color.cyan.encoder(destName)}`);
1748
+ spinner_default.succeed(`imported ${import_termkit12.Color.green.encoder(destName)}`);
1749
+ return;
1750
+ }
1751
+ if (detectedType === "book") {
1752
+ const destPath = (0, import_path12.resolve)(destRoot, entry);
1753
+ if ((0, import_fs12.existsSync)(destPath)) {
1754
+ spinner_default.warn(`already exists: ${entry}`);
1755
+ return;
1756
+ }
1757
+ if (isDir || isBookDir) {
1758
+ moveItem(entryPath, destPath);
1759
+ } else {
1760
+ (0, import_fs12.mkdirSync)(destRoot, { recursive: true });
1761
+ if (sameDev2(entryPath, destRoot)) {
1762
+ (0, import_fs12.renameSync)(entryPath, destPath);
1763
+ } else {
1764
+ (0, import_fs12.cpSync)(entryPath, destPath);
1765
+ (0, import_fs12.rmSync)(entryPath);
1766
+ }
1767
+ }
1768
+ recordImport(sessionId, entryPath, destPath, "move");
1769
+ spinner_default.succeed(`imported ${import_termkit12.Color.green.encoder(entry)}`);
1605
1770
  return;
1606
1771
  }
1607
1772
  const parsed = parseDownloadName(entry);
@@ -1674,7 +1839,7 @@ var processItem = async (entryPath, useHardlink, verbose, language, auto) => {
1674
1839
  if (isDir) (0, import_fs12.rmSync)(entryPath, { recursive: true, force: true });
1675
1840
  }
1676
1841
  recordImport(sessionId, entryPath, seasonPath, mode);
1677
- spinner_default.succeed(`imported ${import_termkit12.Color.cyan.encoder(`${showFolderName} / ${seasonFolderName} / ${episodeName}`)}`);
1842
+ spinner_default.succeed(`imported ${import_termkit12.Color.green.encoder(`${showFolderName} / ${seasonFolderName} / ${episodeName}`)}`);
1678
1843
  return;
1679
1844
  }
1680
1845
  const edition = detectEdition(entry);
@@ -1731,7 +1896,7 @@ var processItem = async (entryPath, useHardlink, verbose, language, auto) => {
1731
1896
  }
1732
1897
  recordImport(sessionId, entryPath, destFolder, "move");
1733
1898
  }
1734
- spinner_default.succeed(`imported ${import_termkit12.Color.cyan.encoder(folderName)}`);
1899
+ spinner_default.succeed(`imported ${import_termkit12.Color.green.encoder(folderName)}`);
1735
1900
  };
1736
1901
  var watch = async ({ hardlink = false, verbose = false, auto = false }) => {
1737
1902
  const config = getConfig();
@@ -1762,7 +1927,7 @@ var watch = async ({ hardlink = false, verbose = false, auto = false }) => {
1762
1927
  watcher.on("add", handle);
1763
1928
  spinner_default.start();
1764
1929
  spinner_default.succeed(`watching ${config.sources.length} source${config.sources.length !== 1 ? "s" : ""}`);
1765
- for (const s of config.sources) spinner_default.info(` ${import_termkit12.Color.blue.encoder(s)}`);
1930
+ for (const s of config.sources) spinner_default.info(` ${import_termkit12.Color.white.encoder(s)}`);
1766
1931
  spinner_default.stop();
1767
1932
  process.stdin.resume();
1768
1933
  };