reelsort 0.2.4 → 0.2.6
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 +289 -197
- package/dist/index.d.mts +9 -4
- package/dist/index.d.ts +9 -4
- package/dist/index.js +281 -185
- package/dist/index.mjs +184 -90
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -37,6 +37,7 @@ __export(index_exports, {
|
|
|
37
37
|
configSet: () => configSet,
|
|
38
38
|
configShow: () => configShow,
|
|
39
39
|
deleteImport: () => deleteImport,
|
|
40
|
+
deleteImportSession: () => deleteImportSession,
|
|
40
41
|
deleteSession: () => deleteSession,
|
|
41
42
|
destAdd: () => destAdd,
|
|
42
43
|
destRemove: () => destRemove,
|
|
@@ -49,6 +50,7 @@ __export(index_exports, {
|
|
|
49
50
|
getConfig: () => getConfig,
|
|
50
51
|
getHistory: () => getHistory,
|
|
51
52
|
getImportByDest: () => getImportByDest,
|
|
53
|
+
getLastImportSession: () => getLastImportSession,
|
|
52
54
|
getLastSession: () => getLastSession,
|
|
53
55
|
getMediaInfo: () => getMediaInfo,
|
|
54
56
|
history: () => history_default,
|
|
@@ -130,6 +132,10 @@ var db = () => {
|
|
|
130
132
|
_db.exec("ALTER TABLE shows ADD COLUMN title TEXT");
|
|
131
133
|
} catch {
|
|
132
134
|
}
|
|
135
|
+
try {
|
|
136
|
+
_db.exec("ALTER TABLE imports ADD COLUMN type TEXT");
|
|
137
|
+
} catch {
|
|
138
|
+
}
|
|
133
139
|
return _db;
|
|
134
140
|
};
|
|
135
141
|
var recordRename = (sessionId, oldPath, newPath) => {
|
|
@@ -143,8 +149,16 @@ var getLastSession = () => {
|
|
|
143
149
|
var deleteSession = (sessionId) => {
|
|
144
150
|
db().prepare("DELETE FROM renameHistory WHERE sessionId = ?").run(sessionId);
|
|
145
151
|
};
|
|
146
|
-
var
|
|
147
|
-
db().prepare("
|
|
152
|
+
var getLastImportSession = () => {
|
|
153
|
+
const last = db().prepare("SELECT sessionId FROM imports ORDER BY id DESC LIMIT 1").get();
|
|
154
|
+
if (!last) return [];
|
|
155
|
+
return db().prepare("SELECT * FROM imports WHERE sessionId = ? ORDER BY id DESC").all(last.sessionId);
|
|
156
|
+
};
|
|
157
|
+
var deleteImportSession = (sessionId) => {
|
|
158
|
+
db().prepare("DELETE FROM imports WHERE sessionId = ?").run(sessionId);
|
|
159
|
+
};
|
|
160
|
+
var recordImport = (sessionId, sourcePath, destPath, mode, tmdbId, type) => {
|
|
161
|
+
db().prepare("INSERT INTO imports (sessionId, sourcePath, destinationPath, mode, tmdbId, type) VALUES (?, ?, ?, ?, ?, ?)").run(sessionId, sourcePath, destPath, mode, tmdbId ?? null, type ?? null);
|
|
148
162
|
};
|
|
149
163
|
var getMediaInfo = (filePath) => {
|
|
150
164
|
return db().prepare("SELECT * FROM mediaInfo WHERE filePath = ?").get(filePath);
|
|
@@ -320,10 +334,10 @@ var saveConfig = (config) => {
|
|
|
320
334
|
};
|
|
321
335
|
|
|
322
336
|
// src/helpers/formatEpisode.ts
|
|
323
|
-
var DEFAULT_EPISODE_FORMAT = "{s}x{ee}";
|
|
337
|
+
var DEFAULT_EPISODE_FORMAT = "{s}x{ee} - {title}";
|
|
324
338
|
var DEFAULT_SEASON_FORMAT = "Season {s}";
|
|
325
339
|
var renderEpisode = (format, season, episode, title, name) => {
|
|
326
|
-
return format.replace(/\{sss\}/g, season.toString().padStart(3, "0")).replace(/\{ss\}/g, season.toString().padStart(2, "0")).replace(/\{s\}/g, season.toString()).replace(/\{eee\}/g, episode.toString().padStart(3, "0")).replace(/\{ee\}/g, episode.toString().padStart(2, "0")).replace(/\{e\}/g, episode.toString()).replace(/\{
|
|
340
|
+
return format.replace(/\{sss\}/g, season.toString().padStart(3, "0")).replace(/\{ss\}/g, season.toString().padStart(2, "0")).replace(/\{s\}/g, season.toString()).replace(/\{eee\}/g, episode.toString().padStart(3, "0")).replace(/\{ee\}/g, episode.toString().padStart(2, "0")).replace(/\{e\}/g, episode.toString()).replace(/\{show\}/g, title ?? "").replace(/\{title\}/g, name ?? "").replace(/(\s*-\s*)+$/, "").replace(/^(\s*-\s*)+/, "").replace(/\s+/g, " ").trim();
|
|
327
341
|
};
|
|
328
342
|
var formatSeasonFolder = (format, season) => format.replace(/\{sss\}/g, season.toString().padStart(3, "0")).replace(/\{ss\}/g, season.toString().padStart(2, "0")).replace(/\{s\}/g, season.toString()).trim();
|
|
329
343
|
var formatEpisode = (season, episode, format = DEFAULT_EPISODE_FORMAT, double = false, title, name) => {
|
|
@@ -1136,8 +1150,8 @@ var reset = async ({ dir: inputDir, double }) => {
|
|
|
1136
1150
|
var reset_default = reset;
|
|
1137
1151
|
|
|
1138
1152
|
// src/actions/scan.ts
|
|
1139
|
-
var
|
|
1140
|
-
var
|
|
1153
|
+
var import_fs11 = require("fs");
|
|
1154
|
+
var import_path12 = require("path");
|
|
1141
1155
|
var import_termkit10 = require("termkit");
|
|
1142
1156
|
|
|
1143
1157
|
// src/helpers/detectEdition.ts
|
|
@@ -1161,10 +1175,49 @@ var detectEdition = (filename) => {
|
|
|
1161
1175
|
// src/helpers/hyperlink.ts
|
|
1162
1176
|
var hyperlink = (url, text) => `\x1B]8;;${url}\x1B\\${text}\x1B]8;;\x1B\\`;
|
|
1163
1177
|
|
|
1178
|
+
// src/helpers/trash.ts
|
|
1179
|
+
var import_child_process2 = require("child_process");
|
|
1180
|
+
var import_fs10 = require("fs");
|
|
1181
|
+
var import_os3 = require("os");
|
|
1182
|
+
var import_path11 = require("path");
|
|
1183
|
+
var trashDir = () => {
|
|
1184
|
+
if (process.platform === "linux") return (0, import_path11.join)((0, import_os3.homedir)(), ".local", "share", "Trash", "files");
|
|
1185
|
+
return (0, import_path11.join)((0, import_os3.homedir)(), ".Trash");
|
|
1186
|
+
};
|
|
1187
|
+
var trashPath = (filePath, opts) => {
|
|
1188
|
+
if (process.platform === "darwin" || process.platform === "linux") {
|
|
1189
|
+
try {
|
|
1190
|
+
const dir = trashDir();
|
|
1191
|
+
if (!(0, import_fs10.existsSync)(dir)) (0, import_fs10.mkdirSync)(dir, { recursive: true });
|
|
1192
|
+
const name = (0, import_path11.basename)(filePath);
|
|
1193
|
+
let dest = (0, import_path11.join)(dir, name);
|
|
1194
|
+
let i = 1;
|
|
1195
|
+
while ((0, import_fs10.existsSync)(dest)) dest = (0, import_path11.join)(dir, `${name} ${i++}`);
|
|
1196
|
+
(0, import_fs10.renameSync)(filePath, dest);
|
|
1197
|
+
return;
|
|
1198
|
+
} catch {
|
|
1199
|
+
}
|
|
1200
|
+
} else if (process.platform === "win32") {
|
|
1201
|
+
try {
|
|
1202
|
+
const escaped = filePath.replace(/'/g, "''");
|
|
1203
|
+
const isDir = opts?.recursive ?? false;
|
|
1204
|
+
const method = isDir ? "DeleteDirectory" : "DeleteFile";
|
|
1205
|
+
(0, import_child_process2.execSync)(
|
|
1206
|
+
`powershell -NoProfile -NonInteractive -Command "Add-Type -AssemblyName Microsoft.VisualBasic; [Microsoft.VisualBasic.FileIO.FileSystem]::${method}('${escaped}', 'OnlyErrorDialogs', 'SendToRecycleBin')"`,
|
|
1207
|
+
{ stdio: "ignore" }
|
|
1208
|
+
);
|
|
1209
|
+
return;
|
|
1210
|
+
} catch {
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
(0, import_fs10.rmSync)(filePath, { recursive: opts?.recursive ?? false, force: true });
|
|
1214
|
+
};
|
|
1215
|
+
|
|
1164
1216
|
// src/helpers/parseDownloadName.ts
|
|
1165
1217
|
var QUALITY_TOKENS = /* @__PURE__ */ new Set(["480p", "576p", "720p", "1080p", "2160p", "4k", "8k", "bluray", "bdrip", "bdremux", "brrip", "webrip", "web-dl", "webdl", "web", "hdtv", "dvdrip", "dvdscr", "cam", "ts", "scr", "x264", "x265", "hevc", "avc", "h264", "h265", "xvid", "divx", "dts", "ac3", "aac", "mp3", "truehd", "atmos", "dd5", "hdr", "hdr10", "hlg", "dv", "dolby", "remux", "proper", "repack", "extended", "theatrical", "unrated", "multi", "dubbed", "subbed", "internal"]);
|
|
1166
1218
|
var TV_PATTERN = /^(.*?)[.\s_-]*(?:S(\d{2,3})E(\d{2,3})|(\d{1,2})x(\d{2,3})|Season[\s.](\d+))/i;
|
|
1167
|
-
var parseDownloadName = (
|
|
1219
|
+
var parseDownloadName = (rawName) => {
|
|
1220
|
+
const name = rawName.replace(/^[\w.-]+\.\w{2,6}\s+-+\s+/i, "");
|
|
1168
1221
|
const base = name.replace(/\.[a-z0-9]{2,4}$/i, "");
|
|
1169
1222
|
const tvMatch = TV_PATTERN.exec(base);
|
|
1170
1223
|
if (tvMatch) {
|
|
@@ -1263,31 +1316,31 @@ var bookExtensions_default = ["epub", "mobi", "azw3", "azw"];
|
|
|
1263
1316
|
var sameDev = (a, b) => {
|
|
1264
1317
|
try {
|
|
1265
1318
|
let bExisting = b;
|
|
1266
|
-
while (!(0,
|
|
1267
|
-
return (0,
|
|
1319
|
+
while (!(0, import_fs11.existsSync)(bExisting)) bExisting = (0, import_path12.dirname)(bExisting);
|
|
1320
|
+
return (0, import_fs11.statSync)(a).dev === (0, import_fs11.statSync)(bExisting).dev;
|
|
1268
1321
|
} catch {
|
|
1269
1322
|
return false;
|
|
1270
1323
|
}
|
|
1271
1324
|
};
|
|
1272
1325
|
var moveFolder = (src, dest) => {
|
|
1273
1326
|
if (sameDev(src, dest)) {
|
|
1274
|
-
(0,
|
|
1327
|
+
(0, import_fs11.renameSync)(src, dest);
|
|
1275
1328
|
} else {
|
|
1276
|
-
(0,
|
|
1277
|
-
(
|
|
1329
|
+
(0, import_fs11.cpSync)(src, dest, { recursive: true });
|
|
1330
|
+
trashPath(src, { recursive: true });
|
|
1278
1331
|
}
|
|
1279
1332
|
};
|
|
1280
|
-
var findVideo = (dir) => (0,
|
|
1333
|
+
var findVideo = (dir) => (0, import_fs11.readdirSync)(dir).find((f) => {
|
|
1281
1334
|
const ext = f.match(/([^.]+$)/)?.[0];
|
|
1282
1335
|
return ext && videoExtensions_default.includes(ext);
|
|
1283
1336
|
}) ?? null;
|
|
1284
|
-
var containsBook = (dir, depth = 2) => (0,
|
|
1337
|
+
var containsBook = (dir, depth = 2) => (0, import_fs11.readdirSync)(dir).some((f) => {
|
|
1285
1338
|
const ext = f.match(/([^.]+$)/)?.[0];
|
|
1286
1339
|
if (ext && bookExtensions_default.includes(ext)) return true;
|
|
1287
1340
|
if (depth > 1) {
|
|
1288
1341
|
try {
|
|
1289
|
-
const sub = (0,
|
|
1290
|
-
if ((0,
|
|
1342
|
+
const sub = (0, import_path12.resolve)(dir, f);
|
|
1343
|
+
if ((0, import_fs11.lstatSync)(sub).isDirectory()) return containsBook(sub, depth - 1);
|
|
1291
1344
|
} catch {
|
|
1292
1345
|
}
|
|
1293
1346
|
}
|
|
@@ -1297,11 +1350,11 @@ var isTvEpisodeName = (name) => /S\d{2,3}E\d{2,3}/i.test(name) || /\d+x\d{2,3}/i
|
|
|
1297
1350
|
var isSeasonDirName = (name) => !isTvEpisodeName(name) && /(?:^|[.\s_-])(?:season|s)\s*0*\d+(?:[.\s_-]|$)/i.test(name);
|
|
1298
1351
|
var gatherEntries = (source) => {
|
|
1299
1352
|
const result = [];
|
|
1300
|
-
for (const name of (0,
|
|
1301
|
-
const fullPath = (0,
|
|
1353
|
+
for (const name of (0, import_fs11.readdirSync)(source)) {
|
|
1354
|
+
const fullPath = (0, import_path12.resolve)(source, name);
|
|
1302
1355
|
let isDir;
|
|
1303
1356
|
try {
|
|
1304
|
-
isDir = (0,
|
|
1357
|
+
isDir = (0, import_fs11.lstatSync)(fullPath).isDirectory();
|
|
1305
1358
|
} catch {
|
|
1306
1359
|
continue;
|
|
1307
1360
|
}
|
|
@@ -1319,17 +1372,17 @@ var gatherEntries = (source) => {
|
|
|
1319
1372
|
}
|
|
1320
1373
|
let children;
|
|
1321
1374
|
try {
|
|
1322
|
-
children = (0,
|
|
1375
|
+
children = (0, import_fs11.readdirSync)(fullPath);
|
|
1323
1376
|
} catch {
|
|
1324
1377
|
result.push({ entry: name, entryPath: fullPath, isDir: true });
|
|
1325
1378
|
continue;
|
|
1326
1379
|
}
|
|
1327
1380
|
if (children.some((c) => isTvEpisodeName(c))) {
|
|
1328
1381
|
for (const child of children) {
|
|
1329
|
-
const childPath = (0,
|
|
1382
|
+
const childPath = (0, import_path12.resolve)(fullPath, child);
|
|
1330
1383
|
let childIsDir;
|
|
1331
1384
|
try {
|
|
1332
|
-
childIsDir = (0,
|
|
1385
|
+
childIsDir = (0, import_fs11.lstatSync)(childPath).isDirectory();
|
|
1333
1386
|
} catch {
|
|
1334
1387
|
continue;
|
|
1335
1388
|
}
|
|
@@ -1341,25 +1394,25 @@ var gatherEntries = (source) => {
|
|
|
1341
1394
|
}
|
|
1342
1395
|
const seasonDirs = children.filter((c) => {
|
|
1343
1396
|
try {
|
|
1344
|
-
return isSeasonDirName(c) && (0,
|
|
1397
|
+
return isSeasonDirName(c) && (0, import_fs11.lstatSync)((0, import_path12.resolve)(fullPath, c)).isDirectory();
|
|
1345
1398
|
} catch {
|
|
1346
1399
|
return false;
|
|
1347
1400
|
}
|
|
1348
1401
|
});
|
|
1349
1402
|
if (seasonDirs.length > 0) {
|
|
1350
1403
|
for (const seasonDir of seasonDirs) {
|
|
1351
|
-
const seasonPath = (0,
|
|
1404
|
+
const seasonPath = (0, import_path12.resolve)(fullPath, seasonDir);
|
|
1352
1405
|
let seasonChildren;
|
|
1353
1406
|
try {
|
|
1354
|
-
seasonChildren = (0,
|
|
1407
|
+
seasonChildren = (0, import_fs11.readdirSync)(seasonPath);
|
|
1355
1408
|
} catch {
|
|
1356
1409
|
continue;
|
|
1357
1410
|
}
|
|
1358
1411
|
for (const child of seasonChildren) {
|
|
1359
|
-
const childPath = (0,
|
|
1412
|
+
const childPath = (0, import_path12.resolve)(seasonPath, child);
|
|
1360
1413
|
let childIsDir;
|
|
1361
1414
|
try {
|
|
1362
|
-
childIsDir = (0,
|
|
1415
|
+
childIsDir = (0, import_fs11.lstatSync)(childPath).isDirectory();
|
|
1363
1416
|
} catch {
|
|
1364
1417
|
continue;
|
|
1365
1418
|
}
|
|
@@ -1375,19 +1428,19 @@ var gatherEntries = (source) => {
|
|
|
1375
1428
|
return result;
|
|
1376
1429
|
};
|
|
1377
1430
|
var findShowFolder = (destRoot, title) => {
|
|
1378
|
-
if (!(0,
|
|
1431
|
+
if (!(0, import_fs11.existsSync)(destRoot)) return null;
|
|
1379
1432
|
const normalize = (s) => s.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
1380
1433
|
const target = normalize(title);
|
|
1381
|
-
return (0,
|
|
1434
|
+
return (0, import_fs11.readdirSync)(destRoot).filter((f) => {
|
|
1382
1435
|
try {
|
|
1383
|
-
return (0,
|
|
1436
|
+
return (0, import_fs11.lstatSync)((0, import_path12.resolve)(destRoot, f)).isDirectory();
|
|
1384
1437
|
} catch {
|
|
1385
1438
|
return false;
|
|
1386
1439
|
}
|
|
1387
1440
|
}).find((f) => normalize(f) === target) ?? null;
|
|
1388
1441
|
};
|
|
1389
1442
|
var findShowFolderByContent = (destRoot, title) => {
|
|
1390
|
-
if (!(0,
|
|
1443
|
+
if (!(0, import_fs11.existsSync)(destRoot)) return null;
|
|
1391
1444
|
const normalize = (s) => s.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
1392
1445
|
const target = normalize(title);
|
|
1393
1446
|
const matchesTitle = (name) => {
|
|
@@ -1395,18 +1448,18 @@ var findShowFolderByContent = (destRoot, title) => {
|
|
|
1395
1448
|
const p = parseDownloadName(name);
|
|
1396
1449
|
return !!p && normalize(p.title) === target;
|
|
1397
1450
|
};
|
|
1398
|
-
for (const folder of (0,
|
|
1451
|
+
for (const folder of (0, import_fs11.readdirSync)(destRoot)) {
|
|
1399
1452
|
try {
|
|
1400
|
-
const folderPath = (0,
|
|
1401
|
-
if (!(0,
|
|
1402
|
-
const children = (0,
|
|
1453
|
+
const folderPath = (0, import_path12.resolve)(destRoot, folder);
|
|
1454
|
+
if (!(0, import_fs11.lstatSync)(folderPath).isDirectory()) continue;
|
|
1455
|
+
const children = (0, import_fs11.readdirSync)(folderPath);
|
|
1403
1456
|
if (children.some(matchesTitle)) return folder;
|
|
1404
1457
|
for (const child of children) {
|
|
1405
1458
|
if (!isSeasonDirName(child)) continue;
|
|
1406
1459
|
try {
|
|
1407
|
-
const seasonPath = (0,
|
|
1408
|
-
if (!(0,
|
|
1409
|
-
if ((0,
|
|
1460
|
+
const seasonPath = (0, import_path12.resolve)(folderPath, child);
|
|
1461
|
+
if (!(0, import_fs11.lstatSync)(seasonPath).isDirectory()) continue;
|
|
1462
|
+
if ((0, import_fs11.readdirSync)(seasonPath).some(matchesTitle)) return folder;
|
|
1410
1463
|
} catch {
|
|
1411
1464
|
}
|
|
1412
1465
|
}
|
|
@@ -1415,15 +1468,19 @@ var findShowFolderByContent = (destRoot, title) => {
|
|
|
1415
1468
|
}
|
|
1416
1469
|
return null;
|
|
1417
1470
|
};
|
|
1418
|
-
var findSeasonFolder = (showPath, season) => {
|
|
1419
|
-
if (!(0,
|
|
1420
|
-
const folders = (0,
|
|
1471
|
+
var findSeasonFolder = (showPath, season, specialsFolder) => {
|
|
1472
|
+
if (!(0, import_fs11.existsSync)(showPath)) return null;
|
|
1473
|
+
const folders = (0, import_fs11.readdirSync)(showPath).filter((f) => {
|
|
1421
1474
|
try {
|
|
1422
|
-
return (0,
|
|
1475
|
+
return (0, import_fs11.lstatSync)((0, import_path12.resolve)(showPath, f)).isDirectory();
|
|
1423
1476
|
} catch {
|
|
1424
1477
|
return false;
|
|
1425
1478
|
}
|
|
1426
1479
|
});
|
|
1480
|
+
if (season === 0 && specialsFolder) {
|
|
1481
|
+
const existing = folders.find((f) => f.toLowerCase() === specialsFolder.toLowerCase());
|
|
1482
|
+
if (existing) return existing;
|
|
1483
|
+
}
|
|
1427
1484
|
return folders.find((f) => {
|
|
1428
1485
|
const match = f.match(/(?:season|s)\s*0*(\d+)/i);
|
|
1429
1486
|
return match && parseInt(match[1]) === season;
|
|
@@ -1439,12 +1496,12 @@ var classifyMovieConfidence = (entry) => {
|
|
|
1439
1496
|
return "ambiguous";
|
|
1440
1497
|
};
|
|
1441
1498
|
var typeColor = {
|
|
1442
|
-
movie: import_termkit10.Color.
|
|
1443
|
-
tv: import_termkit10.Color.
|
|
1444
|
-
book: import_termkit10.Color.
|
|
1445
|
-
ps3: import_termkit10.Color.
|
|
1499
|
+
movie: (s) => import_termkit10.Color.cyan.encoder(s),
|
|
1500
|
+
tv: (s) => import_termkit10.Color.green.encoder(s),
|
|
1501
|
+
book: (s) => import_termkit10.Color.yellow.encoder(s),
|
|
1502
|
+
ps3: (s) => import_termkit10.Color.magenta.encoder(s)
|
|
1446
1503
|
};
|
|
1447
|
-
var typeGlyph = (t) => typeColor[t]
|
|
1504
|
+
var typeGlyph = (t) => typeColor[t]("\u25CF");
|
|
1448
1505
|
var typeTag = (t) => isVerbose() ? import_termkit10.Color.white.faint.encoder(` (${t})`) : "";
|
|
1449
1506
|
var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactive }) => {
|
|
1450
1507
|
const config = getConfig();
|
|
@@ -1452,11 +1509,13 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1452
1509
|
const language = config.language ?? "eng";
|
|
1453
1510
|
const movieFormat = config.format?.movie ?? DEFAULT_MOVIE_FORMAT;
|
|
1454
1511
|
const seasonFormat = config.format?.season ?? DEFAULT_SEASON_FORMAT;
|
|
1512
|
+
const specialsFolder = config.specialsFolder ?? "Specials";
|
|
1455
1513
|
const lookupMovie = async (parsed) => {
|
|
1456
1514
|
let tmdbId;
|
|
1457
1515
|
let resolvedTitle = parsed.title;
|
|
1458
1516
|
let resolvedYear = parsed.year;
|
|
1459
1517
|
if (config.tmdbApiKey) {
|
|
1518
|
+
spinner_default.text = `TMDb: ${parsed.title}`;
|
|
1460
1519
|
const results = await searchMovie(parsed.title, parsed.year, config.tmdbApiKey);
|
|
1461
1520
|
if (results.length === 1) {
|
|
1462
1521
|
tmdbId = results[0].id;
|
|
@@ -1484,8 +1543,8 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1484
1543
|
const importMovie = async (entry, entryPath, isDir, resolvedTitle, resolvedYear, tmdbId, destRoot) => {
|
|
1485
1544
|
const edition = detectEdition(entry);
|
|
1486
1545
|
const folderName = formatMovieName(movieFormat, resolvedTitle, resolvedYear, edition);
|
|
1487
|
-
const destFolder = (0,
|
|
1488
|
-
if ((0,
|
|
1546
|
+
const destFolder = (0, import_path12.resolve)(destRoot, folderName);
|
|
1547
|
+
if ((0, import_fs11.existsSync)(destFolder)) {
|
|
1489
1548
|
spinner_default.warn(`already exists: ${folderName}`);
|
|
1490
1549
|
return false;
|
|
1491
1550
|
}
|
|
@@ -1496,49 +1555,49 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1496
1555
|
}
|
|
1497
1556
|
const videoExt = videoFile.match(/([^.]+$)/)?.[0];
|
|
1498
1557
|
const destVideoName = `${folderName}.${videoExt}`;
|
|
1499
|
-
const videoSourcePath = isDir ? (0,
|
|
1500
|
-
const dirFiles = isDir ? (0,
|
|
1558
|
+
const videoSourcePath = isDir ? (0, import_path12.resolve)(entryPath, videoFile) : entryPath;
|
|
1559
|
+
const dirFiles = isDir ? (0, import_fs11.readdirSync)(entryPath) : [];
|
|
1501
1560
|
const subtitle = isDir ? findSubtitle(dirFiles, language) : null;
|
|
1502
1561
|
const subtitleExt = subtitle?.match(/([^.]+$)/)?.[0];
|
|
1503
|
-
const subtitleSourcePath = subtitle ? (0,
|
|
1562
|
+
const subtitleSourcePath = subtitle ? (0, import_path12.resolve)(entryPath, subtitle) : null;
|
|
1504
1563
|
const destSubtitleName = subtitle && subtitleExt ? `${folderName}.${subtitleExt}` : null;
|
|
1505
1564
|
if (!dryRun) {
|
|
1506
1565
|
if (useHardlink) {
|
|
1507
|
-
(0,
|
|
1508
|
-
const destVideoPath = (0,
|
|
1566
|
+
(0, import_fs11.mkdirSync)(destFolder, { recursive: true });
|
|
1567
|
+
const destVideoPath = (0, import_path12.resolve)(destFolder, destVideoName);
|
|
1509
1568
|
let mode;
|
|
1510
1569
|
try {
|
|
1511
1570
|
if (!sameDev(videoSourcePath, destRoot)) throw new Error("cross-filesystem");
|
|
1512
|
-
(0,
|
|
1571
|
+
(0, import_fs11.linkSync)(videoSourcePath, destVideoPath);
|
|
1513
1572
|
mode = "hardlink";
|
|
1514
1573
|
} catch {
|
|
1515
1574
|
spinner_default.warn(`hardlink unavailable \u2014 copying instead: ${folderName}`);
|
|
1516
|
-
(0,
|
|
1575
|
+
(0, import_fs11.cpSync)(videoSourcePath, destVideoPath);
|
|
1517
1576
|
mode = "copy";
|
|
1518
1577
|
}
|
|
1519
|
-
if (subtitleSourcePath && destSubtitleName) (0,
|
|
1520
|
-
recordImport(sessionId, entryPath, destFolder, mode, tmdbId);
|
|
1578
|
+
if (subtitleSourcePath && destSubtitleName) (0, import_fs11.cpSync)(subtitleSourcePath, (0, import_path12.resolve)(destFolder, destSubtitleName));
|
|
1579
|
+
recordImport(sessionId, entryPath, destFolder, mode, tmdbId, "movie");
|
|
1521
1580
|
} else {
|
|
1522
1581
|
if (isDir) {
|
|
1523
1582
|
const keep = new Set([videoFile, subtitle].filter(Boolean));
|
|
1524
|
-
for (const f of dirFiles.filter((f2) => !keep.has(f2))) (
|
|
1525
|
-
(0,
|
|
1526
|
-
if (subtitleSourcePath && destSubtitleName) (0,
|
|
1583
|
+
for (const f of dirFiles.filter((f2) => !keep.has(f2))) trashPath((0, import_path12.resolve)(entryPath, f), { recursive: true });
|
|
1584
|
+
(0, import_fs11.renameSync)(videoSourcePath, (0, import_path12.resolve)(entryPath, destVideoName));
|
|
1585
|
+
if (subtitleSourcePath && destSubtitleName) (0, import_fs11.renameSync)(subtitleSourcePath, (0, import_path12.resolve)(entryPath, destSubtitleName));
|
|
1527
1586
|
moveFolder(entryPath, destFolder);
|
|
1528
1587
|
} else {
|
|
1529
|
-
(0,
|
|
1530
|
-
const destVideoPath = (0,
|
|
1588
|
+
(0, import_fs11.mkdirSync)(destFolder, { recursive: true });
|
|
1589
|
+
const destVideoPath = (0, import_path12.resolve)(destFolder, destVideoName);
|
|
1531
1590
|
if (sameDev(videoSourcePath, destRoot)) {
|
|
1532
|
-
(0,
|
|
1591
|
+
(0, import_fs11.renameSync)(videoSourcePath, destVideoPath);
|
|
1533
1592
|
} else {
|
|
1534
|
-
(0,
|
|
1535
|
-
(
|
|
1593
|
+
(0, import_fs11.cpSync)(videoSourcePath, destVideoPath);
|
|
1594
|
+
trashPath(videoSourcePath);
|
|
1536
1595
|
}
|
|
1537
1596
|
}
|
|
1538
|
-
recordImport(sessionId, entryPath, destFolder, "move", tmdbId);
|
|
1597
|
+
recordImport(sessionId, entryPath, destFolder, "move", tmdbId, "movie");
|
|
1539
1598
|
}
|
|
1540
1599
|
}
|
|
1541
|
-
spinner_default.succeed(`${dryRun ? "[dry] " : ""}${typeGlyph("movie")} ${typeColor.movie
|
|
1600
|
+
spinner_default.succeed(`${dryRun ? "[dry] " : ""}${typeGlyph("movie")} ${typeColor.movie(folderName)}${typeTag("movie")}`);
|
|
1542
1601
|
return true;
|
|
1543
1602
|
};
|
|
1544
1603
|
spinner_default.start();
|
|
@@ -1549,12 +1608,13 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1549
1608
|
const ignoreSet = new Set(config.ignore ?? []);
|
|
1550
1609
|
const seenIgnored = /* @__PURE__ */ new Set();
|
|
1551
1610
|
for (const source of config.sources) {
|
|
1552
|
-
if (!(0,
|
|
1611
|
+
if (!(0, import_fs11.existsSync)(source)) {
|
|
1553
1612
|
spinner_default.warn(`source not found: ${import_termkit10.Color.white.encoder(source)}`);
|
|
1554
1613
|
continue;
|
|
1555
1614
|
}
|
|
1556
1615
|
spinner_default.text = `scanning ${import_termkit10.Color.white.encoder(source)}`;
|
|
1557
1616
|
for (const { entry, entryPath, isDir } of gatherEntries(source)) {
|
|
1617
|
+
spinner_default.text = `scanning: ${entry}`;
|
|
1558
1618
|
if (ignoreSet.has(entry)) {
|
|
1559
1619
|
seenIgnored.add(entry);
|
|
1560
1620
|
if (isVerbose()) spinner_default.info(`ignored: ${entry}`);
|
|
@@ -1589,23 +1649,23 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1589
1649
|
continue;
|
|
1590
1650
|
}
|
|
1591
1651
|
const destName = `${nameMatch[0]} [${id}]`;
|
|
1592
|
-
const destPath = (0,
|
|
1593
|
-
if ((0,
|
|
1652
|
+
const destPath = (0, import_path12.resolve)(destRoot, destName);
|
|
1653
|
+
if ((0, import_fs11.existsSync)(destPath)) {
|
|
1594
1654
|
spinner_default.warn(`already exists: ${destName}`);
|
|
1595
1655
|
skipped++;
|
|
1596
1656
|
continue;
|
|
1597
1657
|
}
|
|
1598
1658
|
if (!dryRun) {
|
|
1599
1659
|
moveFolder(entryPath, destPath);
|
|
1600
|
-
recordImport(sessionId, entryPath, destPath, "move");
|
|
1660
|
+
recordImport(sessionId, entryPath, destPath, "move", void 0, "ps3");
|
|
1601
1661
|
}
|
|
1602
|
-
spinner_default.succeed(`${dryRun ? "[dry] " : ""}${typeGlyph("ps3")} ${typeColor.ps3
|
|
1662
|
+
spinner_default.succeed(`${dryRun ? "[dry] " : ""}${typeGlyph("ps3")} ${typeColor.ps3(destName)}${typeTag("ps3")}`);
|
|
1603
1663
|
imported++;
|
|
1604
1664
|
continue;
|
|
1605
1665
|
}
|
|
1606
1666
|
if (detectedType === "book") {
|
|
1607
|
-
const destPath = (0,
|
|
1608
|
-
if ((0,
|
|
1667
|
+
const destPath = (0, import_path12.resolve)(destRoot, entry);
|
|
1668
|
+
if ((0, import_fs11.existsSync)(destPath)) {
|
|
1609
1669
|
spinner_default.warn(`already exists: ${entry}`);
|
|
1610
1670
|
skipped++;
|
|
1611
1671
|
continue;
|
|
@@ -1614,17 +1674,17 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1614
1674
|
if (isDir || isBookDir) {
|
|
1615
1675
|
moveFolder(entryPath, destPath);
|
|
1616
1676
|
} else {
|
|
1617
|
-
(0,
|
|
1677
|
+
(0, import_fs11.mkdirSync)(destRoot, { recursive: true });
|
|
1618
1678
|
if (sameDev(entryPath, destRoot)) {
|
|
1619
|
-
(0,
|
|
1679
|
+
(0, import_fs11.renameSync)(entryPath, destPath);
|
|
1620
1680
|
} else {
|
|
1621
|
-
(0,
|
|
1622
|
-
(0,
|
|
1681
|
+
(0, import_fs11.cpSync)(entryPath, destPath);
|
|
1682
|
+
(0, import_fs11.rmSync)(entryPath);
|
|
1623
1683
|
}
|
|
1624
1684
|
}
|
|
1625
|
-
recordImport(sessionId, entryPath, destPath, "move");
|
|
1685
|
+
recordImport(sessionId, entryPath, destPath, "move", void 0, "book");
|
|
1626
1686
|
}
|
|
1627
|
-
spinner_default.succeed(`${dryRun ? "[dry] " : ""}${typeGlyph("book")} ${typeColor.book
|
|
1687
|
+
spinner_default.succeed(`${dryRun ? "[dry] " : ""}${typeGlyph("book")} ${typeColor.book(entry)}${typeTag("book")}`);
|
|
1628
1688
|
imported++;
|
|
1629
1689
|
continue;
|
|
1630
1690
|
}
|
|
@@ -1651,6 +1711,7 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1651
1711
|
let resolvedYear = parsed.year;
|
|
1652
1712
|
if (config.tmdbApiKey) {
|
|
1653
1713
|
if (detectedType === "tv") {
|
|
1714
|
+
spinner_default.text = `TMDb: ${parsed.title}`;
|
|
1654
1715
|
const results = await searchTv(parsed.title, config.tmdbApiKey);
|
|
1655
1716
|
if (results.length === 1) {
|
|
1656
1717
|
tmdbId = results[0].id;
|
|
@@ -1695,18 +1756,18 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1695
1756
|
const existingFolder = findShowFolder(destRoot, resolvedTitle) ?? findShowFolderByContent(destRoot, resolvedTitle);
|
|
1696
1757
|
if (existingFolder) {
|
|
1697
1758
|
showFolderName = existingFolder;
|
|
1698
|
-
showPath = (0,
|
|
1759
|
+
showPath = (0, import_path12.resolve)(destRoot, existingFolder);
|
|
1699
1760
|
} else if (auto) {
|
|
1700
1761
|
showFolderName = formatMovieName(movieFormat, resolvedTitle, resolvedYear);
|
|
1701
|
-
showPath = (0,
|
|
1762
|
+
showPath = (0, import_path12.resolve)(destRoot, showFolderName);
|
|
1702
1763
|
if (!dryRun) upsertShow(showPath, tmdbId ?? null, resolvedTitle);
|
|
1703
1764
|
} else {
|
|
1704
1765
|
pendingTv.push({ entry, entryPath, isDir, parsed, resolvedTitle, destRoot });
|
|
1705
1766
|
continue;
|
|
1706
1767
|
}
|
|
1707
1768
|
}
|
|
1708
|
-
const seasonFolderName = findSeasonFolder(showPath, parsed.season) ?? formatSeasonFolder(seasonFormat, parsed.season);
|
|
1709
|
-
const seasonPath = (0,
|
|
1769
|
+
const seasonFolderName = parsed.season === 0 ? findSeasonFolder(showPath, 0, specialsFolder) ?? specialsFolder : findSeasonFolder(showPath, parsed.season) ?? formatSeasonFolder(seasonFormat, parsed.season);
|
|
1770
|
+
const seasonPath = (0, import_path12.resolve)(showPath, seasonFolderName);
|
|
1710
1771
|
const videoFile = isDir ? findVideo(entryPath) : entry;
|
|
1711
1772
|
if (!videoFile) {
|
|
1712
1773
|
if (isVerbose()) spinner_default.info(`no video found in: ${entry}`);
|
|
@@ -1714,13 +1775,15 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1714
1775
|
continue;
|
|
1715
1776
|
}
|
|
1716
1777
|
const videoExt = videoFile.match(/([^.]+$)/)?.[0];
|
|
1778
|
+
if (tmdbId && config.tmdbApiKey) spinner_default.text = `TMDb: episode name for ${resolvedTitle}`;
|
|
1717
1779
|
const tmdbEpisodeName = tmdbId && config.tmdbApiKey ? await getEpisodeName(tmdbId, parsed.season, parsed.episode ?? 1, config.tmdbApiKey) : null;
|
|
1718
1780
|
const episodeName = formatEpisode(parsed.season, parsed.episode ?? 1, config.format?.episode, false, resolvedTitle, tmdbEpisodeName ?? void 0);
|
|
1719
1781
|
const destVideoName = `${episodeName}.${videoExt}`;
|
|
1720
|
-
const destVideoPath = (0,
|
|
1721
|
-
const videoSourcePath = isDir ? (0,
|
|
1722
|
-
if ((0,
|
|
1723
|
-
|
|
1782
|
+
const destVideoPath = (0, import_path12.resolve)(seasonPath, destVideoName);
|
|
1783
|
+
const videoSourcePath = isDir ? (0, import_path12.resolve)(entryPath, videoFile) : entryPath;
|
|
1784
|
+
if ((0, import_fs11.existsSync)(destVideoPath)) {
|
|
1785
|
+
const isRepack = /\brepack\d*\b|\bproper\b/i.test(entry);
|
|
1786
|
+
let shouldReplace = force || isRepack;
|
|
1724
1787
|
if (!shouldReplace && interactive) {
|
|
1725
1788
|
spinner_default.stop();
|
|
1726
1789
|
const select = new import_termkit10.Select();
|
|
@@ -1737,43 +1800,43 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1737
1800
|
continue;
|
|
1738
1801
|
}
|
|
1739
1802
|
if (!dryRun) {
|
|
1740
|
-
for (const f of (0,
|
|
1741
|
-
if (f.startsWith(`${episodeName}.`)) (
|
|
1803
|
+
for (const f of (0, import_fs11.readdirSync)(seasonPath)) {
|
|
1804
|
+
if (f.startsWith(`${episodeName}.`)) trashPath((0, import_path12.resolve)(seasonPath, f));
|
|
1742
1805
|
}
|
|
1743
1806
|
}
|
|
1744
1807
|
}
|
|
1745
|
-
const dirFiles = isDir ? (0,
|
|
1808
|
+
const dirFiles = isDir ? (0, import_fs11.readdirSync)(entryPath) : [];
|
|
1746
1809
|
const subtitle = isDir ? findSubtitle(dirFiles, language) : null;
|
|
1747
1810
|
const subtitleExt = subtitle?.match(/([^.]+$)/)?.[0];
|
|
1748
|
-
const subtitleSourcePath = subtitle ? (0,
|
|
1811
|
+
const subtitleSourcePath = subtitle ? (0, import_path12.resolve)(entryPath, subtitle) : null;
|
|
1749
1812
|
const destSubtitleName = subtitle && subtitleExt ? `${episodeName}.${subtitleExt}` : null;
|
|
1750
1813
|
if (!dryRun) {
|
|
1751
|
-
(0,
|
|
1814
|
+
(0, import_fs11.mkdirSync)(seasonPath, { recursive: true });
|
|
1752
1815
|
let mode = "move";
|
|
1753
1816
|
if (useHardlink) {
|
|
1754
1817
|
try {
|
|
1755
1818
|
if (!sameDev(videoSourcePath, seasonPath)) throw new Error("cross-filesystem");
|
|
1756
|
-
(0,
|
|
1819
|
+
(0, import_fs11.linkSync)(videoSourcePath, destVideoPath);
|
|
1757
1820
|
mode = "hardlink";
|
|
1758
1821
|
} catch {
|
|
1759
1822
|
spinner_default.warn(`hardlink unavailable \u2014 copying instead: ${episodeName}`);
|
|
1760
|
-
(0,
|
|
1823
|
+
(0, import_fs11.cpSync)(videoSourcePath, destVideoPath);
|
|
1761
1824
|
mode = "copy";
|
|
1762
1825
|
}
|
|
1763
|
-
if (subtitleSourcePath && destSubtitleName) (0,
|
|
1826
|
+
if (subtitleSourcePath && destSubtitleName) (0, import_fs11.cpSync)(subtitleSourcePath, (0, import_path12.resolve)(seasonPath, destSubtitleName));
|
|
1764
1827
|
} else {
|
|
1765
1828
|
if (sameDev(videoSourcePath, seasonPath)) {
|
|
1766
|
-
(0,
|
|
1829
|
+
(0, import_fs11.renameSync)(videoSourcePath, destVideoPath);
|
|
1767
1830
|
} else {
|
|
1768
|
-
(0,
|
|
1769
|
-
(
|
|
1831
|
+
(0, import_fs11.cpSync)(videoSourcePath, destVideoPath);
|
|
1832
|
+
trashPath(videoSourcePath);
|
|
1770
1833
|
}
|
|
1771
|
-
if (subtitleSourcePath && destSubtitleName) (0,
|
|
1772
|
-
if (isDir) (
|
|
1834
|
+
if (subtitleSourcePath && destSubtitleName) (0, import_fs11.renameSync)(subtitleSourcePath, (0, import_path12.resolve)(seasonPath, destSubtitleName));
|
|
1835
|
+
if (isDir) trashPath(entryPath, { recursive: true });
|
|
1773
1836
|
}
|
|
1774
|
-
recordImport(sessionId, entryPath, seasonPath, mode, tmdbId);
|
|
1837
|
+
recordImport(sessionId, entryPath, seasonPath, mode, tmdbId, "tv");
|
|
1775
1838
|
}
|
|
1776
|
-
spinner_default.succeed(`${dryRun ? "[dry] " : ""}${typeGlyph("tv")} ${typeColor.tv
|
|
1839
|
+
spinner_default.succeed(`${dryRun ? "[dry] " : ""}${typeGlyph("tv")} ${typeColor.tv(`${showFolderName} / ${seasonFolderName} / ${episodeName}`)}${typeTag("tv")}`);
|
|
1777
1840
|
imported++;
|
|
1778
1841
|
continue;
|
|
1779
1842
|
}
|
|
@@ -1834,61 +1897,92 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1834
1897
|
var scan_default = scan;
|
|
1835
1898
|
|
|
1836
1899
|
// src/actions/undo.ts
|
|
1837
|
-
var
|
|
1900
|
+
var import_fs12 = require("fs");
|
|
1838
1901
|
var import_termkit11 = require("termkit");
|
|
1839
1902
|
var undo = async () => {
|
|
1840
1903
|
spinner_default.start();
|
|
1841
|
-
const
|
|
1842
|
-
|
|
1904
|
+
const renameRecords = getLastSession();
|
|
1905
|
+
const importRecords = getLastImportSession();
|
|
1906
|
+
if (renameRecords.length === 0 && importRecords.length === 0) {
|
|
1843
1907
|
spinner_default.info("nothing to undo");
|
|
1844
1908
|
spinner_default.stop();
|
|
1845
1909
|
return;
|
|
1846
1910
|
}
|
|
1911
|
+
const useImports = importRecords.length > 0 && (renameRecords.length === 0 || importRecords[0].sessionId > renameRecords[0].sessionId);
|
|
1912
|
+
if (!useImports) {
|
|
1913
|
+
let undone2 = 0;
|
|
1914
|
+
for (const record of renameRecords) {
|
|
1915
|
+
(0, import_fs12.renameSync)(record.newPath, record.oldPath);
|
|
1916
|
+
spinner_default.succeed(`${import_termkit11.Color.green.encoder(record.newPath)} \u2192 ${import_termkit11.Color.white.encoder(record.oldPath)}`);
|
|
1917
|
+
undone2++;
|
|
1918
|
+
}
|
|
1919
|
+
deleteSession(renameRecords[0].sessionId);
|
|
1920
|
+
spinner_default.succeed(`undid ${undone2} renames`);
|
|
1921
|
+
spinner_default.stop();
|
|
1922
|
+
return;
|
|
1923
|
+
}
|
|
1847
1924
|
let undone = 0;
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1925
|
+
let skipped = 0;
|
|
1926
|
+
for (const record of importRecords) {
|
|
1927
|
+
if (record.mode !== "move") {
|
|
1928
|
+
spinner_default.info(`skipped ${record.destinationPath} (${record.mode} \u2014 source file unchanged)`);
|
|
1929
|
+
skipped++;
|
|
1930
|
+
continue;
|
|
1931
|
+
}
|
|
1932
|
+
if (record.type === "tv") {
|
|
1933
|
+
spinner_default.info(`skipped TV import \u2014 season folder cannot be cleanly reversed: ${record.destinationPath}`);
|
|
1934
|
+
skipped++;
|
|
1935
|
+
continue;
|
|
1936
|
+
}
|
|
1937
|
+
if (!(0, import_fs12.existsSync)(record.destinationPath)) {
|
|
1938
|
+
spinner_default.info(`skipped \u2014 destination no longer exists: ${record.destinationPath}`);
|
|
1939
|
+
skipped++;
|
|
1940
|
+
continue;
|
|
1941
|
+
}
|
|
1942
|
+
(0, import_fs12.renameSync)(record.destinationPath, record.sourcePath);
|
|
1943
|
+
spinner_default.succeed(`${import_termkit11.Color.green.encoder(record.destinationPath)} \u2192 ${import_termkit11.Color.white.encoder(record.sourcePath)}`);
|
|
1851
1944
|
undone++;
|
|
1852
1945
|
}
|
|
1853
|
-
|
|
1854
|
-
spinner_default.succeed(`undid ${undone}
|
|
1946
|
+
deleteImportSession(importRecords[0].sessionId);
|
|
1947
|
+
if (undone > 0) spinner_default.succeed(`undid ${undone} import${undone !== 1 ? "s" : ""}`);
|
|
1948
|
+
if (skipped > 0) spinner_default.info(`skipped ${skipped} item${skipped !== 1 ? "s" : ""} (TV or non-move mode)`);
|
|
1855
1949
|
spinner_default.stop();
|
|
1856
1950
|
};
|
|
1857
1951
|
var undo_default = undo;
|
|
1858
1952
|
|
|
1859
1953
|
// src/actions/watch.ts
|
|
1860
1954
|
var import_chokidar = __toESM(require("chokidar"));
|
|
1861
|
-
var
|
|
1862
|
-
var
|
|
1955
|
+
var import_fs13 = require("fs");
|
|
1956
|
+
var import_path13 = require("path");
|
|
1863
1957
|
var import_termkit12 = require("termkit");
|
|
1864
1958
|
var sameDev2 = (a, b) => {
|
|
1865
1959
|
try {
|
|
1866
1960
|
let bExisting = b;
|
|
1867
|
-
while (!(0,
|
|
1868
|
-
return (0,
|
|
1961
|
+
while (!(0, import_fs13.existsSync)(bExisting)) bExisting = (0, import_path13.dirname)(bExisting);
|
|
1962
|
+
return (0, import_fs13.statSync)(a).dev === (0, import_fs13.statSync)(bExisting).dev;
|
|
1869
1963
|
} catch {
|
|
1870
1964
|
return false;
|
|
1871
1965
|
}
|
|
1872
1966
|
};
|
|
1873
1967
|
var moveItem = (src, dest) => {
|
|
1874
1968
|
if (sameDev2(src, dest)) {
|
|
1875
|
-
(0,
|
|
1969
|
+
(0, import_fs13.renameSync)(src, dest);
|
|
1876
1970
|
} else {
|
|
1877
|
-
(0,
|
|
1878
|
-
(0,
|
|
1971
|
+
(0, import_fs13.cpSync)(src, dest, { recursive: true });
|
|
1972
|
+
(0, import_fs13.rmSync)(src, { recursive: true, force: true });
|
|
1879
1973
|
}
|
|
1880
1974
|
};
|
|
1881
|
-
var findVideo2 = (dir) => (0,
|
|
1975
|
+
var findVideo2 = (dir) => (0, import_fs13.readdirSync)(dir).find((f) => {
|
|
1882
1976
|
const ext = f.match(/([^.]+$)/)?.[0];
|
|
1883
1977
|
return ext && videoExtensions_default.includes(ext);
|
|
1884
1978
|
}) ?? null;
|
|
1885
|
-
var containsBook2 = (dir, depth = 2) => (0,
|
|
1979
|
+
var containsBook2 = (dir, depth = 2) => (0, import_fs13.readdirSync)(dir).some((f) => {
|
|
1886
1980
|
const ext = f.match(/([^.]+$)/)?.[0];
|
|
1887
1981
|
if (ext && bookExtensions_default.includes(ext)) return true;
|
|
1888
1982
|
if (depth > 1) {
|
|
1889
1983
|
try {
|
|
1890
|
-
const sub = (0,
|
|
1891
|
-
if ((0,
|
|
1984
|
+
const sub = (0, import_path13.resolve)(dir, f);
|
|
1985
|
+
if ((0, import_fs13.lstatSync)(sub).isDirectory()) return containsBook2(sub, depth - 1);
|
|
1892
1986
|
} catch {
|
|
1893
1987
|
}
|
|
1894
1988
|
}
|
|
@@ -1899,26 +1993,26 @@ var isSeasonDirName2 = (name) => !isTvEpisodeName2(name) && /(?:^|[.\s_-])(?:sea
|
|
|
1899
1993
|
var expandWatchPath = (p) => {
|
|
1900
1994
|
let isDir;
|
|
1901
1995
|
try {
|
|
1902
|
-
isDir = (0,
|
|
1996
|
+
isDir = (0, import_fs13.lstatSync)(p).isDirectory();
|
|
1903
1997
|
} catch {
|
|
1904
1998
|
return [p];
|
|
1905
1999
|
}
|
|
1906
2000
|
if (!isDir) return [p];
|
|
1907
|
-
const name = (0,
|
|
2001
|
+
const name = (0, import_path13.basename)(p);
|
|
1908
2002
|
if (isTvEpisodeName2(name) || /(?<=\[).+?(?=\])/.test(name) || /^[A-Z]{4}\d{5}/i.test(name)) return [p];
|
|
1909
2003
|
let children;
|
|
1910
2004
|
try {
|
|
1911
|
-
children = (0,
|
|
2005
|
+
children = (0, import_fs13.readdirSync)(p);
|
|
1912
2006
|
} catch {
|
|
1913
2007
|
return [p];
|
|
1914
2008
|
}
|
|
1915
2009
|
if (children.some((c) => isTvEpisodeName2(c))) {
|
|
1916
2010
|
const entries = [];
|
|
1917
2011
|
for (const child of children) {
|
|
1918
|
-
const cp = (0,
|
|
2012
|
+
const cp = (0, import_path13.resolve)(p, child);
|
|
1919
2013
|
let cd;
|
|
1920
2014
|
try {
|
|
1921
|
-
cd = (0,
|
|
2015
|
+
cd = (0, import_fs13.lstatSync)(cp).isDirectory();
|
|
1922
2016
|
} catch {
|
|
1923
2017
|
continue;
|
|
1924
2018
|
}
|
|
@@ -1930,7 +2024,7 @@ var expandWatchPath = (p) => {
|
|
|
1930
2024
|
}
|
|
1931
2025
|
const seasonDirs = children.filter((c) => {
|
|
1932
2026
|
try {
|
|
1933
|
-
return isSeasonDirName2(c) && (0,
|
|
2027
|
+
return isSeasonDirName2(c) && (0, import_fs13.lstatSync)((0, import_path13.resolve)(p, c)).isDirectory();
|
|
1934
2028
|
} catch {
|
|
1935
2029
|
return false;
|
|
1936
2030
|
}
|
|
@@ -1938,18 +2032,18 @@ var expandWatchPath = (p) => {
|
|
|
1938
2032
|
if (seasonDirs.length > 0) {
|
|
1939
2033
|
const entries = [];
|
|
1940
2034
|
for (const sd of seasonDirs) {
|
|
1941
|
-
const sp = (0,
|
|
2035
|
+
const sp = (0, import_path13.resolve)(p, sd);
|
|
1942
2036
|
let sc;
|
|
1943
2037
|
try {
|
|
1944
|
-
sc = (0,
|
|
2038
|
+
sc = (0, import_fs13.readdirSync)(sp);
|
|
1945
2039
|
} catch {
|
|
1946
2040
|
continue;
|
|
1947
2041
|
}
|
|
1948
2042
|
for (const child of sc) {
|
|
1949
|
-
const cp = (0,
|
|
2043
|
+
const cp = (0, import_path13.resolve)(sp, child);
|
|
1950
2044
|
let cd;
|
|
1951
2045
|
try {
|
|
1952
|
-
cd = (0,
|
|
2046
|
+
cd = (0, import_fs13.lstatSync)(cp).isDirectory();
|
|
1953
2047
|
} catch {
|
|
1954
2048
|
continue;
|
|
1955
2049
|
}
|
|
@@ -1963,10 +2057,10 @@ var expandWatchPath = (p) => {
|
|
|
1963
2057
|
return [p];
|
|
1964
2058
|
};
|
|
1965
2059
|
var findSeasonFolder2 = (showPath, season) => {
|
|
1966
|
-
if (!(0,
|
|
1967
|
-
const folders = (0,
|
|
2060
|
+
if (!(0, import_fs13.existsSync)(showPath)) return null;
|
|
2061
|
+
const folders = (0, import_fs13.readdirSync)(showPath).filter((f) => {
|
|
1968
2062
|
try {
|
|
1969
|
-
return (0,
|
|
2063
|
+
return (0, import_fs13.lstatSync)((0, import_path13.resolve)(showPath, f)).isDirectory();
|
|
1970
2064
|
} catch {
|
|
1971
2065
|
return false;
|
|
1972
2066
|
}
|
|
@@ -1979,10 +2073,10 @@ var findSeasonFolder2 = (showPath, season) => {
|
|
|
1979
2073
|
var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
1980
2074
|
const config = getConfig();
|
|
1981
2075
|
const sessionId = (/* @__PURE__ */ new Date()).toISOString();
|
|
1982
|
-
const entry = (0,
|
|
2076
|
+
const entry = (0, import_path13.basename)(entryPath);
|
|
1983
2077
|
const movieFormat = config.format?.movie ?? DEFAULT_MOVIE_FORMAT;
|
|
1984
2078
|
const seasonFormat = config.format?.season ?? DEFAULT_SEASON_FORMAT;
|
|
1985
|
-
const isDir = (0,
|
|
2079
|
+
const isDir = (0, import_fs13.lstatSync)(entryPath).isDirectory();
|
|
1986
2080
|
const ext = entry.match(/([^.]+$)/)?.[0];
|
|
1987
2081
|
const isVideo = !isDir && ext && videoExtensions_default.includes(ext);
|
|
1988
2082
|
const isBook = !isDir && ext && bookExtensions_default.includes(ext);
|
|
@@ -2008,34 +2102,34 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2008
2102
|
const id = entry.split("-")[0];
|
|
2009
2103
|
if (!nameMatch || !id) return;
|
|
2010
2104
|
const destName = `${nameMatch[0]} [${id}]`;
|
|
2011
|
-
const destPath = (0,
|
|
2012
|
-
if ((0,
|
|
2105
|
+
const destPath = (0, import_path13.resolve)(destRoot, destName);
|
|
2106
|
+
if ((0, import_fs13.existsSync)(destPath)) {
|
|
2013
2107
|
spinner_default.warn(`already exists: ${destName}`);
|
|
2014
2108
|
return;
|
|
2015
2109
|
}
|
|
2016
2110
|
moveItem(entryPath, destPath);
|
|
2017
|
-
recordImport(sessionId, entryPath, destPath, "move");
|
|
2111
|
+
recordImport(sessionId, entryPath, destPath, "move", void 0, "ps3");
|
|
2018
2112
|
spinner_default.succeed(`imported ${import_termkit12.Color.green.encoder(destName)}`);
|
|
2019
2113
|
return;
|
|
2020
2114
|
}
|
|
2021
2115
|
if (detectedType === "book") {
|
|
2022
|
-
const destPath = (0,
|
|
2023
|
-
if ((0,
|
|
2116
|
+
const destPath = (0, import_path13.resolve)(destRoot, entry);
|
|
2117
|
+
if ((0, import_fs13.existsSync)(destPath)) {
|
|
2024
2118
|
spinner_default.warn(`already exists: ${entry}`);
|
|
2025
2119
|
return;
|
|
2026
2120
|
}
|
|
2027
2121
|
if (isDir || isBookDir) {
|
|
2028
2122
|
moveItem(entryPath, destPath);
|
|
2029
2123
|
} else {
|
|
2030
|
-
(0,
|
|
2124
|
+
(0, import_fs13.mkdirSync)(destRoot, { recursive: true });
|
|
2031
2125
|
if (sameDev2(entryPath, destRoot)) {
|
|
2032
|
-
(0,
|
|
2126
|
+
(0, import_fs13.renameSync)(entryPath, destPath);
|
|
2033
2127
|
} else {
|
|
2034
|
-
(0,
|
|
2035
|
-
(0,
|
|
2128
|
+
(0, import_fs13.cpSync)(entryPath, destPath);
|
|
2129
|
+
(0, import_fs13.rmSync)(entryPath);
|
|
2036
2130
|
}
|
|
2037
2131
|
}
|
|
2038
|
-
recordImport(sessionId, entryPath, destPath, "move");
|
|
2132
|
+
recordImport(sessionId, entryPath, destPath, "move", void 0, "book");
|
|
2039
2133
|
spinner_default.succeed(`imported ${import_termkit12.Color.green.encoder(entry)}`);
|
|
2040
2134
|
return;
|
|
2041
2135
|
}
|
|
@@ -2057,14 +2151,14 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2057
2151
|
showFolderName = showPath.split("/").pop() ?? registeredShow.path;
|
|
2058
2152
|
} else if (auto) {
|
|
2059
2153
|
showFolderName = formatMovieName(movieFormat, parsed.title, parsed.year);
|
|
2060
|
-
showPath = (0,
|
|
2154
|
+
showPath = (0, import_path13.resolve)(destRoot, showFolderName);
|
|
2061
2155
|
upsertShow(showPath, null, parsed.title);
|
|
2062
2156
|
} else {
|
|
2063
2157
|
if (isVerbose()) spinner_default.info(`not registered, skipped: ${parsed.title} \u2014 run: reelsort add "${parsed.title}"`);
|
|
2064
2158
|
return;
|
|
2065
2159
|
}
|
|
2066
2160
|
const seasonFolderName = findSeasonFolder2(showPath, parsed.season) ?? formatSeasonFolder(seasonFormat, parsed.season);
|
|
2067
|
-
const seasonPath = (0,
|
|
2161
|
+
const seasonPath = (0, import_path13.resolve)(showPath, seasonFolderName);
|
|
2068
2162
|
const videoFile2 = isDir ? findVideo2(entryPath) : entry;
|
|
2069
2163
|
if (!videoFile2) {
|
|
2070
2164
|
if (isVerbose()) spinner_default.info(`no video found in: ${entry}`);
|
|
@@ -2074,48 +2168,48 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2074
2168
|
const tmdbEpisodeName = registeredShow?.tmdbId && config.tmdbApiKey ? await getEpisodeName(registeredShow.tmdbId, parsed.season, parsed.episode ?? 1, config.tmdbApiKey) : null;
|
|
2075
2169
|
const episodeName = formatEpisode(parsed.season, parsed.episode ?? 1, config.format?.episode, false, parsed.title, tmdbEpisodeName ?? void 0);
|
|
2076
2170
|
const destVideoName2 = `${episodeName}.${videoExt2}`;
|
|
2077
|
-
const destVideoPath = (0,
|
|
2078
|
-
const videoSourcePath2 = isDir ? (0,
|
|
2079
|
-
if ((0,
|
|
2171
|
+
const destVideoPath = (0, import_path13.resolve)(seasonPath, destVideoName2);
|
|
2172
|
+
const videoSourcePath2 = isDir ? (0, import_path13.resolve)(entryPath, videoFile2) : entryPath;
|
|
2173
|
+
if ((0, import_fs13.existsSync)(destVideoPath)) {
|
|
2080
2174
|
spinner_default.warn(`already exists: ${episodeName}`);
|
|
2081
2175
|
return;
|
|
2082
2176
|
}
|
|
2083
|
-
const dirFiles2 = isDir ? (0,
|
|
2177
|
+
const dirFiles2 = isDir ? (0, import_fs13.readdirSync)(entryPath) : [];
|
|
2084
2178
|
const subtitle2 = isDir ? findSubtitle(dirFiles2, language) : null;
|
|
2085
2179
|
const subtitleExt2 = subtitle2?.match(/([^.]+$)/)?.[0];
|
|
2086
|
-
const subtitleSourcePath2 = subtitle2 ? (0,
|
|
2180
|
+
const subtitleSourcePath2 = subtitle2 ? (0, import_path13.resolve)(entryPath, subtitle2) : null;
|
|
2087
2181
|
const destSubtitleName2 = subtitle2 && subtitleExt2 ? `${episodeName}.${subtitleExt2}` : null;
|
|
2088
|
-
(0,
|
|
2182
|
+
(0, import_fs13.mkdirSync)(seasonPath, { recursive: true });
|
|
2089
2183
|
let mode = "move";
|
|
2090
2184
|
if (useHardlink) {
|
|
2091
2185
|
try {
|
|
2092
2186
|
if (!sameDev2(videoSourcePath2, seasonPath)) throw new Error("cross-filesystem");
|
|
2093
|
-
(0,
|
|
2187
|
+
(0, import_fs13.linkSync)(videoSourcePath2, destVideoPath);
|
|
2094
2188
|
mode = "hardlink";
|
|
2095
2189
|
} catch {
|
|
2096
2190
|
spinner_default.warn(`hardlink unavailable \u2014 copying instead: ${episodeName}`);
|
|
2097
|
-
(0,
|
|
2191
|
+
(0, import_fs13.cpSync)(videoSourcePath2, destVideoPath);
|
|
2098
2192
|
mode = "copy";
|
|
2099
2193
|
}
|
|
2100
|
-
if (subtitleSourcePath2 && destSubtitleName2) (0,
|
|
2194
|
+
if (subtitleSourcePath2 && destSubtitleName2) (0, import_fs13.cpSync)(subtitleSourcePath2, (0, import_path13.resolve)(seasonPath, destSubtitleName2));
|
|
2101
2195
|
} else {
|
|
2102
2196
|
if (sameDev2(videoSourcePath2, seasonPath)) {
|
|
2103
|
-
(0,
|
|
2197
|
+
(0, import_fs13.renameSync)(videoSourcePath2, destVideoPath);
|
|
2104
2198
|
} else {
|
|
2105
|
-
(0,
|
|
2106
|
-
(0,
|
|
2199
|
+
(0, import_fs13.cpSync)(videoSourcePath2, destVideoPath);
|
|
2200
|
+
(0, import_fs13.rmSync)(videoSourcePath2);
|
|
2107
2201
|
}
|
|
2108
|
-
if (subtitleSourcePath2 && destSubtitleName2) (0,
|
|
2109
|
-
if (isDir) (0,
|
|
2202
|
+
if (subtitleSourcePath2 && destSubtitleName2) (0, import_fs13.renameSync)(subtitleSourcePath2, (0, import_path13.resolve)(seasonPath, destSubtitleName2));
|
|
2203
|
+
if (isDir) (0, import_fs13.rmSync)(entryPath, { recursive: true, force: true });
|
|
2110
2204
|
}
|
|
2111
|
-
recordImport(sessionId, entryPath, seasonPath, mode);
|
|
2205
|
+
recordImport(sessionId, entryPath, seasonPath, mode, void 0, "tv");
|
|
2112
2206
|
spinner_default.succeed(`imported ${import_termkit12.Color.green.encoder(`${showFolderName} / ${seasonFolderName} / ${episodeName}`)}`);
|
|
2113
2207
|
return;
|
|
2114
2208
|
}
|
|
2115
2209
|
const edition = detectEdition(entry);
|
|
2116
2210
|
const folderName = formatMovieName(movieFormat, parsed.title, parsed.year, edition);
|
|
2117
|
-
const destFolder = (0,
|
|
2118
|
-
if ((0,
|
|
2211
|
+
const destFolder = (0, import_path13.resolve)(destRoot, folderName);
|
|
2212
|
+
if ((0, import_fs13.existsSync)(destFolder)) {
|
|
2119
2213
|
spinner_default.warn(`already exists: ${folderName}`);
|
|
2120
2214
|
return;
|
|
2121
2215
|
}
|
|
@@ -2126,45 +2220,45 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2126
2220
|
}
|
|
2127
2221
|
const videoExt = videoFile.match(/([^.]+$)/)?.[0];
|
|
2128
2222
|
const destVideoName = `${folderName}.${videoExt}`;
|
|
2129
|
-
const videoSourcePath = isDir ? (0,
|
|
2130
|
-
const dirFiles = isDir ? (0,
|
|
2223
|
+
const videoSourcePath = isDir ? (0, import_path13.resolve)(entryPath, videoFile) : entryPath;
|
|
2224
|
+
const dirFiles = isDir ? (0, import_fs13.readdirSync)(entryPath) : [];
|
|
2131
2225
|
const subtitle = isDir ? findSubtitle(dirFiles, language) : null;
|
|
2132
2226
|
const subtitleExt = subtitle?.match(/([^.]+$)/)?.[0];
|
|
2133
|
-
const subtitleSourcePath = subtitle ? (0,
|
|
2227
|
+
const subtitleSourcePath = subtitle ? (0, import_path13.resolve)(entryPath, subtitle) : null;
|
|
2134
2228
|
const destSubtitleName = subtitle && subtitleExt ? `${folderName}.${subtitleExt}` : null;
|
|
2135
2229
|
if (useHardlink) {
|
|
2136
|
-
(0,
|
|
2137
|
-
const destVideoPath = (0,
|
|
2230
|
+
(0, import_fs13.mkdirSync)(destFolder, { recursive: true });
|
|
2231
|
+
const destVideoPath = (0, import_path13.resolve)(destFolder, destVideoName);
|
|
2138
2232
|
let mode;
|
|
2139
2233
|
try {
|
|
2140
2234
|
if (!sameDev2(videoSourcePath, destRoot)) throw new Error("cross-filesystem");
|
|
2141
|
-
(0,
|
|
2235
|
+
(0, import_fs13.linkSync)(videoSourcePath, destVideoPath);
|
|
2142
2236
|
mode = "hardlink";
|
|
2143
2237
|
} catch {
|
|
2144
2238
|
spinner_default.warn(`hardlink unavailable \u2014 copying instead: ${folderName}`);
|
|
2145
|
-
(0,
|
|
2239
|
+
(0, import_fs13.cpSync)(videoSourcePath, destVideoPath);
|
|
2146
2240
|
mode = "copy";
|
|
2147
2241
|
}
|
|
2148
|
-
if (subtitleSourcePath && destSubtitleName) (0,
|
|
2149
|
-
recordImport(sessionId, entryPath, destFolder, mode);
|
|
2242
|
+
if (subtitleSourcePath && destSubtitleName) (0, import_fs13.cpSync)(subtitleSourcePath, (0, import_path13.resolve)(destFolder, destSubtitleName));
|
|
2243
|
+
recordImport(sessionId, entryPath, destFolder, mode, void 0, "movie");
|
|
2150
2244
|
} else {
|
|
2151
2245
|
if (isDir) {
|
|
2152
2246
|
const keep = new Set([videoFile, subtitle].filter(Boolean));
|
|
2153
|
-
for (const f of dirFiles.filter((f2) => !keep.has(f2))) (0,
|
|
2154
|
-
(0,
|
|
2155
|
-
if (subtitleSourcePath && destSubtitleName) (0,
|
|
2247
|
+
for (const f of dirFiles.filter((f2) => !keep.has(f2))) (0, import_fs13.rmSync)((0, import_path13.resolve)(entryPath, f), { recursive: true, force: true });
|
|
2248
|
+
(0, import_fs13.renameSync)(videoSourcePath, (0, import_path13.resolve)(entryPath, destVideoName));
|
|
2249
|
+
if (subtitleSourcePath && destSubtitleName) (0, import_fs13.renameSync)(subtitleSourcePath, (0, import_path13.resolve)(entryPath, destSubtitleName));
|
|
2156
2250
|
moveItem(entryPath, destFolder);
|
|
2157
2251
|
} else {
|
|
2158
|
-
(0,
|
|
2159
|
-
const destVideoPath = (0,
|
|
2252
|
+
(0, import_fs13.mkdirSync)(destFolder, { recursive: true });
|
|
2253
|
+
const destVideoPath = (0, import_path13.resolve)(destFolder, destVideoName);
|
|
2160
2254
|
if (sameDev2(videoSourcePath, destRoot)) {
|
|
2161
|
-
(0,
|
|
2255
|
+
(0, import_fs13.renameSync)(videoSourcePath, destVideoPath);
|
|
2162
2256
|
} else {
|
|
2163
|
-
(0,
|
|
2164
|
-
(0,
|
|
2257
|
+
(0, import_fs13.cpSync)(videoSourcePath, destVideoPath);
|
|
2258
|
+
(0, import_fs13.rmSync)(videoSourcePath);
|
|
2165
2259
|
}
|
|
2166
2260
|
}
|
|
2167
|
-
recordImport(sessionId, entryPath, destFolder, "move");
|
|
2261
|
+
recordImport(sessionId, entryPath, destFolder, "move", void 0, "movie");
|
|
2168
2262
|
}
|
|
2169
2263
|
spinner_default.succeed(`imported ${import_termkit12.Color.green.encoder(folderName)}`);
|
|
2170
2264
|
};
|
|
@@ -2213,6 +2307,7 @@ var watch_default = watch;
|
|
|
2213
2307
|
configSet,
|
|
2214
2308
|
configShow,
|
|
2215
2309
|
deleteImport,
|
|
2310
|
+
deleteImportSession,
|
|
2216
2311
|
deleteSession,
|
|
2217
2312
|
destAdd,
|
|
2218
2313
|
destRemove,
|
|
@@ -2225,6 +2320,7 @@ var watch_default = watch;
|
|
|
2225
2320
|
getConfig,
|
|
2226
2321
|
getHistory,
|
|
2227
2322
|
getImportByDest,
|
|
2323
|
+
getLastImportSession,
|
|
2228
2324
|
getLastSession,
|
|
2229
2325
|
getMediaInfo,
|
|
2230
2326
|
history,
|