feat(flow): support folder-to-folder conversion with sidecar migration and prune
Build / Lint & Test (push) Successful in 28s
Build / Lint & Test (push) Successful in 28s
This commit is contained in:
1 parent
f2e483ca1f
commit
88ecf9a847
3 files changed
+253
-62
No files matched your search
@@ -27,6 +27,14 @@ const details = () => ({
|
||||
inputUI: { type: 'dropdown', options: ['true', 'false'] },
|
||||
tooltip: 'Delete the processed file after it has replaced the original.',
|
||||
},
|
||||
{
|
||||
label: 'Output Directory (Optional)',
|
||||
name: 'outputDirectory',
|
||||
type: 'string',
|
||||
defaultValue: '',
|
||||
inputUI: { type: 'text' },
|
||||
tooltip: 'Target library directory for folder-to-folder conversion. If empty, automatically uses Library Output if configured, or replaces in-place. Example: T:/Media/Film or T:/Media/Serie',
|
||||
},
|
||||
],
|
||||
outputs: [
|
||||
{ number: 1, tooltip: 'Original file replaced — continue to next plugin' },
|
||||
@@ -47,6 +55,36 @@ const plugin = async (args) => {
|
||||
if (!fs.existsSync(source)) {
|
||||
throw new Error(`[replaceOriginalFile] Processed file not found: ${source}`);
|
||||
}
|
||||
|
||||
const originalSourceFile = destination;
|
||||
const originalSourceDir = path.dirname(originalSourceFile);
|
||||
|
||||
// Check for folder-to-folder target directory (from plugin input or Tdarr librarySettings.output)
|
||||
const configuredOutput = ((args.inputs && args.inputs.outputDirectory) || '').trim()
|
||||
|| ((args.librarySettings && args.librarySettings.output) || '').trim();
|
||||
const sourceLibraryRoot = ((args.librarySettings && args.librarySettings.folder) || '').trim();
|
||||
|
||||
if (configuredOutput && path.resolve(configuredOutput) !== path.resolve(sourceLibraryRoot || originalSourceDir)) {
|
||||
let relPath = '';
|
||||
if (sourceLibraryRoot) {
|
||||
try {
|
||||
relPath = path.relative(path.resolve(sourceLibraryRoot), path.resolve(originalSourceFile));
|
||||
} catch (_) { relPath = ''; }
|
||||
}
|
||||
if (!relPath || relPath.startsWith('..') || path.isAbsolute(relPath)) {
|
||||
const base = path.basename(originalSourceFile);
|
||||
const parent = path.basename(originalSourceDir);
|
||||
const grandParent = path.basename(path.dirname(originalSourceDir));
|
||||
if (grandParent && grandParent !== '.' && !grandParent.includes(':')) {
|
||||
relPath = path.join(grandParent, parent, base);
|
||||
} else {
|
||||
relPath = path.join(parent, base);
|
||||
}
|
||||
}
|
||||
destination = path.join(path.resolve(configuredOutput), relPath);
|
||||
jobLog(`[replaceOriginalFile] Folder-to-folder mode: target is ${destination}\n`);
|
||||
}
|
||||
|
||||
if (path.resolve(source) === path.resolve(destination)) {
|
||||
jobLog('[replaceOriginalFile] Processed file is already at the original path — skipping.\n');
|
||||
return { outputNumber: 1, outputFileObj: inputFileObj, variables };
|
||||
@@ -54,7 +92,8 @@ const plugin = async (args) => {
|
||||
|
||||
const destinationDir = path.dirname(destination);
|
||||
if (!fs.existsSync(destinationDir)) {
|
||||
throw new Error(`[replaceOriginalFile] Destination directory does not exist: ${destinationDir}`);
|
||||
fs.mkdirSync(destinationDir, { recursive: true });
|
||||
jobLog(`[replaceOriginalFile] Created destination directory: ${destinationDir}\n`);
|
||||
}
|
||||
|
||||
const sourceExt = path.extname(source);
|
||||
@@ -107,7 +146,7 @@ const plugin = async (args) => {
|
||||
}
|
||||
}
|
||||
|
||||
if ((args.inputs.deleteSource || 'true') === 'true') {
|
||||
if ((args.inputs.deleteSource || 'true') === 'true' && path.resolve(source) !== path.resolve(destination)) {
|
||||
try {
|
||||
fs.unlinkSync(source);
|
||||
} catch (error) {
|
||||
@@ -115,6 +154,15 @@ const plugin = async (args) => {
|
||||
}
|
||||
}
|
||||
|
||||
if (path.resolve(destination) !== path.resolve(originalSourceFile) && fs.existsSync(originalSourceFile)) {
|
||||
try {
|
||||
fs.unlinkSync(originalSourceFile);
|
||||
jobLog(`[replaceOriginalFile] Removed source file from intake directory: ${originalSourceFile}\n`);
|
||||
} catch (error) {
|
||||
jobLog(`[replaceOriginalFile] WARNING: Could not delete intake source file ${originalSourceFile}: ${error.message}\n`);
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up temporary workDir copy (_old) if still present
|
||||
if (variables.workDirCopyApplied === 'true') {
|
||||
const workDir = path.dirname(source);
|
||||
@@ -125,6 +173,9 @@ const plugin = async (args) => {
|
||||
}
|
||||
}
|
||||
|
||||
variables.finalFile = destination;
|
||||
variables.finalDir = destinationDir;
|
||||
|
||||
jobLog(`[replaceOriginalFile] Replaced original file: ${destination}\n`);
|
||||
return {
|
||||
outputNumber: 1,
|
||||
|
||||
Reference in new issue
Block a user