From 72e6c50d3f2c05cd024599c3de641020e9d3d90c Mon Sep 17 00:00:00 2001 From: John Andrews Date: Wed, 14 Feb 2024 09:57:31 +1300 Subject: [PATCH] FF-1263 - copy/move now take in an optional input file --- BasicNodes/BasicNodes.en.json | 6 +++ BasicNodes/File/CopyFile.cs | 72 +++++++++++++++++++++++----------- BasicNodes/File/MoveFile.cs | 67 ++++++++++++++++++++++++------- FileFlows.Plugin.dll | Bin 132096 -> 132096 bytes FileFlows.Plugin.pdb | Bin 30688 -> 30688 bytes 5 files changed, 108 insertions(+), 37 deletions(-) diff --git a/BasicNodes/BasicNodes.en.json b/BasicNodes/BasicNodes.en.json index c2d13376..3f71cf38 100644 --- a/BasicNodes/BasicNodes.en.json +++ b/BasicNodes/BasicNodes.en.json @@ -27,6 +27,9 @@ "1": "File copied" }, "Fields": { + "InputFile": "File To Copy", + "InputFile-Help": "The file to copy, if left empty then the working file will be copied", + "InputFile-Placeholder": "Working File", "DestinationPath": "Destination Folder", "DestinationPath-Help": "The folder where the file will be copied to", "DestinationFile": "Destination File", @@ -187,6 +190,9 @@ "2": "File moved, however original file could not be deleted" }, "Fields": { + "InputFile": "File To Move", + "InputFile-Help": "The file to move, if left empty then the working file will be moved", + "InputFile-Placeholder": "Working File", "DestinationPath": "Destination Folder", "DestinationPath-Help": "The folder where the file will be moved to", "DestinationFile": "Destination File", diff --git a/BasicNodes/File/CopyFile.cs b/BasicNodes/File/CopyFile.cs index aefb4b69..3f24713a 100644 --- a/BasicNodes/File/CopyFile.cs +++ b/BasicNodes/File/CopyFile.cs @@ -38,33 +38,40 @@ namespace FileFlows.BasicNodes.File private string _DestinationPath = string.Empty; private string _DestinationFile = string.Empty; + + /// + /// Gets or sets the input file to move + /// + [TextVariable(1)] + public string InputFile{ get; set; } + [Required] - [Folder(1)] + [Folder(2)] public string DestinationPath { get => _DestinationPath; set { _DestinationPath = value ?? ""; } } - [TextVariable(2)] + [TextVariable(3)] public string DestinationFile { get => _DestinationFile; set { _DestinationFile = value ?? ""; } } - [Boolean(3)] + [Boolean(4)] public bool CopyFolder { get; set; } - [StringArray(4)] + [StringArray(5)] public string[] AdditionalFiles { get; set; } - [Boolean(5)] + [Boolean(6)] public bool AdditionalFilesFromOriginal { get; set; } /// /// Gets or sets if the original files creation and last write time dates should be preserved /// - [Boolean(6)] + [Boolean(7)] public bool PreserverOriginalDates { get; set; } private bool Canceled; @@ -80,43 +87,62 @@ namespace FileFlows.BasicNodes.File { Canceled = false; + var destParts = MoveFile.GetDestinationPathParts(args, DestinationPath, DestinationFile, CopyFolder); if (destParts.Filename == null) return -1; + string inputFile = args.ReplaceVariables(InputFile ?? string.Empty, stripMissing: true)?.EmptyAsNull() ?? args.WorkingFile; + // cant use new FileInfo(dest).Directory.Name here since // if the folder is a linux folder and this node is running on windows // /mnt, etc will be converted to c:\mnt and break the destination var destDir = destParts.Path; - string dest = destParts.Path + destParts.Separator + destParts.Filename; + var destFile = destParts.Filename; + if(inputFile != args.WorkingFile && string.IsNullOrWhiteSpace(DestinationFile)) + { + destFile = FileHelper.GetShortFileName(inputFile); + } + string dest = FileHelper.Combine(destParts.Path, destFile); //bool copied = args.CopyFile(args.WorkingFile, dest, updateWorkingFile: true); //if (!copied) // return -1; - - if (args.FileService.FileCopy(args.WorkingFile, dest, true).IsFailed) - return -1; - args.SetWorkingFile(dest); + args.Logger?.ILog("File to copy: " + inputFile); + args.Logger?.ILog("Destination: " + dest); - if (PreserverOriginalDates) + if (args.FileService.FileCopy(inputFile, dest, true).Failed(out string error)) { - if (args.Variables.TryGetValue("ORIGINAL_CREATE_UTC", out object oCreateTimeUtc) && - args.Variables.TryGetValue("ORIGINAL_LAST_WRITE_UTC", out object oLastWriteUtc) && - oCreateTimeUtc is DateTime dtCreateTimeUtc && oLastWriteUtc is DateTime dtLastWriteUtc) + args.FailureReason = "Failed to copy file: " + error; + args.Logger?.ELog(args.FailureReason); + return -1; + } + + if (inputFile == args.WorkingFile) + { + args.Logger?.ILog("Setting working file to: " + dest); + args.SetWorkingFile(dest); + + if (PreserverOriginalDates) { - args.Logger?.ILog("Preserving dates"); - Helpers.FileHelper.SetLastWriteTime(dest, dtLastWriteUtc); - Helpers.FileHelper.SetCreationTime(dest, dtCreateTimeUtc); - } - else - { - args.Logger?.WLog("Preserve dates is on but failed to get original dates from variables"); + if (args.Variables.TryGetValue("ORIGINAL_CREATE_UTC", out object oCreateTimeUtc) && + args.Variables.TryGetValue("ORIGINAL_LAST_WRITE_UTC", out object oLastWriteUtc) && + oCreateTimeUtc is DateTime dtCreateTimeUtc && oLastWriteUtc is DateTime dtLastWriteUtc) + { + args.Logger?.ILog("Preserving dates"); + Helpers.FileHelper.SetLastWriteTime(dest, dtLastWriteUtc); + Helpers.FileHelper.SetCreationTime(dest, dtCreateTimeUtc); + } + else + { + args.Logger?.WLog("Preserve dates is on but failed to get original dates from variables"); + } } } var srcDir = FileHelper.GetDirectory(AdditionalFilesFromOriginal ? args.FileName - : args.WorkingFile); + : inputFile); if (AdditionalFiles?.Any() == true) { diff --git a/BasicNodes/File/MoveFile.cs b/BasicNodes/File/MoveFile.cs index 5506ce53..87d2f6a0 100644 --- a/BasicNodes/File/MoveFile.cs +++ b/BasicNodes/File/MoveFile.cs @@ -32,49 +32,55 @@ public class MoveFile : Node /// public override string HelpUrl => "https://fileflows.com/docs/plugins/basic-nodes/move-file"; + /// + /// Gets or sets the input file to move + /// + [TextVariable(1)] + public string InputFile{ get; set; } + /// /// Gets or sets the destination path /// [Required] - [Folder(1)] + [Folder(2)] public string DestinationPath { get; set; } /// /// Gets or sets the destination file /// - [TextVariable(2)] + [TextVariable(3)] public string DestinationFile{ get; set; } /// /// Gets or sets if the folder should be moved /// - [Boolean(3)] + [Boolean(4)] public bool MoveFolder { get; set; } /// /// Gets or sets if the original should be deleted /// - [Boolean(4)] + [Boolean(5)] public bool DeleteOriginal { get; set; } /// /// Gets or sets additional files that should also be moved /// - [StringArray(5)] + [StringArray(6)] public string[] AdditionalFiles { get; set; } /// /// Gets or sets original files from the original file location that should also be moved /// - [Boolean(6)] + [Boolean(7)] public bool AdditionalFilesFromOriginal { get; set; } /// /// Gets or sets if the original files creation and last write time dates should be preserved /// - [Boolean(7)] + [Boolean(8)] public bool PreserverOriginalDates { get; set; } - + /// /// Executes the node /// @@ -82,21 +88,54 @@ public class MoveFile : Node /// the output to call next public override int Execute(NodeParameters args) { - var dest = GetDestinationPath(args, DestinationPath, DestinationFile, MoveFolder); + string destFile = args.ReplaceVariables(DestinationFile ?? string.Empty); + + string inputFile = args.ReplaceVariables(InputFile ?? string.Empty)?.EmptyAsNull() ?? args.WorkingFile; + if (inputFile != args.WorkingFile && string.IsNullOrWhiteSpace(DestinationFile)) + destFile = FileHelper.GetShortFileName(inputFile); + + var dest = GetDestinationPath(args, DestinationPath, destFile, MoveFolder); if (dest == null) + { + args.FailureReason = "Failed to get move destination"; + args.Logger?.ELog(args.FailureReason); return -1; + } + + if (inputFile != args.WorkingFile && string.IsNullOrWhiteSpace(destFile) == false) + dest = FileHelper.Combine(FileHelper.GetDirectory(dest), destFile); // ensure the non working file has correct extension/name // store srcDir here before we move and the working file is altered - var srcDir = FileHelper.GetDirectory(AdditionalFilesFromOriginal ? args.FileName : args.WorkingFile); + var srcDir = FileHelper.GetDirectory(AdditionalFilesFromOriginal ? args.FileName : inputFile); args.Logger?.ILog("Source Directory: " + srcDir); string shortNameLookup = FileHelper.GetShortFileName(args.FileName); if (shortNameLookup.LastIndexOf(".", StringComparison.InvariantCulture) > 0) - shortNameLookup = shortNameLookup.Substring(0, shortNameLookup.LastIndexOf(".", StringComparison.Ordinal)); + shortNameLookup = shortNameLookup[..shortNameLookup.LastIndexOf(".", StringComparison.Ordinal)]; args.Logger?.ILog("shortNameLookup: " + shortNameLookup); + + args.Logger?.ILog("Moving file: " + inputFile); + args.Logger?.ILog("Destination: " + dest); - if (args.MoveFile(dest) == false) - return -1; + + if (inputFile == args.WorkingFile) + { + if (args.MoveFile(dest) == false) + { + args.FailureReason = "Failed to move file"; + args.Logger?.ELog(args.FailureReason); + return -1; + } + } + else + { + if (args.FileService.FileMove(inputFile, dest).Failed(out string error)) + { + args.FailureReason = "Failed to move file: " + error; + args.Logger?.ELog(args.FailureReason); + return -1; + } + } if (PreserverOriginalDates) { @@ -198,7 +237,7 @@ public class MoveFile : Node var result = GetDestinationPathParts(args, destinationPath, destinationFile, moveFolder); if(result.Filename == null) return null; - return result.Path + result.Separator + result.Filename; + return FileHelper.Combine(result.Path, result.Filename); } /// diff --git a/FileFlows.Plugin.dll b/FileFlows.Plugin.dll index 39652f01e9d1dafa93c32d120a7176f1a714d167..9611608079424e871182df15331bf0f385a891d2 100644 GIT binary patch delta 114 zcmZqZ;ArUJn9#v8Lpfq!W6#ze#xpMkw*JvMv_9ar+rD?p@|43Pi??UIVzd!tImoto z)$|p=83P3T0}5)ofBp9D5m;}Rv#8W|I>#SIdj+UyFH97uUhAgj_2(L!Rk diff --git a/FileFlows.Plugin.pdb b/FileFlows.Plugin.pdb index 79fcc822a1e57c59e12a4ca0befe9bec50acb362..2b1fff47780e249fcab4370c324b8bf4e53ee492 100644 GIT binary patch delta 5749 zcmZ8lc_5VQ_aBwIZ6w7@jIk?a&6*f9%-F`h-h{!(2$L~H5oL*x<&i8YOW7_7We-_G z*|JoYN{e)rWJ!B|^Gu`L@B8=poX^>xbKZAyj$?9;BPnEunfbAhxUb(x@!QI^z4HPN z-dSH&Qjz4le~+rHimDpLo$NtTRa8)=sJg1DYPhNH_fRD(QQXzlR24N8T*+kB{YvC2 z85MI5WS{)&v#4+sO4-@P%6nIh(Yc6byqO$H2qdYBBJzB;7f=`uly~}jQC(e5?74cS zslU2V3rry&c&54@*)J&^2ek*^YS~rF+hF7T89Q1UD>%UqruH)Th8s|LrKqsfp@^4o zAtSi3>mqRusH%xjn<7<~Q8_47g3{j`)?@q3((=*w#niULUsq4?s5J&FQR>A%ZijN#Fz{7(=_h&OXa)+iu zu)1qro|}s#Ht%B5&gWTK!C;rVgyH6gHzuLqrPt}*x8gjB$~fw~ZsBQ;0oGPo_+ zg5q3m+zHy$C6MSBBOTCG$hG`Z!Kwso<@PfRUG2P+U`1UL33kqS%M#8$m=hedHqPtl z3(N0q)CVmZe85s;JJJw&_X4T~g=$`j&=Q$yA*B9rZELS~s%8OFH29D*!{CQ7On=YB z%J)fK#48uj9ADR=IG6w-m9n_?Xw*-H$C&m0&m*qDTu!RLk(Ay;L(q=Hx8jc)5P7{&g#h_1Ag# z+p>{*WCmFdPxJ8+rTMbC4;^{Xgvwciy7Bl`(r3XkY*J#@LVxbNHGpwI0;l7!lm@*d zhB>2eYPuN3&%ENi7NdIjPr6@ytbX%bAvD~+y?FSHkAZ%Kk(16}4BSS*q0I|s4{Sqh zF2|KYD*XVg0%b2`n9;=kIo918qnuz9YQA&#Z z0nO?n3Wna1SF|tyeCHc6xP4BSssG`aT?8g)lyWMcouZ( znMO?ZVTuYaO4<3F@N>^@CXOGBALK&T5#POpmhf)X!gZ4_ggQ+PPI*N|S0+I8U{mkj zVJHk7uqPeb9Zovm6($wGux>BA0KamN`yxIICs0l2X|EQUmjK%RjK}7T1c9^mPDFXS z@-ws_@$IVe4oyeG?ML*T#eMkwAYL02CbpdM5~hK(nG3_OeG?mVl2p@pWFTy{zQMj^ zAF7+2Wq>b(>a*Ho=V>p_K}HLcW(}l!uR+3T7ok?ZNBh`H1RC9k%PR9R z=s{nhkaEJHSsR-MvMva92wNAKTvjx3z7cG~ikrMW8J7Ts4aQ%R`bYz$^9EWFL&pvxgERR$4B{_E^y?w<5aZ;(1mb$Tnt4DlRvM9LIl z7#SvHCWl*I3zBqq0!7^&$hP^JH0Z*EZ&RN;5LaSdabEF{7>Oegfd0kN5S-f%IZjh( z-}JY;=r^Q2UViie|C9-lL{@?Va-eWns%Br=L=FCCy{ExdSGNqPzuTNn^`SDTft1zG zC90TyVkL>^-bXuo5;l3;;%#BdSOLo&42}y?(`f$y-5J^2ut&gk44)8HVo!)08V3X% z1`&QjOn^fB`ZjIgwgsOIL@%P#c+|luoEReSbnXSD{`CDhZ>$!tN##Iwd)W8O9H15_ zLHPc?_AwOBJ-tVyFC^#z$J(8vxTwMZ(q&FP_S;L#ik=o|=c(MyiWJ zuc`bS(cl~&jo`6~SKt=2tT-dTYm8qnx{(1PC;cBWRg5Ni5u4C=d{Kr}0n z^YSS8X;t>84T}H@J(%qJ`$7M!DXj_W42@r?!r8eRWye{zKUcQPV(XA7J~VnT+cW%Z zO$VHAjXf>7qlONqg;86Y6}VHY*jRH@3WJU0HNDXfqt(3r;qfxzoo@3_F0+`mZ|H0! z1R5|H$Jpp~)2@;5@>rodO<1-C=$7Y&b9-))w*@77yO2@u4XrS%yB)Ba&WYc#x#QKk z^swV8;E5iZkiLiX3JOsS(uHtC8RMMK6a1agq2YhnA(5%xY�hZG$jYo%d1wK_|Ge zg<7tQkQ-q66pFBxT@pT|rUm{GHs_zlDotx;gd>B2JD72jYq0X?7)^on{Uv$Wy=>KhTS!au>RHM2bk^OAI`FL!hGal$}9qyFW|4?`hwUzfPt5VXI&(FN|-dzRC)s=3dH{~ zVM*}|p!hSNzh4^a)y|1Y)Urx>zbA$PqG}WPuv2}2m;7d+O^wI z)n=q-#&T0BHt|F4gMF==uwWZ;7cwuz&Dta=PLC%J^l!wR{7qZa(_ha3JW&$iu+Le5 zwC4(ko#rOv%~HGSxc)u8ZV-eM1rd*pnhj8h3E{I4|2kz9v3uh=H`k|MK_yWf$*+7J z1BE>nG-;{8b-J<3744S>Z;FChq7X7%IME36r8~*<%J34uSz2aje^ZbCFc3B1L$(Ge zM?lT7?4LPyZ~i9Sj$NDI>g;3%%nZpwhCeLLvWs;Rb=&k1EYh~xn%+-r| zgpz3Pl+`ZtxLQ*RO(<+8ht=lJleXU`vaedd$NR`UFM=`k$z zQhCQKWQ&OY>5LCWPpUGhLYlCjmc-YNXCTXPH?s64 zdjuL3$;Ya(qktlMO zq*;K%tUr;BbLJ<{!KWTexT=H?rQ=nb4}mx% zX{2UPGwVF|8giWJIvryI9+427YAYwWXM{$scwsIZmX^Sh0mFHRftdWbZV;Z6z+Dj-EUd6#O>g!3|ya!5B8y99-vW}_xTg*Ao^)$^%g zz2Vdplbwi5*Wn73%NA5YZrL@5Z$Ax-o&{HO1KlF9&jdpnbM4KAiYpEg^Ho?Uh={)v+KDv2CFy^L`Wj+z1p^cYXFtQ zfl3UsnH1yMYn*e=B5HK);~WS9%O*U?nYVpcVXhZ?R2_;pZZXbUctQ4GvW{i~)RA<1*{53C2liL_VnQJ$%(RfBLqfU5V(W5~>a+ zY2&2;k12Ctt(9&=&38Tt>lJ0To8~Ct-$vUk-e3xa0o9ZTDQ*#qhk-nOc}Z7xFx05l zRc$_Z`PnE7P;R;z5pG!Ng@6OcYrY@s*3vuMrmQ%!R>} z84q$8jr@R~g^Co9zRXcJ$xq8zbl!pMX9A=V@q*eyDEx_b7K`&U!G>pU85<72eu*hW zgQMmfk;XvsCM^)s6E1 zztyUGXWAg)38yiVhl{6P^WnQ0i@}|5fvlCJ@z<(zl~DNn_NiCYt(%N06l(8%S{t&K zO=wLCV#xhZ-UsJZSVv)=TKj$?y3IIu;Jtsbc4R5YvXVm5F3GXZNyW#2Jxfq zgTCHzQDD>xgJ^m4K7yV9#k^VO)2Ul}jRoZve!6qBfXA92Ip7*y36I3jUlK~+TtaZw zKYs{J?hlm$SZhhd=!w@d?97k*MrbB$o@1M<{FWOEsbV118bd6v9Ix}S`3*Iz4i`rDPyYm zOQpMvicK+*jyyMxf@B+hpkl*~r0g!9h1!gIeu_xDD6Xv0gDYdhQCkRLH*x+NG1g9} z+jL5PIMr#E|JMHFa&tX{36VjP)q>KYfyBoS-7j{IV^bG~FH*btkAnv`Qi!dWC9CH~ z{EkT{t&_x;G%93oOMDOxxNXshqUspy>t$cVONZySjwaz_^JOQZ^6QvFe&A@k84)=< z@eicFS9})}?-GbhsXsn<{rKMZAk0?SXwCg?tl_gQbZ!uxjxjsWsr1Mz&5+J3fII!{se8Dh?d$sf-A=IsuWd0%Vv6<)Sjr7LYAquIJ%*KrLi5He zj}U;iLv$xDQdzS_FF3Cqcy$NqOjmny@4|Qn+m;<=_3xd~Mvg>i0vpqhidw z*8iBizk2eQ9ewpOkA5_^622k}vh8;vn+|B)hFz2$tF!eMzDqavj;qcw=OjEBuop%Y zEXPn5#qg$eVy<~9j4Fsgzh+GTzogKMU5O2{fGbd z=46H^d(ruYma%@baDno{%Y;~?9Esi0iOLieaMVE*7m!l)6b9ElUvSvuGtuahfJ2b- s_%V{*uhinm{;S8vlj4W0Pw-4*cRnX|E@F(&J{jeS)1y^=4%^ZH2N$OtwEzGB delta 5749 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 B6@dT%