diff --git a/FlowHelpers/1.0.0/fileUtils.js b/FlowHelpers/1.0.0/fileUtils.js index f2cfcee..5fcb922 100644 --- a/FlowHelpers/1.0.0/fileUtils.js +++ b/FlowHelpers/1.0.0/fileUtils.js @@ -1,12 +1,269 @@ -'use strict'; -/** - * Local mock of Tdarr FlowHelpers/1.0.0/fileUtils.js - */ -const fs = require('fs'); -const path = require('path'); - -const moveFile = (src, dest) => fs.renameSync(src, dest); -const copyFile = (src, dest) => fs.copyFileSync(src, dest); -const getFileSize = (fp) => { try { return fs.statSync(fp).size; } catch(_) { return 0; } }; - -module.exports = { moveFile, copyFile, getFileSize }; +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); + return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (g && (g = 0, op[0] && (_ = 0)), _) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.hashFile = exports.getScanTypes = exports.getPluginWorkDir = exports.moveFileAndValidate = exports.getFileSize = exports.getSubStem = exports.getFfType = exports.getFileAbosluteDir = exports.getFileAbsoluteDir = exports.getFileName = exports.getContainer = exports.fileExists = void 0; +var fs_1 = __importStar(require("fs")); +var crypto_1 = __importDefault(require("crypto")); +var fileExists = function (path) { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, fs_1.promises.stat(path).catch(function () { return false; })]; + case 1: return [2 /*return*/, !!(_a.sent())]; + } +}); }); }; +exports.fileExists = fileExists; +var getContainer = function (filePath) { + var parts = filePath.split('.'); + return parts[parts.length - 1]; +}; +exports.getContainer = getContainer; +var getFileName = function (filePath) { + var parts = filePath.split('/'); + var fileNameAndContainer = parts[parts.length - 1]; + var parts2 = fileNameAndContainer.split('.'); + parts2.pop(); + return parts2.join('.'); +}; +exports.getFileName = getFileName; +var getFileAbsoluteDir = function (filePath) { + var parts = filePath.split('/'); + parts.pop(); + return parts.join('/'); +}; +exports.getFileAbsoluteDir = getFileAbsoluteDir; +// backwards compatibility for typo +exports.getFileAbosluteDir = exports.getFileAbsoluteDir; +var getFfType = function (codecType) { return (codecType === 'video' ? 'v' : 'a'); }; +exports.getFfType = getFfType; +var getSubStem = function (_a) { + var inputPathStem = _a.inputPathStem, inputPath = _a.inputPath; + var subStem = inputPath.substring(inputPathStem.length); + var parts = subStem.split('/'); + parts.pop(); + return parts.join('/'); +}; +exports.getSubStem = getSubStem; +var getFileSize = function (file) { return __awaiter(void 0, void 0, void 0, function () { + var stats, size; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, fs_1.promises.stat(file)]; + case 1: + stats = _a.sent(); + size = stats.size; + return [2 /*return*/, size]; + } + }); +}); }; +exports.getFileSize = getFileSize; +var moveFileAndValidate = function (_a) { return __awaiter(void 0, [_a], void 0, function (_b) { + var inputSize, res1, outputSize, err_1, res2, errMessage; + var inputPath = _b.inputPath, outputPath = _b.outputPath, args = _b.args; + return __generator(this, function (_c) { + switch (_c.label) { + case 0: return [4 /*yield*/, (0, exports.getFileSize)(inputPath)]; + case 1: + inputSize = _c.sent(); + args.jobLog("Attempt 1: Moving file from ".concat(inputPath, " to ").concat(outputPath)); + return [4 /*yield*/, new Promise(function (resolve) { + args.deps.gracefulfs.rename(inputPath, outputPath, function (err) { + if (err) { + args.jobLog("Failed to move file from ".concat(inputPath, " to ").concat(outputPath)); + args.jobLog(JSON.stringify(err)); + resolve(false); + } + else { + resolve(true); + } + }); + })]; + case 2: + res1 = _c.sent(); + outputSize = 0; + _c.label = 3; + case 3: + _c.trys.push([3, 5, , 6]); + return [4 /*yield*/, (0, exports.getFileSize)(outputPath)]; + case 4: + outputSize = _c.sent(); + return [3 /*break*/, 6]; + case 5: + err_1 = _c.sent(); + args.jobLog(JSON.stringify(err_1)); + return [3 /*break*/, 6]; + case 6: + if (!(!res1 || inputSize !== outputSize)) return [3 /*break*/, 9]; + if (inputSize !== outputSize) { + args.jobLog("File sizes do not match, input: ".concat(inputSize, " ") + + "does not equal output: ".concat(outputSize)); + } + args.jobLog("Attempt 1 failed: Moving file from ".concat(inputPath, " to ").concat(outputPath)); + args.jobLog("Attempt 2: Moving file from ".concat(inputPath, " to ").concat(outputPath)); + return [4 /*yield*/, new Promise(function (resolve) { + args.deps.mvdir(inputPath, outputPath, { overwrite: true }) + .then(function () { + resolve(true); + }).catch(function (err) { + args.jobLog("Failed to move file from ".concat(inputPath, " to ").concat(outputPath)); + args.jobLog(JSON.stringify(err)); + resolve(false); + }); + })]; + case 7: + res2 = _c.sent(); + return [4 /*yield*/, (0, exports.getFileSize)(outputPath)]; + case 8: + outputSize = _c.sent(); + if (!res2 || inputSize !== outputSize) { + if (inputSize !== outputSize) { + args.jobLog("File sizes do not match, input: ".concat(inputSize, " ") + + "does not equal output: ".concat(outputSize)); + } + errMessage = "Failed to move file from ".concat(inputPath, " to ").concat(outputPath, ", check errors above"); + args.jobLog(errMessage); + throw new Error(errMessage); + } + _c.label = 9; + case 9: return [2 /*return*/]; + } + }); +}); }; +exports.moveFileAndValidate = moveFileAndValidate; +var getPluginWorkDir = function (args) { + var pluginWorkDir = "".concat(args.workDir, "/").concat(new Date().getTime()); + args.deps.fsextra.ensureDirSync(pluginWorkDir); + return pluginWorkDir; +}; +exports.getPluginWorkDir = getPluginWorkDir; +var getScanTypes = function (pluginsTextRaw) { + var scanTypes = { + exifToolScan: true, + mediaInfoScan: false, + closedCaptionScan: false, + }; + var scannerTypes = [ + // needed for frame and duration data for ffmpeg + // { + // type: 'exifToolScan', + // terms: [ + // 'meta', + // ], + // }, + { + type: 'mediaInfoScan', + terms: [ + 'mediaInfo', + ], + }, + { + type: 'closedCaptionScan', + terms: [ + 'hasClosedCaptions', + ], + }, + ]; + var text = pluginsTextRaw.join(''); + scannerTypes.forEach(function (scanner) { + scanner.terms.forEach(function (term) { + if (text.includes(term)) { + scanTypes[scanner.type] = true; + } + }); + }); + return scanTypes; +}; +exports.getScanTypes = getScanTypes; +var hashFile = function (filePath, algorithm) { + if (algorithm === void 0) { algorithm = 'sha256'; } + return new Promise(function (resolve, reject) { + try { + var hash_1 = crypto_1.default.createHash(algorithm); + var stream = fs_1.default.createReadStream(filePath); + stream.on('data', function (data) { + hash_1.update(data); + }); + stream.on('end', function () { + var hashStr = hash_1.digest('hex'); + resolve(hashStr); + }); + stream.on('error', function (error) { + reject(new Error("Error reading file for hashing: ".concat(error.message))); + }); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } + catch (error) { + reject(new Error("Error setting up file hash: ".concat(error.message))); + } + }); +}; +exports.hashFile = hashFile; diff --git a/install.sh b/install.sh index 3ade69c..9fc49b7 100644 --- a/install.sh +++ b/install.sh @@ -92,17 +92,27 @@ download_plugin() { download_flow_helpers() { local dest="${PLUGIN_DIR}/FlowHelpers/1.0.0" - local helpers=("copyWithProgress.js" "cliUtils.js" "fileUtils.js") mkdir -p "$dest" - for helper in "${helpers[@]}"; do - local url="${RAW}/FlowHelpers/1.0.0/${helper}" - if curl -fsSL "$url" -o "${dest}/${helper}"; then - echo " ✓ FlowHelper ${helper} → ${dest}/${helper}" - else - echo " ✗ Failed to download ${helper} from ${url}" - return 1 + + # Restore official Tdarr fileUtils.js if it was previously overwritten by a mock + local official_fu="https://raw.githubusercontent.com/HaveAGitGat/Tdarr_Plugins/master/FlowPlugins/FlowHelpers/1.0.0/fileUtils.js" + if [[ -f "${dest}/fileUtils.js" ]]; then + if grep -q "Local mock of Tdarr" "${dest}/fileUtils.js" || ! grep -q "getFileAbsoluteDir" "${dest}/fileUtils.js"; then + echo " 🔄 Restoring official Tdarr fileUtils.js..." + curl -fsSL "$official_fu" -o "${dest}/fileUtils.js" || true fi - done + else + curl -fsSL "$official_fu" -o "${dest}/fileUtils.js" || true + fi + + # Download our custom copyWithProgress helper + local url="${RAW}/FlowHelpers/1.0.0/copyWithProgress.js" + if curl -fsSL "$url" -o "${dest}/copyWithProgress.js"; then + echo " ✓ FlowHelper copyWithProgress.js → ${dest}/copyWithProgress.js" + else + echo " ✗ Failed to download copyWithProgress.js from ${url}" + return 1 + fi } clean_deprecated_plugins() {