diff --git a/FlowPlugins/CommunityFlowPlugins/file/moveToDirectory/2.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/file/moveToDirectory/2.0.0/index.js index 78954bd..4a33e89 100644 --- a/FlowPlugins/CommunityFlowPlugins/file/moveToDirectory/2.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/file/moveToDirectory/2.0.0/index.js @@ -40,6 +40,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) { }; Object.defineProperty(exports, "__esModule", { value: true }); exports.plugin = exports.details = void 0; +var fileMoveOrCopy_1 = __importDefault(require("../../../../FlowHelpers/1.0.0/fileMoveOrCopy")); var fileUtils_1 = require("../../../../FlowHelpers/1.0.0/fileUtils"); var normJoinPath_1 = __importDefault(require("../../../../FlowHelpers/1.0.0/normJoinPath")); /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ @@ -137,9 +138,10 @@ var plugin = function (args) { return __awaiter(void 0, void 0, void 0, function }]; } args.deps.fsextra.ensureDirSync(outputPath); - return [4 /*yield*/, (0, fileUtils_1.moveFileAndValidate)({ - inputPath: args.inputFileObj._id, - outputPath: ouputFilePath, + return [4 /*yield*/, (0, fileMoveOrCopy_1.default)({ + operation: 'move', + sourcePath: args.inputFileObj._id, + destinationPath: ouputFilePath, args: args, })]; case 1: diff --git a/FlowPlugins/CommunityFlowPlugins/file/moveToOriginalDirectory/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/file/moveToOriginalDirectory/1.0.0/index.js index 06e3df4..90d729a 100644 --- a/FlowPlugins/CommunityFlowPlugins/file/moveToOriginalDirectory/1.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/file/moveToOriginalDirectory/1.0.0/index.js @@ -35,8 +35,12 @@ var __generator = (this && this.__generator) || function (thisArg, body) { 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.plugin = exports.details = void 0; +var fileMoveOrCopy_1 = __importDefault(require("../../../../FlowHelpers/1.0.0/fileMoveOrCopy")); var fileUtils_1 = require("../../../../FlowHelpers/1.0.0/fileUtils"); /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ var details = function () { return ({ @@ -83,9 +87,10 @@ var plugin = function (args) { return __awaiter(void 0, void 0, void 0, function variables: args.variables, }]; } - return [4 /*yield*/, (0, fileUtils_1.moveFileAndValidate)({ - inputPath: args.inputFileObj._id, - outputPath: ouputFilePath, + return [4 /*yield*/, (0, fileMoveOrCopy_1.default)({ + operation: 'move', + sourcePath: args.inputFileObj._id, + destinationPath: ouputFilePath, args: args, })]; case 1: diff --git a/FlowPlugins/CommunityFlowPlugins/file/renameFile/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/file/renameFile/1.0.0/index.js index 4fe65ed..93cf093 100644 --- a/FlowPlugins/CommunityFlowPlugins/file/renameFile/1.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/file/renameFile/1.0.0/index.js @@ -35,8 +35,12 @@ var __generator = (this && this.__generator) || function (thisArg, body) { 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.plugin = exports.details = void 0; +var fileMoveOrCopy_1 = __importDefault(require("../../../../FlowHelpers/1.0.0/fileMoveOrCopy")); var fileUtils_1 = require("../../../../FlowHelpers/1.0.0/fileUtils"); /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ var details = function () { return ({ @@ -97,9 +101,10 @@ var plugin = function (args) { return __awaiter(void 0, void 0, void 0, function variables: args.variables, }]; } - return [4 /*yield*/, (0, fileUtils_1.moveFileAndValidate)({ - inputPath: args.inputFileObj._id, - outputPath: newPath, + return [4 /*yield*/, (0, fileMoveOrCopy_1.default)({ + operation: 'move', + sourcePath: args.inputFileObj._id, + destinationPath: newPath, args: args, })]; case 1: diff --git a/FlowPlugins/CommunityFlowPlugins/file/replaceOriginalFile/1.0.0/index.js b/FlowPlugins/CommunityFlowPlugins/file/replaceOriginalFile/1.0.0/index.js index 4ec9bff..f27c9db 100644 --- a/FlowPlugins/CommunityFlowPlugins/file/replaceOriginalFile/1.0.0/index.js +++ b/FlowPlugins/CommunityFlowPlugins/file/replaceOriginalFile/1.0.0/index.js @@ -35,8 +35,12 @@ var __generator = (this && this.__generator) || function (thisArg, body) { 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.plugin = exports.details = void 0; +var fileMoveOrCopy_1 = __importDefault(require("../../../../FlowHelpers/1.0.0/fileMoveOrCopy")); var fileUtils_1 = require("../../../../FlowHelpers/1.0.0/fileUtils"); /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ var details = function () { return ({ @@ -94,9 +98,10 @@ var plugin = function (args) { return __awaiter(void 0, void 0, void 0, function return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 2000); })]; case 1: _a.sent(); - return [4 /*yield*/, (0, fileUtils_1.moveFileAndValidate)({ - inputPath: currentPath, - outputPath: newPathTmp, + return [4 /*yield*/, (0, fileMoveOrCopy_1.default)({ + operation: 'move', + sourcePath: currentPath, + destinationPath: newPathTmp, args: args, })]; case 2: @@ -110,9 +115,10 @@ var plugin = function (args) { return __awaiter(void 0, void 0, void 0, function return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 2000); })]; case 3: _a.sent(); - return [4 /*yield*/, (0, fileUtils_1.moveFileAndValidate)({ - inputPath: newPathTmp, - outputPath: newPath, + return [4 /*yield*/, (0, fileMoveOrCopy_1.default)({ + operation: 'move', + sourcePath: newPathTmp, + destinationPath: newPath, args: args, })]; case 4: diff --git a/FlowPlugins/FlowHelpers/1.0.0/fileMoveOrCopy.js b/FlowPlugins/FlowHelpers/1.0.0/fileMoveOrCopy.js new file mode 100644 index 0000000..529a028 --- /dev/null +++ b/FlowPlugins/FlowHelpers/1.0.0/fileMoveOrCopy.js @@ -0,0 +1,328 @@ +"use strict"; +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; + return g = { next: verb(0), "throw": verb(1), "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 }; + } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var fs_1 = require("fs"); +var fileUtils_1 = require("./fileUtils"); +var getSizeBytes = function (fPath) { return __awaiter(void 0, void 0, void 0, function () { + var size, err_1; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + size = 0; + _a.label = 1; + case 1: + _a.trys.push([1, 3, , 4]); + return [4 /*yield*/, (0, fileUtils_1.getFileSize)(fPath)]; + case 2: + size = _a.sent(); + return [3 /*break*/, 4]; + case 3: + err_1 = _a.sent(); + return [3 /*break*/, 4]; + case 4: return [2 /*return*/, size]; + } + }); +}); }; +var compareOldNew = function (_a) { + var sourceFileSize = _a.sourceFileSize, destinationSize = _a.destinationSize, args = _a.args; + if (destinationSize !== sourceFileSize) { + args.jobLog("After move/copy, destination file of size ".concat(destinationSize, " does not match") + + " cache file of size ".concat(sourceFileSize)); + } + else { + args.jobLog("After move/copy, destination file of size ".concat(destinationSize, " does match") + + " cache file of size ".concat(sourceFileSize)); + } +}; +var tryMove = function (_a) { + var sourcePath = _a.sourcePath, destinationPath = _a.destinationPath, sourceFileSize = _a.sourceFileSize, args = _a.args; + return __awaiter(void 0, void 0, void 0, function () { + var error, err_2, destinationSize; + return __generator(this, function (_b) { + switch (_b.label) { + case 0: + args.jobLog("Attempting move from ".concat(sourcePath, " to ").concat(destinationPath, ", method 1")); + error = false; + _b.label = 1; + case 1: + _b.trys.push([1, 3, , 4]); + return [4 /*yield*/, fs_1.promises.rename(sourcePath, destinationPath)]; + case 2: + _b.sent(); + return [3 /*break*/, 4]; + case 3: + err_2 = _b.sent(); + error = true; + args.jobLog("File move error: ".concat(JSON.stringify(err_2))); + return [3 /*break*/, 4]; + case 4: return [4 /*yield*/, getSizeBytes(destinationPath)]; + case 5: + destinationSize = _b.sent(); + compareOldNew({ + sourceFileSize: sourceFileSize, + destinationSize: destinationSize, + args: args, + }); + if (error || destinationSize !== sourceFileSize) { + return [2 /*return*/, false]; + } + return [2 /*return*/, true]; + } + }); + }); +}; +var tryMvdir = function (_a) { + var sourcePath = _a.sourcePath, destinationPath = _a.destinationPath, sourceFileSize = _a.sourceFileSize, args = _a.args; + return __awaiter(void 0, void 0, void 0, function () { + var error, destinationSize; + return __generator(this, function (_b) { + switch (_b.label) { + case 0: + args.jobLog("Attempting move from ".concat(sourcePath, " to ").concat(destinationPath, ", method 2")); + error = false; + return [4 /*yield*/, new Promise(function (resolve) { + // fs-extra and move-file don't work when destination is on windows root of drive + // mvdir will try to move else fall back to copy/unlink + // potential bug on unraid + args.deps.mvdir(sourcePath, destinationPath, { overwrite: true }) + .then(function () { + resolve(true); + }).catch(function (err) { + error = true; + args.jobLog("File move error: ".concat(err)); + resolve(err); + }); + })]; + case 1: + _b.sent(); + return [4 /*yield*/, getSizeBytes(destinationPath)]; + case 2: + destinationSize = _b.sent(); + compareOldNew({ + sourceFileSize: sourceFileSize, + destinationSize: destinationSize, + args: args, + }); + if (error || destinationSize !== sourceFileSize) { + return [2 /*return*/, false]; + } + return [2 /*return*/, true]; + } + }); + }); +}; +// Keep in e.g. https://github.com/HaveAGitGat/Tdarr/issues/858 +var tyNcp = function (_a) { + var sourcePath = _a.sourcePath, destinationPath = _a.destinationPath, sourceFileSize = _a.sourceFileSize, args = _a.args; + return __awaiter(void 0, void 0, void 0, function () { + var error_1, destinationSize; + return __generator(this, function (_b) { + switch (_b.label) { + case 0: + if (!args.deps.ncp) return [3 /*break*/, 3]; + args.jobLog("Attempting copy from ".concat(sourcePath, " to ").concat(destinationPath, " , method 1")); + error_1 = false; + return [4 /*yield*/, new Promise(function (resolve) { + args.deps.ncp(sourcePath, destinationPath, function (err) { + if (err) { + error_1 = true; + args.jobLog("File copy error: ".concat(err)); + resolve(err); + } + else { + resolve(true); + } + }); + })]; + case 1: + _b.sent(); + return [4 /*yield*/, getSizeBytes(destinationPath)]; + case 2: + destinationSize = _b.sent(); + compareOldNew({ + sourceFileSize: sourceFileSize, + destinationSize: destinationSize, + args: args, + }); + if (error_1 || destinationSize !== sourceFileSize) { + return [2 /*return*/, false]; + } + return [2 /*return*/, true]; + case 3: return [2 /*return*/, false]; + } + }); + }); +}; +var tryNormalCopy = function (_a) { + var sourcePath = _a.sourcePath, destinationPath = _a.destinationPath, sourceFileSize = _a.sourceFileSize, args = _a.args; + return __awaiter(void 0, void 0, void 0, function () { + var error, err_3, destinationSize; + return __generator(this, function (_b) { + switch (_b.label) { + case 0: + args.jobLog("Attempting copy from ".concat(sourcePath, " to ").concat(destinationPath, " , method 2")); + error = false; + _b.label = 1; + case 1: + _b.trys.push([1, 3, , 4]); + return [4 /*yield*/, fs_1.promises.copyFile(sourcePath, destinationPath)]; + case 2: + _b.sent(); + return [3 /*break*/, 4]; + case 3: + err_3 = _b.sent(); + error = true; + args.jobLog("File copy error: ".concat(JSON.stringify(err_3))); + return [3 /*break*/, 4]; + case 4: return [4 /*yield*/, getSizeBytes(destinationPath)]; + case 5: + destinationSize = _b.sent(); + compareOldNew({ + sourceFileSize: sourceFileSize, + destinationSize: destinationSize, + args: args, + }); + if (error || destinationSize !== sourceFileSize) { + return [2 /*return*/, false]; + } + return [2 /*return*/, true]; + } + }); + }); +}; +var cleanSourceFile = function (_a) { + var args = _a.args, sourcePath = _a.sourcePath; + return __awaiter(void 0, void 0, void 0, function () { + var err_4; + return __generator(this, function (_b) { + switch (_b.label) { + case 0: + _b.trys.push([0, 2, , 3]); + args.jobLog("Deleting source file ".concat(sourcePath)); + return [4 /*yield*/, fs_1.promises.unlink(sourcePath)]; + case 1: + _b.sent(); + return [3 /*break*/, 3]; + case 2: + err_4 = _b.sent(); + args.jobLog("Failed to delete source file ".concat(sourcePath, ": ").concat(JSON.stringify(err_4))); + return [3 /*break*/, 3]; + case 3: return [2 /*return*/]; + } + }); + }); +}; +var fileMoveOrCopy = function (_a) { + var operation = _a.operation, sourcePath = _a.sourcePath, destinationPath = _a.destinationPath, args = _a.args; + return __awaiter(void 0, void 0, void 0, function () { + var sourceFileSize, moved, mvdird, ncpd, copied; + return __generator(this, function (_b) { + switch (_b.label) { + case 0: + args.jobLog('Calculating cache file size in bytes'); + return [4 /*yield*/, getSizeBytes(sourcePath)]; + case 1: + sourceFileSize = _b.sent(); + args.jobLog("".concat(sourceFileSize)); + if (!(operation === 'move')) return [3 /*break*/, 4]; + return [4 /*yield*/, tryMove({ + sourcePath: sourcePath, + destinationPath: destinationPath, + args: args, + sourceFileSize: sourceFileSize, + })]; + case 2: + moved = _b.sent(); + if (moved) { + return [2 /*return*/, true]; + } + return [4 /*yield*/, tryMvdir({ + sourcePath: sourcePath, + destinationPath: destinationPath, + args: args, + sourceFileSize: sourceFileSize, + })]; + case 3: + mvdird = _b.sent(); + if (mvdird) { + return [2 /*return*/, true]; + } + args.jobLog('Failed to move file, trying copy'); + _b.label = 4; + case 4: return [4 /*yield*/, tyNcp({ + sourcePath: sourcePath, + destinationPath: destinationPath, + args: args, + sourceFileSize: sourceFileSize, + })]; + case 5: + ncpd = _b.sent(); + if (!ncpd) return [3 /*break*/, 8]; + if (!(operation === 'move')) return [3 /*break*/, 7]; + return [4 /*yield*/, cleanSourceFile({ + args: args, + sourcePath: sourcePath, + })]; + case 6: + _b.sent(); + _b.label = 7; + case 7: return [2 /*return*/, true]; + case 8: return [4 /*yield*/, tryNormalCopy({ + sourcePath: sourcePath, + destinationPath: destinationPath, + args: args, + sourceFileSize: sourceFileSize, + })]; + case 9: + copied = _b.sent(); + if (!copied) return [3 /*break*/, 12]; + if (!(operation === 'move')) return [3 /*break*/, 11]; + return [4 /*yield*/, cleanSourceFile({ + args: args, + sourcePath: sourcePath, + })]; + case 10: + _b.sent(); + _b.label = 11; + case 11: return [2 /*return*/, true]; + case 12: throw new Error("Failed to ".concat(operation, " file")); + } + }); + }); +}; +exports.default = fileMoveOrCopy; diff --git a/FlowPlugins/FlowHelpers/1.0.0/fileUtils.js b/FlowPlugins/FlowHelpers/1.0.0/fileUtils.js index 2e5963a..3f777d3 100644 --- a/FlowPlugins/FlowHelpers/1.0.0/fileUtils.js +++ b/FlowPlugins/FlowHelpers/1.0.0/fileUtils.js @@ -36,7 +36,7 @@ var __generator = (this && this.__generator) || function (thisArg, body) { } }; Object.defineProperty(exports, "__esModule", { value: true }); -exports.getScanTypes = exports.getPluginWorkDir = exports.moveFileAndValidate = exports.getSubStem = exports.getFfType = exports.getFileAbosluteDir = exports.getFileName = exports.getContainer = void 0; +exports.getScanTypes = exports.getPluginWorkDir = exports.moveFileAndValidate = exports.getFileSize = exports.getSubStem = exports.getFfType = exports.getFileAbosluteDir = exports.getFileName = exports.getContainer = void 0; var fs_1 = require("fs"); var getContainer = function (filePath) { var parts = filePath.split('.'); @@ -79,13 +79,14 @@ var getFileSize = function (file) { return __awaiter(void 0, void 0, void 0, fun } }); }); }; +exports.getFileSize = getFileSize; var moveFileAndValidate = function (_a) { var inputPath = _a.inputPath, outputPath = _a.outputPath, args = _a.args; return __awaiter(void 0, void 0, void 0, function () { var inputSize, res1, outputSize, err_1, res2, errMessage; return __generator(this, function (_b) { switch (_b.label) { - case 0: return [4 /*yield*/, getFileSize(inputPath)]; + case 0: return [4 /*yield*/, (0, exports.getFileSize)(inputPath)]; case 1: inputSize = _b.sent(); args.jobLog("Attempt 1: Moving file from ".concat(inputPath, " to ").concat(outputPath)); @@ -107,7 +108,7 @@ var moveFileAndValidate = function (_a) { _b.label = 3; case 3: _b.trys.push([3, 5, , 6]); - return [4 /*yield*/, getFileSize(outputPath)]; + return [4 /*yield*/, (0, exports.getFileSize)(outputPath)]; case 4: outputSize = _b.sent(); return [3 /*break*/, 6]; @@ -135,7 +136,7 @@ var moveFileAndValidate = function (_a) { })]; case 7: res2 = _b.sent(); - return [4 /*yield*/, getFileSize(outputPath)]; + return [4 /*yield*/, (0, exports.getFileSize)(outputPath)]; case 8: outputSize = _b.sent(); if (!res2 || inputSize !== outputSize) { diff --git a/FlowPluginsTs/CommunityFlowPlugins/file/moveToDirectory/2.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/file/moveToDirectory/2.0.0/index.ts index cc34658..2bf0446 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/file/moveToDirectory/2.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/file/moveToDirectory/2.0.0/index.ts @@ -1,5 +1,6 @@ +import fileMoveOrCopy from '../../../../FlowHelpers/1.0.0/fileMoveOrCopy'; import { - getContainer, getFileName, getSubStem, moveFileAndValidate, + getContainer, getFileName, getSubStem, } from '../../../../FlowHelpers/1.0.0/fileUtils'; import { IpluginDetails, @@ -113,11 +114,11 @@ const plugin = async (args:IpluginInputArgs):Promise => { args.deps.fsextra.ensureDirSync(outputPath); - await moveFileAndValidate({ - inputPath: args.inputFileObj._id, - outputPath: ouputFilePath, + await fileMoveOrCopy({ + operation: 'move', + sourcePath: args.inputFileObj._id, + destinationPath: ouputFilePath, args, - }); return { diff --git a/FlowPluginsTs/CommunityFlowPlugins/file/moveToOriginalDirectory/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/file/moveToOriginalDirectory/1.0.0/index.ts index 693027a..6db8589 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/file/moveToOriginalDirectory/1.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/file/moveToOriginalDirectory/1.0.0/index.ts @@ -1,5 +1,6 @@ +import fileMoveOrCopy from '../../../../FlowHelpers/1.0.0/fileMoveOrCopy'; import { - getContainer, getFileAbosluteDir, getFileName, moveFileAndValidate, + getContainer, getFileAbosluteDir, getFileName, } from '../../../../FlowHelpers/1.0.0/fileUtils'; import { IpluginDetails, @@ -54,9 +55,10 @@ const plugin = async (args:IpluginInputArgs):Promise => { }; } - await moveFileAndValidate({ - inputPath: args.inputFileObj._id, - outputPath: ouputFilePath, + await fileMoveOrCopy({ + operation: 'move', + sourcePath: args.inputFileObj._id, + destinationPath: ouputFilePath, args, }); diff --git a/FlowPluginsTs/CommunityFlowPlugins/file/renameFile/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/file/renameFile/1.0.0/index.ts index 81b21a3..d48a263 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/file/renameFile/1.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/file/renameFile/1.0.0/index.ts @@ -1,5 +1,6 @@ +import fileMoveOrCopy from '../../../../FlowHelpers/1.0.0/fileMoveOrCopy'; import { - getContainer, getFileAbosluteDir, getFileName, moveFileAndValidate, + getContainer, getFileAbosluteDir, getFileName, } from '../../../../FlowHelpers/1.0.0/fileUtils'; import { IpluginDetails, @@ -68,9 +69,10 @@ const plugin = async (args: IpluginInputArgs): Promise => { }; } - await moveFileAndValidate({ - inputPath: args.inputFileObj._id, - outputPath: newPath, + await fileMoveOrCopy({ + operation: 'move', + sourcePath: args.inputFileObj._id, + destinationPath: newPath, args, }); diff --git a/FlowPluginsTs/CommunityFlowPlugins/file/replaceOriginalFile/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/file/replaceOriginalFile/1.0.0/index.ts index 8f4f68c..bd66ba3 100644 --- a/FlowPluginsTs/CommunityFlowPlugins/file/replaceOriginalFile/1.0.0/index.ts +++ b/FlowPluginsTs/CommunityFlowPlugins/file/replaceOriginalFile/1.0.0/index.ts @@ -1,5 +1,6 @@ +import fileMoveOrCopy from '../../../../FlowHelpers/1.0.0/fileMoveOrCopy'; import { - getContainer, getFileAbosluteDir, getFileName, moveFileAndValidate, + getContainer, getFileAbosluteDir, getFileName, } from '../../../../FlowHelpers/1.0.0/fileUtils'; import { IpluginDetails, @@ -66,9 +67,10 @@ const plugin = async (args: IpluginInputArgs): Promise => { await new Promise((resolve) => setTimeout(resolve, 2000)); - await moveFileAndValidate({ - inputPath: currentPath, - outputPath: newPathTmp, + await fileMoveOrCopy({ + operation: 'move', + sourcePath: currentPath, + destinationPath: newPathTmp, args, }); @@ -83,9 +85,10 @@ const plugin = async (args: IpluginInputArgs): Promise => { await new Promise((resolve) => setTimeout(resolve, 2000)); - await moveFileAndValidate({ - inputPath: newPathTmp, - outputPath: newPath, + await fileMoveOrCopy({ + operation: 'move', + sourcePath: newPathTmp, + destinationPath: newPath, args, }); diff --git a/FlowPluginsTs/FlowHelpers/1.0.0/fileMoveOrCopy.ts b/FlowPluginsTs/FlowHelpers/1.0.0/fileMoveOrCopy.ts new file mode 100644 index 0000000..d15e652 --- /dev/null +++ b/FlowPluginsTs/FlowHelpers/1.0.0/fileMoveOrCopy.ts @@ -0,0 +1,275 @@ +import { promises as fs } from 'fs'; + +import { getFileSize } from './fileUtils'; +import { IpluginInputArgs } from './interfaces/interfaces'; + +interface Imove { + sourcePath:string, + destinationPath:string, + sourceFileSize:number, + args:IpluginInputArgs, + } + +const getSizeBytes = async (fPath: string): Promise => { + let size = 0; + try { + size = await getFileSize(fPath); + } catch (err) { + // err + } + return size; +}; + +const compareOldNew = ({ + sourceFileSize, + destinationSize, + args, +}:{ + sourceFileSize:number, + destinationSize:number, + args:IpluginInputArgs, + }):void => { + if (destinationSize !== sourceFileSize) { + args.jobLog(`After move/copy, destination file of size ${destinationSize} does not match` + + ` cache file of size ${sourceFileSize}`); + } else { + args.jobLog(`After move/copy, destination file of size ${destinationSize} does match` + + ` cache file of size ${sourceFileSize}`); + } +}; + +const tryMove = async ({ + sourcePath, + destinationPath, + sourceFileSize, + args, +}:Imove):Promise => { + args.jobLog(`Attempting move from ${sourcePath} to ${destinationPath}, method 1`); + + let error = false; + try { + await fs.rename(sourcePath, destinationPath); + } catch (err) { + error = true; + args.jobLog(`File move error: ${JSON.stringify(err)}`); + } + + const destinationSize = await getSizeBytes(destinationPath); + compareOldNew({ + sourceFileSize, + destinationSize, + args, + }); + + if (error || destinationSize !== sourceFileSize) { + return false; + } + + return true; +}; + +const tryMvdir = async ({ + sourcePath, + destinationPath, + sourceFileSize, + args, +}:Imove):Promise => { + args.jobLog(`Attempting move from ${sourcePath} to ${destinationPath}, method 2`); + + let error = false; + await new Promise((resolve) => { + // fs-extra and move-file don't work when destination is on windows root of drive + // mvdir will try to move else fall back to copy/unlink + // potential bug on unraid + args.deps.mvdir(sourcePath, destinationPath, { overwrite: true }) + .then(() => { + resolve(true); + }).catch((err: Error) => { + error = true; + args.jobLog(`File move error: ${err}`); + resolve(err); + }); + }); + + const destinationSize = await getSizeBytes(destinationPath); + compareOldNew({ + sourceFileSize, + destinationSize, + args, + }); + + if (error || destinationSize !== sourceFileSize) { + return false; + } + + return true; +}; + +// Keep in e.g. https://github.com/HaveAGitGat/Tdarr/issues/858 +const tyNcp = async ({ + sourcePath, + destinationPath, + sourceFileSize, + args, +}:Imove):Promise => { + // added in 2.14.01 + if (args.deps.ncp) { + args.jobLog(`Attempting copy from ${sourcePath} to ${destinationPath} , method 1`); + + let error = false; + await new Promise((resolve) => { + args.deps.ncp(sourcePath, destinationPath, (err: Error) => { + if (err) { + error = true; + args.jobLog(`File copy error: ${err}`); + resolve(err); + } else { + resolve(true); + } + }); + }); + + const destinationSize = await getSizeBytes(destinationPath); + compareOldNew({ + sourceFileSize, + destinationSize, + args, + }); + + if (error || destinationSize !== sourceFileSize) { + return false; + } + + return true; + } + + return false; +}; + +const tryNormalCopy = async ({ + sourcePath, + destinationPath, + sourceFileSize, + args, +}:Imove):Promise => { + args.jobLog(`Attempting copy from ${sourcePath} to ${destinationPath} , method 2`); + + let error = false; + try { + await fs.copyFile(sourcePath, destinationPath); + } catch (err) { + error = true; + args.jobLog(`File copy error: ${JSON.stringify(err)}`); + } + + const destinationSize = await getSizeBytes(destinationPath); + compareOldNew({ + sourceFileSize, + destinationSize, + args, + }); + + if (error || destinationSize !== sourceFileSize) { + return false; + } + + return true; +}; + +const cleanSourceFile = async ({ + args, + sourcePath, +}:{ + args:IpluginInputArgs, + sourcePath: string, +}) => { + try { + args.jobLog(`Deleting source file ${sourcePath}`); + await fs.unlink(sourcePath); + } catch (err) { + args.jobLog(`Failed to delete source file ${sourcePath}: ${JSON.stringify(err)}`); + } +}; + +const fileMoveOrCopy = async ({ + operation, + sourcePath, + destinationPath, + args, +}: { + operation: 'move' | 'copy', + sourcePath: string, + destinationPath: string, + args: IpluginInputArgs, +}):Promise => { + args.jobLog('Calculating cache file size in bytes'); + + const sourceFileSize = await getSizeBytes(sourcePath); + args.jobLog(`${sourceFileSize}`); + + if (operation === 'move') { + const moved = await tryMove({ + sourcePath, + destinationPath, + args, + sourceFileSize, + }); + + if (moved) { + return true; + } + + const mvdird = await tryMvdir({ + sourcePath, + destinationPath, + args, + sourceFileSize, + }); + + if (mvdird) { + return true; + } + + args.jobLog('Failed to move file, trying copy'); + } + + const ncpd = await tyNcp({ + sourcePath, + destinationPath, + args, + sourceFileSize, + }); + + if (ncpd) { + if (operation === 'move') { + await cleanSourceFile({ + args, + sourcePath, + }); + } + + return true; + } + + const copied = await tryNormalCopy({ + sourcePath, + destinationPath, + args, + sourceFileSize, + }); + + if (copied) { + if (operation === 'move') { + await cleanSourceFile({ + args, + sourcePath, + }); + } + + return true; + } + + throw new Error(`Failed to ${operation} file`); +}; + +export default fileMoveOrCopy; diff --git a/FlowPluginsTs/FlowHelpers/1.0.0/fileUtils.ts b/FlowPluginsTs/FlowHelpers/1.0.0/fileUtils.ts index bb2fb1b..a7f4cfe 100644 --- a/FlowPluginsTs/FlowHelpers/1.0.0/fileUtils.ts +++ b/FlowPluginsTs/FlowHelpers/1.0.0/fileUtils.ts @@ -36,7 +36,7 @@ export const getSubStem = ({ return parts.join('/'); }; -const getFileSize = async (file:string):Promise => { +export const getFileSize = async (file:string):Promise => { const stats = await fs.stat(file); const { size } = stats; return size; diff --git a/FlowPluginsTs/FlowHelpers/1.0.0/interfaces/interfaces.ts b/FlowPluginsTs/FlowHelpers/1.0.0/interfaces/interfaces.ts index 917ee09..e3e80a7 100644 --- a/FlowPluginsTs/FlowHelpers/1.0.0/interfaces/interfaces.ts +++ b/FlowPluginsTs/FlowHelpers/1.0.0/interfaces/interfaces.ts @@ -132,7 +132,9 @@ export interface IpluginInputArgs { gracefulfs: any, // eslint-disable-next-line @typescript-eslint/no-explicit-any mvdir: any, - // eslint-disable-next-line @typescript-eslint/no-explicit-any + // eslint-disable-next-line @typescript-eslint/no-explicit-any + ncp: any, + // eslint-disable-next-line @typescript-eslint/no-explicit-any axios: any, // eslint-disable-next-line @typescript-eslint/no-explicit-any crudTransDBN: (collection: string, mode: string, docID: string, obj: any)=> any,