From b32783c402fa3ab050bda7af443aa02d3ca9c000 Mon Sep 17 00:00:00 2001 From: John Andrews Date: Sun, 10 Apr 2022 18:27:06 +1200 Subject: [PATCH] added FFMPEG Builder nodes --- FileFlows.Plugin.dll | Bin 39936 -> 39936 bytes FileFlows.Plugin.pdb | Bin 21756 -> 21780 bytes .../Audio/FfmpegBuilderAudioAddTrack.cs | 173 ++++++++++ .../Audio/FfmpegBuilderAudioTrackRemover.cs | 54 +++ .../FfmpegBuilderExecutor.cs | 51 +++ .../FfmpegBuilderNodes/FfmpegBuilderNode.cs | 62 ++++ .../FfmpegBuilderNodes/FfmpegBuilderStart.cs | 24 ++ .../Models/FfmpegAudioStream.cs | 36 ++ .../FfmpegBuilderNodes/Models/FfmpegModel.cs | 57 ++++ .../FfmpegBuilderNodes/Models/FfmpegStream.cs | 12 + .../Models/FfmpegSubtitleStream.cs | 22 ++ .../Models/FfmpegVideoStream.cs | 61 ++++ .../FfmpegBuilderSubtitleFormatRemover.cs | 77 +++++ .../FfmpegBuilderSubtitleTrackRemover.cs | 42 +++ .../FfmpegBuilderUnsupportedMP4Subtitles.cs | 25 ++ .../Video/FfmpegBuilderCropBlackBars.cs | 37 +++ .../Video/FfmpegBuilderRemuxToMP4.cs | 12 + .../Video/FfmpegBuilderRemuxToMkv.cs | 12 + .../Video/FfmpegBuilderScaler.cs | 64 ++++ .../Video/FfmpegBuilderVideoEncode.cs | 62 ++++ VideoNodes/GlobalUsings.cs | 9 + VideoNodes/LogicalNodes/DetectBlackBars.cs | 43 +-- .../FfmpegBuilder_BasicTests.cs | 313 ++++++++++++++++++ VideoNodes/VideoNodes.en.json | 139 +++++++- .../PluginInfoGenerator/FileFlows.Plugin.dll | Bin 39936 -> 39936 bytes .../PluginInfoGenerator/FileFlows.Plugin.pdb | Bin 21756 -> 21780 bytes .../FileFlows.ServerShared.dll | Bin 41472 -> 41472 bytes .../FileFlows.ServerShared.pdb | Bin 20544 -> 20544 bytes .../PluginInfoGenerator/FileFlows.Shared.dll | Bin 65024 -> 65536 bytes .../PluginInfoGenerator/FileFlows.Shared.pdb | Bin 31396 -> 31572 bytes .../PluginInfoGenerator/FileFlows.Shared.xml | 5 + .../PluginInfoGenerator.dll | Bin 15360 -> 15360 bytes .../PluginInfoGenerator.pdb | Bin 13440 -> 13440 bytes build/utils/spellcheck/ignoredwords.txt | 4 +- ref/FileFlows.Plugin.dll | Bin 17408 -> 17408 bytes 35 files changed, 1373 insertions(+), 23 deletions(-) create mode 100644 VideoNodes/FfmpegBuilderNodes/Audio/FfmpegBuilderAudioAddTrack.cs create mode 100644 VideoNodes/FfmpegBuilderNodes/Audio/FfmpegBuilderAudioTrackRemover.cs create mode 100644 VideoNodes/FfmpegBuilderNodes/FfmpegBuilderExecutor.cs create mode 100644 VideoNodes/FfmpegBuilderNodes/FfmpegBuilderNode.cs create mode 100644 VideoNodes/FfmpegBuilderNodes/FfmpegBuilderStart.cs create mode 100644 VideoNodes/FfmpegBuilderNodes/Models/FfmpegAudioStream.cs create mode 100644 VideoNodes/FfmpegBuilderNodes/Models/FfmpegModel.cs create mode 100644 VideoNodes/FfmpegBuilderNodes/Models/FfmpegStream.cs create mode 100644 VideoNodes/FfmpegBuilderNodes/Models/FfmpegSubtitleStream.cs create mode 100644 VideoNodes/FfmpegBuilderNodes/Models/FfmpegVideoStream.cs create mode 100644 VideoNodes/FfmpegBuilderNodes/Subtitle/FfmpegBuilderSubtitleFormatRemover.cs create mode 100644 VideoNodes/FfmpegBuilderNodes/Subtitle/FfmpegBuilderSubtitleTrackRemover.cs create mode 100644 VideoNodes/FfmpegBuilderNodes/Subtitle/FfmpegBuilderUnsupportedMP4Subtitles.cs create mode 100644 VideoNodes/FfmpegBuilderNodes/Video/FfmpegBuilderCropBlackBars.cs create mode 100644 VideoNodes/FfmpegBuilderNodes/Video/FfmpegBuilderRemuxToMP4.cs create mode 100644 VideoNodes/FfmpegBuilderNodes/Video/FfmpegBuilderRemuxToMkv.cs create mode 100644 VideoNodes/FfmpegBuilderNodes/Video/FfmpegBuilderScaler.cs create mode 100644 VideoNodes/FfmpegBuilderNodes/Video/FfmpegBuilderVideoEncode.cs create mode 100644 VideoNodes/GlobalUsings.cs create mode 100644 VideoNodes/Tests/FfmpegBuilderTests/FfmpegBuilder_BasicTests.cs diff --git a/FileFlows.Plugin.dll b/FileFlows.Plugin.dll index 111075da1569aaf773ad7c0bbe14b8ab94eacdd4..f3efaf01d33d82a4cb67728e6cabbf399850e02a 100644 GIT binary patch delta 18057 zcmbVz3w#vS_5Ycf-I;ylv5#aoZ?c4hB@Ybmw1FlqWsbGiEPj2>$~@)$HCrbr&8DJsbZ*&3beS1Z%gsqjN9DNLti-c- zyg>394phlWhsF|pa21haHm~btj#0<;>qz7%ZLeyLA{3|%(S$;T0yP>U@|42Ri4C}x z+SfAkg!-`gy!PiflzkJWM1Z1SLLmYa{SyiiD7R80J2+Cqja^_>>$A~RW7ih38;cTv zsznRgIDj?IC=5DOxqL}8|Kvzl2AJPDE(2cT>P}%&gy&MTVISi ziks$`FFQNJ>!kRm>sO>+2$#MKGz?O>@xtv3^v4xTwNJ5Com_uZP{QXK9G?Z9lgMAc z7d3ixwefn6sevOaS4y>LPqg8U_5wzG11fIT%13I^J`h#5=`j{#7$;A8H#OQGn((Rs zrldGjm#xuUEs7EIJQCAku*_Q62%S};cS2E8R%*WGDOCEKpL@FGwK$d<6*sBT0x6OM zj1DlndwaxF0%{ap^R%iGb+TLI4G7m!5zx=-`#|dlvR4(LL{+1h0X;Fq?I2^MVb?M7 z6s%Nf46caIuSQECXt8P(tEfDJG0BZy1o;U1uCQbt^A;Bkg61){mHxa_l=!4xjmrh6#DDs)?Klsa3c zqVYyQN^-RZf?Ff%7DH>;i_ zQXA)SGrA#3R333vq$d=kU@a*twy7&N z7`!axDi7KkyMh>&B@ZgS6qkxePNW6sY{-Rdi;afjIH#;)-tHS6N78!Q^^K}TW#xnQ zy2B2dTlYIa>}N1|^tw;lDvWbHyv8{m-YD0Y5=!mej5b_uGwAR;+V$<$R}VO%<6vr4 zyWg>R;$={Ez8unc7DgFA^75ATP8KbuL|`Z&CG!#`gUOPXllhdtAaptE^cs3}0j|i5Go2WHSE;=mU}5vbCUaq3ufxJQ zH#WsI13j>mz7;4c#~NWhd_=%Q0x4lBM1TUbZYc(vS;0bcb}(YD3wCqLGr`;+>^>1b zm_f@&1Sl~4mO=z5Foc#u1P&ZoLunE?=ELF9y@nRWbI~)e9HW$?E*fvqzCJ4)i=GFKE286A(V@*K5+`;&OiSq1DzTmw?eVQFGBQFRFt>wJwP)&4(?-_)jY zv>pFHH?C4O`t3t<)E`2v{DepC4qa=!nJ*2Dp)UO7d>iKaS%^8RV+2c~d51OFZ+N2S zSD_xQ7QWuMqRw;HKKp?qcT;hNUQ$<#`y<6!KEkl>6pWsyyuVRrU^ryIP;r4#7fh1& zuDT+Sx{j>jRV>9-SJp;xVTx+F>~%8QgwT8(!liKSTlepO|NGpd_VGQi&ex0$$qLev+G~v)hGnyL!s=wHRN;T;Wr}%yu~);{Bkr zZlSu{c`{^E6kUa?WIN=kZ-fILTPBQkPXP3~Z9tjE`o&yf9ALzd7oJe8IVp!@NL5=O zjxA{=-zM_yz!D=?ycE~i9e@;9beUL^np7jUTr?}pKjl}U(Gf3Hy_NM z-=`Ogy#eLeNNIIzbR+`bNLYqE&WdVH9;dOpp{WRHaXb$^^0bcjGcV4|Rr;CN)ZxLZH@UrUOQ!t`OCb5_*xVSIO=+_-W1tUVaPpG=ByjU+I5!4$QBPHq2tW7 zOSuxdR(_f_gH{F6dr(iR-g+cP8x)`GZnfbhndhL>?`)ZA_xWbp?`$>IHnHY9NqDl8 zgl8K>rW@Df!jNlq9~oqBDsUw7;^zhR@&9vPAVQy_mtUlo z4{T|so2%_Xt*l5dZ=169Z9RJHNTq<9pB6O2nH^dz3rcILP-SD#!_FHcia{yE5ucZdm=|@#P^m|c zp;b@Cy(j$o{2u&dvqvnysJ(KZSyj{lIHM>EysM}J_-avC;9rY+0XudW28?wW4Sc=B zxRhik)OK#OONWR#u(%AS*~K?qC3&pX%}++ExndXrPpfb>3D=5Nb6L@9=R|9SC*tIC z*Q~B4zgxoI(wd5E&FYd4x!4a{R0P;(B^0yFrjp^F>k{-j^SzQG%4{>MV_E!R1ks4p zm5nc!FI+Plk=D2_veZ$Jwc%wJ-3<5S0~~Xy(~AmGj+_mURgU@y95AT-bS`)4u}5$% z%TgP#_i^YIT1J^J?@Bz;k(huIwSjj`4td979lOQxoEJY{tGK-Ukv(*6;!)Qwvn2_Z zB!^bD{R_}UA`>>ZwsN4hp}Te0%Y(^Ejl@Pf85mw^z&{*)6kV}uHfvKe%;nLPez+$c zcvKo!m1nn#(Jo-$NtVIVMcW~ymO3gvK~$~Vv%Lnw>x5Qvg5R&zp@|b+ah#ZI`a9)Y zH*r6+vTV4ys8d=T`@o+Z@_DeGZ^&8Sdc=kIM3XJA+A!jxZ4d1^BOYoQ@jjjz$@|VO z@RLK%fhOicHdf~u@nm4CyoU)#x5$XkGjAv@>W7_b%TOnSvpthT%?3+n4>c=fx=xm+ zL|R!}4>68n))3EYJH#W0-XzuC42(U7SopljDp|Y3QLG_lIA!Kmx@eA$tK0;9Rz#XK z;NsQ8J$oAsQ_VeP8I!QTbXac{47C9}R7WEp2~~LvH7~I#yi|^se=ektZbaX zP9n;a$STT5Og;95jLssnb9ru@D_oobMGW^mGK_f)_q!f+go4hpd47lAd4CWFu2gD@ zJqd5W9@{Dxd3?sEKn`{3U==Us^QE;5_Um=O^kAy{CmJZrZ|&pP4eRlHqj|VIOJcDj z;2L5-PnN1^rH&RtU6I==qE^P(Q!v^b_o4oi&TNc5EzXIa#qIrhtD3{HXWF=UtcQ3T z4=j0TF;NLTu-qj=z>Zk&djQWYLk{M0M}(7W(13PBhpr0i9GqIhm`2vLAP zg0qvng^BGz$w!K_j0TR}Vo~fO${MlpLQiDGxvtPzHR$rY${SnhqAy7V>=}I-Vr(ZM z=m~hDuRy4%Rzt-ec|)Z7J&nyoV;rjWrU+kPV8c@OaeL_uERLW@UBuV3nL#WUYNlNf zRlS;Qw)wq-|*8U9NKm~Z7AK=+)U<0O^FK075U_UEa zwj>uE6}^dyjD{OPqHjSwF#rR6y_wxDe;D6@@^--;`!$RG0guF7xF=4~iR_?*dnj#7 zqd1NngXY}v0I6Ol@on^+x&UA(!PH~cZ)EvHDyF|3LuZ0S2yd1Yq1?5I)oe< z?uBmrILa6=FA=F|*!U5HVe2s7OzU2nk9?vF@Q41;d-(eZe=7do#$SawzI$~%Er9&S zN2epHe+uceJdJT)I^#DfTS7rH!fb6yo#4~y%Jd;YovPCq|B%W^BJULJFPeu_xa_j@ z2?3qbau`2MVVoCW+$EY}uzKhfWno;^>7bVlOZ-E#blRe@JXG+#9ImJ-$eN;z!SGz~ zVfi<~!Rk1gfHk0jmf=>^X|2ZkangZ$sV(B=vTBHNV+P~Psat@(GFdLMT0vNGowQso ze7S!@AdSvuv(t+iALQwju5dsDfobH)V*PxD3r_}mXo<2A0WA!D51){mYj|9G+~8yR z&p{rWF5)u-;ReX-uFlZuIAonxNb`k$$Z`6EkTY&Jyq7;%4bhCio1qZ33%-fQTw?7c z4P1zk$f0-Cp_qt;8505pl%2mitAIviug+>uk87ViDo)dP1cwR>@3VTmHucIBpUXk{$qhC;*ho;0fz9h!2 zHhWoE*K`hTHvLjqgA|-iuL|?pwq3$>X>c~ZMq0#2F{Ho)9rsWvy!@?dz6U%7{y-OvuD{MMT zsM>e0Q`g6+_d73d>Y29@xJU48Hp}%Yz8-0xB8H~?BR;a5pilN@**y@Z>rDG#-mwrF+s9?y!)?aCie-M1h@ia=^ z;d~DGrM?|_z5WvLxRWv6`MMMHKLQtpb_T|%^cmgRj3|5pX|oNpm_%H$sBW}N{Z&iEdFN}Vw%LA)sqKP$*Xtx&CldwiMhM&a+A9O6*NX5d^W2g?3EsZtH-A?iS3U8mx# zLg2I%?rYZ|qovuH!y~a!#>3Xf#nmz#Q`yWt&?zVHe&ECu#*Z^PNTtUh7fPS*Rw@w6 zI|}#pw9*B#s`ddY%0A5(~1h?ZuVAzr{I7Gk$*(Hr0}w=lu# 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 CfPLox delta 17583 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 UXYJRTy0HJY;62Bc&N}0N0Y%b0;s5{u diff --git a/FileFlows.Plugin.pdb b/FileFlows.Plugin.pdb index a8cbfced11bee8b6b841f800970f830a373c63fb..341340c631fd847b1f90893ee6aaf362ecc0081c 100644 GIT binary patch delta 3574 zcmZ9O3s6*L6vxk97TD#@%90C<$U{eARFsFVt001`ZxkhUBt@uKCgMv%!6* zPM-)Kwd`Q$x`rczntB{gxw~k+WzW|0wMf&b_#?sQHbj`3#zI}7<h ztvfkM>FY^QYzFtQX*M!-wNWB;9xCr-S`vC|LfcDe)o2Hk@mLH-$b3V~W>*xeL^AR&WC*{K8KE>ITK8_I_UK}FCQ zs1z!Lra^POV`MiwEkJ&cHdh_uvs(No;x?Ifa%IL*33Mrw$5|;g%Wb7@SypmDeIOS! z6e@=HKy^@FcPqvAu+lfs&rpDa2P>55Fic%a%{LfGT|TZJfdMxzTgyaKf&Lkj5!4Lm52-A7l6aG z@r~Y`i7ene&3D0%()>u&_kYZ8S|Sn_#Kjt0F(3x<5NH4fL~9d-g9i#uMHA-M@N3{L zga(0aTKyB?!Gd`M4Dqp>uri{dz68-z;6iY$HbD}Y?~2ZWh=&U{AubZk{s_VBj}**4 ze@X9r=#N%JPmGIW_xb!YTLn(m8U}+`3qFiRf!)*&1!XWT%?WA`=4Wlt zxC6KvF`xP`VuFs^gdZE4xf6IU{6vWT&ftZJ!!=F=Z$>TO)iv*d&<8Zx+n{Yl7L|BA9*mR*~R>Z4~KSkR8mMOJ5)lcwI2pzabXL zp3Vt~CqQq)@1fnXWbh8b9e9twB@&%buv2h1@GimK!Mg?Xt89m;@4+hr? zE<%6WClXaC(3y8QpMv`#hxQHl4jd57cc2dAb)O&ELBT=bcSJppKP1@1&;PJUa6!Fb zZs5W3q&MJ$sG&uJM!~#CjtJ%j9Bsi($m#RBx#2O9;D+yt0TJNi)XF!28=Me6^T`%` zN-!_rG+uFfL+*DL(%6m7Mf8CvXuwZTGM~`QPCSAohtechRlHN52Z3>Z+Lkyyx?nIZE4`haQyv7=J4>GTfYUz3_c&ef;QQ`*wag{#ZZ%FRorc zblFp3&rd0siEoL;m=~W6HDM7^3UxMC22!pOf9d5IO|j%On&N4Qfp3?AN=%eO38tB0 z>?Kp60pC44pgLN;TM%Q5x6ceAy8*Mb;{Oywbu4aa%HrSEI9IIlP<6QC6r=i&tF=1a zwN{ykXj|B+&|@QP;RR4jqLd!)8UgT delta 3621 zcmZ9Pdr(w$6vxlKEX%GeuVqnoS(Oz8TXjU9!U{x?f?+5oJw&W@G*eX6s8#WTTJ|v0 zZcoJu?V(maT2aY&*=y`EJ!Yhwrff#XYMB0Urc7)4p5to%iO7QYw3##LEaGJxPo7y-f%n|O zM4y&`Z&|$be(;KniA5Kb{nbbQdEwHz8;&)*|Cs(1(sbwW!_oF(L>QXxfyP1Wp>LoI zM1x30@Hyn)dSb3p7V;@>Rr@8&P5(m4hyrpZtPG#}cU{vEL^&r8L5L#Y-zo5y`gjVP$bgL; zP$J}kd;vSv1_n_>AR6xudN2@!g~ecDG1MH0rHz3&(gX2WU;@1w;KlRHO>ZME?uFjO zdtuW}858@n{gIcpP4v=EXn)vulNMSLc_u=eh7DIvHi`=GJfdmnsSxZ3-zxYg)H6q; zz7g>x_>;gfMt^5GXQKpgx#3TSpJ@0AsITm2joL)QiFl^LE;M)$Plc+`kYo%H2c9B$ zG$vsl0)HL67U(u`vQa+-TrIc|JhhKCA{P?3_a%sCgKNMZV}LX;Ulo(15Kk9uLtHDE z{TYJUze6zl{2{~RVSY?SR7du{3F5)C%mTb~L%m?G=S9i|H3(*Zj$rP;S1|kc8O-yC zC*ZTm9FPm<3g!Xk31)x3VD=XXX8!@f>@O6|{)4?7oRMFR>J3+4eD1@iz)1hcS*VKn$j!3VJ@utw=9SOC-6 zo1l?kzSpY3ncyH|KJ}l%0Hcfn&#ApU2HXsP1jPPWa0}u%gU5kiLM>m_P4Kgf{#nRv zA}x-xkr;~NXow4Pz-frD8x48jQN{pI!_PMc;B#ZMU_Pv#8Ngd9zOUX6|5?cF=PRc3 z{x=i6E1we$d{jO!mWNvsx zFxS5-7Ra8-KE(GyufZ=cu2?#_L+~ix<2yxSEDClB&IZ3OxB&cyV1AUnDYy*$mf%U? z-GZyZdj!{Fe%dP%D^XxF?{Gc^--ad_pMbBxJA(NNyo-L*=UcW<@F4JhQP2Gc1l#!j z9~22Lcuz1-5W?|fPQVAzp#c+g3g$g>STHZ($N)ZyoH?Gx6LyINPx!uQhzB3T)7xy| z362Y&`NRM|DVP^<29G$ipXd7!GFZdrAo@rY9Ku&mGM~`QMYsiRhPrwU#*H`mE_gd4d$|+?@G;8|9`eu}~DH<5woXSL(A|Q{#(E zes)JuT}i6PN=Hg?{|~zyO6bd^KP{=(udJ`Q>BrwQKWnsQwvV2xudK6;dE#pP!UI=( z*5}VwKM!5<*SVe6pNMN2DsOyr28?a zeced?Se0MN(l1y2tpr0Cr(`KghwiLCuXKcdtM0TY1^R2#=PQ}|>RONPtG!#<5L#W^ Opeh&iAMPl^li@!gdW+fs diff --git a/VideoNodes/FfmpegBuilderNodes/Audio/FfmpegBuilderAudioAddTrack.cs b/VideoNodes/FfmpegBuilderNodes/Audio/FfmpegBuilderAudioAddTrack.cs new file mode 100644 index 00000000..46a88d34 --- /dev/null +++ b/VideoNodes/FfmpegBuilderNodes/Audio/FfmpegBuilderAudioAddTrack.cs @@ -0,0 +1,173 @@ +using FileFlows.VideoNodes.FfmpegBuilderNodes.Models; + +namespace FileFlows.VideoNodes.FfmpegBuilderNodes +{ + public class FfmpegBuilderAudioAddTrack: FfmpegBuilderNode + { + public override string Icon => "fas fa-volume-off"; + + [NumberInt(1)] + [Range(1, 100)] + [DefaultValue(2)] + public int Index { get; set; } + + + [DefaultValue("aac")] + [Select(nameof(CodecOptions), 1)] + public string Codec { get; set; } + + private static List _CodecOptions; + public static List CodecOptions + { + get + { + if (_CodecOptions == null) + { + _CodecOptions = new List + { + new ListOption { Label = "AAC", Value = "aac"}, + new ListOption { Label = "AC3", Value = "ac3"}, + new ListOption { Label = "EAC3", Value = "eac3" }, + new ListOption { Label = "MP3", Value = "mp3"}, + }; + } + return _CodecOptions; + } + } + + [DefaultValue(2f)] + [Select(nameof(ChannelsOptions), 2)] + public float Channels { get; set; } + + private static List _ChannelsOptions; + public static List ChannelsOptions + { + get + { + if (_ChannelsOptions == null) + { + _ChannelsOptions = new List + { + new ListOption { Label = "Same as source", Value = 0}, + new ListOption { Label = "Mono", Value = 1f}, + new ListOption { Label = "Stereo", Value = 2f} + }; + } + return _ChannelsOptions; + } + } + + [Select(nameof(BitrateOptions), 3)] + public int Bitrate { get; set; } + + private static List _BitrateOptions; + public static List BitrateOptions + { + get + { + if (_BitrateOptions == null) + { + _BitrateOptions = new List + { + new ListOption { Label = "Automatic", Value = 0}, + new ListOption { Label = "64 Kbps", Value = 64}, + new ListOption { Label = "96 Kbps", Value = 96}, + new ListOption { Label = "128 Kbps", Value = 128}, + new ListOption { Label = "160 Kbps", Value = 160}, + new ListOption { Label = "192 Kbps", Value = 192}, + new ListOption { Label = "224 Kbps", Value = 224}, + new ListOption { Label = "256 Kbps", Value = 256}, + new ListOption { Label = "288 Kbps", Value = 288}, + new ListOption { Label = "320 Kbps", Value = 320}, + }; + } + return _BitrateOptions; + } + } + + [DefaultValue("eng")] + [TextVariable(4)] + public string Language { get; set; } + + public override int Execute(NodeParameters args) + { + base.Init(args); + + var audio = new FfmpegAudioStream(); + audio.Stream = Model.AudioStreams[0].Stream; + +#pragma warning disable IL2026 // Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code + var bestAudio = Model.AudioStreams.Where(x => System.Text.Json.JsonSerializer.Serialize(x.Stream).ToLower().Contains("commentary") == false) +#pragma warning restore IL2026 // Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code + .OrderBy(x => + { + if (Language != string.Empty) + { + args.Logger?.ILog("Language: " + x.Stream.Language, x); + if (string.IsNullOrEmpty(x.Stream.Language)) + return 50; // no language specified + if (x.Stream.Language?.ToLower() != Language) + return 100; // low priority not the desired language + } + return 0; + }) + .ThenByDescending(x => x.Stream.Channels) + .ThenBy(x => x.Index) + .FirstOrDefault(); + + audio.EncodingParameters.AddRange(GetNewAudioTrackParameters("0:a:" + (bestAudio.Stream.TypeIndex - 1))); + if (Index > Model.AudioStreams.Count) + Model.AudioStreams.Add(audio); + else + Model.AudioStreams.Insert(Math.Max(0, Index - 1), audio); + + return 1; + } + + + private string[] GetNewAudioTrackParameters(string source) + { + if (Channels == 0) + { + // same as source + if (Bitrate == 0) + { + return new[] + { + "-map", source, + "-c:a:{index}", + Codec + }; + } + return new[] + { + "-map", source, + "-c:a:{index}", + Codec, + "-b:a:{index}", Bitrate + "k" + }; + } + else + { + if (Bitrate == 0) + { + return new[] + { + "-map", source, + "-c:a:{index}", + Codec, + "-ac", Channels.ToString() + }; + } + return new[] + { + "-map", source, + "-c:a:{index}", + Codec, + "-ac", Channels.ToString(), + "-b:a:{index}", Bitrate + "k" + }; + } + } + } +} diff --git a/VideoNodes/FfmpegBuilderNodes/Audio/FfmpegBuilderAudioTrackRemover.cs b/VideoNodes/FfmpegBuilderNodes/Audio/FfmpegBuilderAudioTrackRemover.cs new file mode 100644 index 00000000..81e55524 --- /dev/null +++ b/VideoNodes/FfmpegBuilderNodes/Audio/FfmpegBuilderAudioTrackRemover.cs @@ -0,0 +1,54 @@ +namespace FileFlows.VideoNodes.FfmpegBuilderNodes +{ + public class FfmpegBuilderAudioTrackRemover: FfmpegBuilderNode + { + public override string Icon => "fas fa-volume-off"; + + public override int Outputs => 2; + + [Boolean(1)] + public bool RemoveAll { get; set; } + + + [TextVariable(2)] + [ConditionEquals(nameof(RemoveAll), false)] + public string Pattern { get; set; } + + [Boolean(3)] + [ConditionEquals(nameof(RemoveAll), false)] + public bool NotMatching { get; set; } + + [Boolean(4)] + [ConditionEquals(nameof(RemoveAll), false)] + public bool UseLanguageCode { get; set; } + + public override int Execute(NodeParameters args) + { + this.Init(args); + bool removing = false; + var regex = new Regex(this.Pattern, RegexOptions.IgnoreCase); + foreach(var audio in Model.AudioStreams) + { + if (RemoveAll) + { + audio.Deleted = true; + removing = true; + continue; + } + string str = UseLanguageCode ? audio.Stream.Language : audio.Stream.Title; + if (string.IsNullOrEmpty(str) == false) // if empty we always use this since we have no info to go on + { + bool matches = regex.IsMatch(str); + if (NotMatching) + matches = !matches; + if (matches) + { + audio.Deleted = true; + removing = true; + } + } + } + return removing ? 1 : 2; + } + } +} diff --git a/VideoNodes/FfmpegBuilderNodes/FfmpegBuilderExecutor.cs b/VideoNodes/FfmpegBuilderNodes/FfmpegBuilderExecutor.cs new file mode 100644 index 00000000..3adbf07d --- /dev/null +++ b/VideoNodes/FfmpegBuilderNodes/FfmpegBuilderExecutor.cs @@ -0,0 +1,51 @@ +using FileFlows.Plugin; +using FileFlows.VideoNodes.FfmpegBuilderNodes.Models; + +namespace FileFlows.VideoNodes.FfmpegBuilderNodes +{ + public class FfmpegBuilderExecutor: FfmpegBuilderNode + { + public override string Icon => "far fa-file-video"; + public override int Inputs => 1; + public override int Outputs => 2; + public override FlowElementType Type => FlowElementType.BuildEnd; + + public override int Execute(NodeParameters args) + { + this.Init(args); + var model = this.Model; + List ffArgs = new List(); + bool hasChange = false; + int actualIndex = 0; + int currentType = 0; + foreach (var item in model.VideoStreams.Select((x, index) => (stream: (FfmpegStream)x, index, type: 1)).Union( + model.AudioStreams.Select((x, index) => (stream: (FfmpegStream)x, index, type: 2))).Union( + model.SubtitleStreams.Select((x, index) => (stream: (FfmpegStream)x, index, type: 3)))) + { + if (item.stream.Deleted) + { + hasChange = true; + continue; + } + if (currentType != item.type) + { + actualIndex = 0; + currentType = item.type; + } + ffArgs.AddRange(item.stream.GetParameters(actualIndex)); + hasChange |= item.stream.HasChange; + ++actualIndex; + } + + if (hasChange == false && (string.IsNullOrWhiteSpace(model.Extension) || args.WorkingFile.ToLower().EndsWith("." + model.Extension.ToLower()))) + return 2; // nothing to do + + string extension = model.Extension?.EmptyAsNull() ?? "mkv"; + + if (Encode(args, ffmpegExe, ffArgs, extension) == false) + return -1; + + return 1; + } + } +} diff --git a/VideoNodes/FfmpegBuilderNodes/FfmpegBuilderNode.cs b/VideoNodes/FfmpegBuilderNodes/FfmpegBuilderNode.cs new file mode 100644 index 00000000..cc6d81e2 --- /dev/null +++ b/VideoNodes/FfmpegBuilderNodes/FfmpegBuilderNode.cs @@ -0,0 +1,62 @@ +using FileFlows.Plugin; +using FileFlows.VideoNodes.FfmpegBuilderNodes.Models; + +namespace FileFlows.VideoNodes.FfmpegBuilderNodes +{ + public abstract class FfmpegBuilderNode: EncodingNode + { + private const string MODEL_KEY = "FFMPEG_BUILDER_MODEL"; + protected string ffmpegExe; + + public override int Inputs => 1; + public override int Outputs => 1; + public override string Icon => "far fa-file-video"; + public override FlowElementType Type => FlowElementType.BuildPart; + + protected void Init(NodeParameters args) + { + this.args = args; + this.ffmpegExe = GetFFMpegExe(args); + if (string.IsNullOrEmpty(ffmpegExe)) + throw new Exception("FFMPEG not found"); + + if (Model == null) + throw new Exception("FFMPEG Builder Model not set, use the \"FFMPEG Builder Start\" node to first"); + } + + public override int Execute(NodeParameters args) + { + return 1; + } + + protected FfmpegModel Model + { + get + { + if (args.Variables.ContainsKey(MODEL_KEY)) + return args.Variables[MODEL_KEY] as FfmpegModel; + return null; + } + set + { + if (args.Variables.ContainsKey(MODEL_KEY)) + { + if (value == null) + args.Variables.Remove(MODEL_KEY); + else + args.Variables[MODEL_KEY] = value; + } + else if(value != null) + args.Variables.Add(MODEL_KEY, value); + } + } + + protected string[] SplitCommand(string cmd) + { + return Regex.Matches(cmd, @"[\""].+?[\""]|[^ ]+") + .Cast() + .Select(x => x.Value.Trim('"')) + .ToArray(); + } + } +} diff --git a/VideoNodes/FfmpegBuilderNodes/FfmpegBuilderStart.cs b/VideoNodes/FfmpegBuilderNodes/FfmpegBuilderStart.cs new file mode 100644 index 00000000..93326014 --- /dev/null +++ b/VideoNodes/FfmpegBuilderNodes/FfmpegBuilderStart.cs @@ -0,0 +1,24 @@ +using FileFlows.Plugin; + +namespace FileFlows.VideoNodes.FfmpegBuilderNodes +{ + public class FfmpegBuilderStart: FfmpegBuilderNode + { + public override int Inputs => 1; + public override int Outputs => 1; + public override string Icon => "far fa-file-video"; + public override FlowElementType Type => FlowElementType.BuildStart; + + public override int Execute(NodeParameters args) + { + this.args = args; + VideoInfo videoInfo = GetVideoInfo(args); + if (videoInfo == null) + return -1; + + this.Model = Models.FfmpegModel.CreateModel(videoInfo); + this.Init(args); + return 1; + } + } +} diff --git a/VideoNodes/FfmpegBuilderNodes/Models/FfmpegAudioStream.cs b/VideoNodes/FfmpegBuilderNodes/Models/FfmpegAudioStream.cs new file mode 100644 index 00000000..1e1ca200 --- /dev/null +++ b/VideoNodes/FfmpegBuilderNodes/Models/FfmpegAudioStream.cs @@ -0,0 +1,36 @@ +namespace FileFlows.VideoNodes.FfmpegBuilderNodes.Models +{ + public class FfmpegAudioStream:FfmpegStream + { + public AudioStream Stream { get; set; } + public override bool HasChange => false; + + private List _EncodingParameters = new List(); + public List EncodingParameters + { + get => _EncodingParameters; + set + { + _EncodingParameters = value ?? new List(); + } + } + public override string[] GetParameters(int outputIndex) + { + if (Deleted) + return new string[] { }; + + var results = new List { "-map", "0:a:" + (Stream.TypeIndex - 1), "-c:a:" + outputIndex }; + if (EncodingParameters.Any() == false) + { + results.Add("copy"); + return results.ToArray(); + } + if (EncodingParameters[0] == "-map") + results.Clear(); + + results.AddRange(EncodingParameters.Select(x => x.Replace("{index}", outputIndex.ToString()))); + + return results.ToArray(); + } + } +} diff --git a/VideoNodes/FfmpegBuilderNodes/Models/FfmpegModel.cs b/VideoNodes/FfmpegBuilderNodes/Models/FfmpegModel.cs new file mode 100644 index 00000000..59a9218c --- /dev/null +++ b/VideoNodes/FfmpegBuilderNodes/Models/FfmpegModel.cs @@ -0,0 +1,57 @@ +namespace FileFlows.VideoNodes.FfmpegBuilderNodes.Models +{ + public class FfmpegModel + { + private List _VideoStreams = new List(); + public List VideoStreams + { + get => _VideoStreams; + set => _VideoStreams = value ?? new List(); + } + private List _AudioStreams = new List(); + public List AudioStreams + { + get => _AudioStreams; + set => _AudioStreams = value ?? new List(); + } + private List _SubtitleStreams = new List(); + public List SubtitleStreams + { + get => _SubtitleStreams; + set => _SubtitleStreams = value ?? new List(); + } + + public string Extension { get; set; } + + + internal static FfmpegModel CreateModel(VideoInfo info) + { + var model = new FfmpegModel(); + foreach (var item in info.VideoStreams.Select((stream, index) => (stream, index))) + { + model.VideoStreams.Add(new FfmpegVideoStream + { + Index = item.index, + Stream = item.stream, + }); + } + foreach (var item in info.AudioStreams.Select((stream, index) => (stream, index))) + { + model.AudioStreams.Add(new FfmpegAudioStream + { + Index = item.index, + Stream = item.stream, + }); + } + foreach (var item in info.SubtitleStreams.Select((stream, index) => (stream, index))) + { + model.SubtitleStreams.Add(new FfmpegSubtitleStream + { + Index = item.index, + Stream = item.stream, + }); + } + return model; + } + } +} diff --git a/VideoNodes/FfmpegBuilderNodes/Models/FfmpegStream.cs b/VideoNodes/FfmpegBuilderNodes/Models/FfmpegStream.cs new file mode 100644 index 00000000..cf469ba4 --- /dev/null +++ b/VideoNodes/FfmpegBuilderNodes/Models/FfmpegStream.cs @@ -0,0 +1,12 @@ +namespace FileFlows.VideoNodes.FfmpegBuilderNodes.Models +{ + public abstract class FfmpegStream + { + public bool Deleted { get; set; } + public int Index { get; set; } + + public abstract bool HasChange { get; } + + public abstract string[] GetParameters(int outputIndex); + } +} diff --git a/VideoNodes/FfmpegBuilderNodes/Models/FfmpegSubtitleStream.cs b/VideoNodes/FfmpegBuilderNodes/Models/FfmpegSubtitleStream.cs new file mode 100644 index 00000000..e05cb3f3 --- /dev/null +++ b/VideoNodes/FfmpegBuilderNodes/Models/FfmpegSubtitleStream.cs @@ -0,0 +1,22 @@ +namespace FileFlows.VideoNodes.FfmpegBuilderNodes.Models +{ + public class FfmpegSubtitleStream : FfmpegStream + { + public SubtitleStream Stream { get; set; } + + public override bool HasChange => false; + + public override string[] GetParameters(int outputIndex) + { + if (Deleted) + return new string[] { }; + + var results = new List { "-map", "0:s:" + outputIndex, "-c:s:" + (Stream.TypeIndex - 1) }; + //if (EncodingParameters.Any() == false) + { + results.Add("copy"); + return results.ToArray(); + } + } + } +} diff --git a/VideoNodes/FfmpegBuilderNodes/Models/FfmpegVideoStream.cs b/VideoNodes/FfmpegBuilderNodes/Models/FfmpegVideoStream.cs new file mode 100644 index 00000000..f915afd4 --- /dev/null +++ b/VideoNodes/FfmpegBuilderNodes/Models/FfmpegVideoStream.cs @@ -0,0 +1,61 @@ +namespace FileFlows.VideoNodes.FfmpegBuilderNodes.Models +{ + public class FfmpegVideoStream : FfmpegStream + { + public VideoStream Stream { get; set; } + + private List _Filter = new List(); + public List Filter + { + get => _Filter; + set + { + _Filter = value ?? new List(); + } + } + + private List _EncodingParameters = new List(); + public List EncodingParameters + { + get => _EncodingParameters; + set + { + _EncodingParameters = value ?? new List(); + } + } + public override bool HasChange => EncodingParameters.Any() || Filter.Any(); + + public override string[] GetParameters(int outputIndex) + { + if (Deleted) + return new string[] { }; + + var results = new List { "-map", "0:v:" + outputIndex }; + if (Filter.Any() == false && EncodingParameters.Any() == false) + { + results.Add("-c:v:" + Stream.TypeIndex); + results.Add("copy"); + return results.ToArray(); + } + + if (EncodingParameters.Any()) + { + results.Add("-c:v:" + Stream.TypeIndex); + results.AddRange(EncodingParameters.Select(x => x.Replace("{index}", outputIndex.ToString()))); + } + else + { + // we need to set this codec since a filter will be applied, so we cant copy it. + //results.Add("copy"); + } + + if (Filter.Any()) + { + results.Add("-vf"); + results.Add(String.Join(", ", Filter)); + } + + return results.ToArray(); + } + } +} diff --git a/VideoNodes/FfmpegBuilderNodes/Subtitle/FfmpegBuilderSubtitleFormatRemover.cs b/VideoNodes/FfmpegBuilderNodes/Subtitle/FfmpegBuilderSubtitleFormatRemover.cs new file mode 100644 index 00000000..e8c1f6c2 --- /dev/null +++ b/VideoNodes/FfmpegBuilderNodes/Subtitle/FfmpegBuilderSubtitleFormatRemover.cs @@ -0,0 +1,77 @@ +namespace FileFlows.VideoNodes.FfmpegBuilderNodes +{ + public class FfmpegBuilderSubtitleFormatRemover : FfmpegBuilderNode + { + public override string Icon => "fas fa-comment"; + public override int Outputs => 2; + + [Boolean(1)] + public bool RemoveAll { get; set; } + + [Checklist(nameof(Options), 2)] + public List SubtitlesToRemove { get; set; } + + private static List _Options; + public static List Options + { + get + { + if (_Options == null) + { + _Options = new List + { + new ListOption { Value = "mov_text", Label = "3GPP Timed Text subtitle"}, + new ListOption { Value = "ssa", Label = "ASS (Advanced SubStation Alpha) subtitle (codec ass)"}, + new ListOption { Value = "ass", Label = "ASS (Advanced SubStation Alpha) subtitle"}, + new ListOption { Value = "xsub", Label = "DivX subtitles (XSUB)" }, + new ListOption { Value = "dvbsub", Label = "DVB subtitles (codec dvb_subtitle)"}, + new ListOption { Value = "dvdsub", Label = "DVD subtitles (codec dvd_subtitle)"}, + new ListOption { Value = "dvb_teletext", Label = "DVB/Teletext Format"}, + new ListOption { Value = "text", Label = "Raw text subtitle"}, + new ListOption { Value = "subrip", Label = "SubRip subtitle"}, + new ListOption { Value = "srt", Label = "SubRip subtitle (codec subrip)"}, + new ListOption { Value = "ttml", Label = "TTML subtitle"}, + new ListOption { Value = "webvtt", Label = "WebVTT subtitle"}, + }; + } + return _Options; + } + } + + + public override int Execute(NodeParameters args) + { + this.Init(args); + + if (RemoveAll) + { + if (Model.SubtitleStreams.Any() == false) + return 2; + foreach (var stream in Model.SubtitleStreams) + stream.Deleted = true; + return 1; + } + + + var removeCodecs = SubtitlesToRemove?.Where(x => string.IsNullOrWhiteSpace(x) == false)?.Select(x => x.ToLower())?.ToList() ?? new List(); + + if (removeCodecs.Count == 0) + return 2; // nothing to remove + + + bool removing = false; + foreach (var sub in Model.SubtitleStreams) + { + args.Logger?.ILog("Subtitle found: " + sub.Stream.Codec + ", " + sub.Stream.Title); + if (removeCodecs.Contains(sub.Stream.Codec.ToLower())) + { + sub.Deleted = true; + removing = true; + continue; + } + } + + return removing ? 1 : 2; + } + } +} diff --git a/VideoNodes/FfmpegBuilderNodes/Subtitle/FfmpegBuilderSubtitleTrackRemover.cs b/VideoNodes/FfmpegBuilderNodes/Subtitle/FfmpegBuilderSubtitleTrackRemover.cs new file mode 100644 index 00000000..db73f971 --- /dev/null +++ b/VideoNodes/FfmpegBuilderNodes/Subtitle/FfmpegBuilderSubtitleTrackRemover.cs @@ -0,0 +1,42 @@ +namespace FileFlows.VideoNodes.FfmpegBuilderNodes +{ + public class FfmpegBuilderSubtitleTrackRemover : FfmpegBuilderNode + { + public override string Icon => "fas fa-comment"; + + public override int Outputs => 2; + + + [TextVariable(1)] + public string Pattern { get; set; } + + [Boolean(2)] + public bool NotMatching { get; set; } + + [Boolean(3)] + public bool UseLanguageCode { get; set; } + + public override int Execute(NodeParameters args) + { + this.Init(args); + bool removing = false; + var regex = new Regex(this.Pattern, RegexOptions.IgnoreCase); + foreach(var stream in Model.SubtitleStreams) + { + string str = UseLanguageCode ? stream.Stream.Language : stream.Stream.Title; + if (string.IsNullOrEmpty(str) == false) // if empty we always use this since we have no info to go on + { + bool matches = regex.IsMatch(str); + if (NotMatching) + matches = !matches; + if (matches) + { + stream.Deleted = true; + removing = true; + } + } + } + return removing ? 1 : 2; + } + } +} diff --git a/VideoNodes/FfmpegBuilderNodes/Subtitle/FfmpegBuilderUnsupportedMP4Subtitles.cs b/VideoNodes/FfmpegBuilderNodes/Subtitle/FfmpegBuilderUnsupportedMP4Subtitles.cs new file mode 100644 index 00000000..dbcf75b6 --- /dev/null +++ b/VideoNodes/FfmpegBuilderNodes/Subtitle/FfmpegBuilderUnsupportedMP4Subtitles.cs @@ -0,0 +1,25 @@ +//namespace FileFlows.VideoNodes.FfmpegBuilderNodes +//{ +// public class FfmpegBuilderUnsupportedMP4Subtitles : FfmpegBuilderNode +// { +// public override string Icon => "fas fa-comment"; + +// public override int Outputs => 2; + +// public override int Execute(NodeParameters args) +// { +// this.Init(args); +// bool removing = false; +// string[] unsupported = new[] { "" }; +// foreach (var stream in Model.SubtitleStreams) +// { +// if (unsupported.Contains(stream.Stream.Codec?.ToLower())) +// { +// stream.Deleted = true; +// removing = true; +// } +// } +// return removing ? 1 : 2; +// } +// } +//} diff --git a/VideoNodes/FfmpegBuilderNodes/Video/FfmpegBuilderCropBlackBars.cs b/VideoNodes/FfmpegBuilderNodes/Video/FfmpegBuilderCropBlackBars.cs new file mode 100644 index 00000000..bf9e1cbf --- /dev/null +++ b/VideoNodes/FfmpegBuilderNodes/Video/FfmpegBuilderCropBlackBars.cs @@ -0,0 +1,37 @@ +namespace FileFlows.VideoNodes.FfmpegBuilderNodes +{ + public class FfmpegBuilderCropBlackBars : FfmpegBuilderNode + { + [NumberInt(1)] + public int CroppingThreshold { get; set; } + public override int Outputs => 2; + public override int Execute(NodeParameters args) + { + base.Init(args); + + string ffmpeg = GetFFMpegExe(args); + if (string.IsNullOrEmpty(ffmpeg)) + return -1; + + var videoInfo = GetVideoInfo(args); + if (videoInfo == null || videoInfo.VideoStreams?.Any() != true) + return -1; + + + string crop = DetectBlackBars.Detect(ffmpeg, videoInfo, args, this.CroppingThreshold); + if (string.IsNullOrWhiteSpace(crop)) + return 2; + + //var parts = crop.Split(':'); + ////parts[2] = "iw-" + parts[2]; + ////parts[3] = "ih-" + parts[3]; + //crop = String.Join(":", parts.Take(2)); + + args.Logger?.ILog("Black bars detected, crop: " + crop); + + var video = Model.VideoStreams[0]; + video.Filter.AddRange(new[] { "crop=" + crop }); + return 1; + } + } +} diff --git a/VideoNodes/FfmpegBuilderNodes/Video/FfmpegBuilderRemuxToMP4.cs b/VideoNodes/FfmpegBuilderNodes/Video/FfmpegBuilderRemuxToMP4.cs new file mode 100644 index 00000000..5fc00f23 --- /dev/null +++ b/VideoNodes/FfmpegBuilderNodes/Video/FfmpegBuilderRemuxToMP4.cs @@ -0,0 +1,12 @@ +namespace FileFlows.VideoNodes.FfmpegBuilderNodes +{ + public class FfmpegBuilderRemuxToMP4: FfmpegBuilderNode + { + public override int Execute(NodeParameters args) + { + base.Init(args); + this.Model.Extension = "mp4"; + return 1; + } + } +} diff --git a/VideoNodes/FfmpegBuilderNodes/Video/FfmpegBuilderRemuxToMkv.cs b/VideoNodes/FfmpegBuilderNodes/Video/FfmpegBuilderRemuxToMkv.cs new file mode 100644 index 00000000..9b7844cf --- /dev/null +++ b/VideoNodes/FfmpegBuilderNodes/Video/FfmpegBuilderRemuxToMkv.cs @@ -0,0 +1,12 @@ +namespace FileFlows.VideoNodes.FfmpegBuilderNodes +{ + public class FfmpegBuilderRemuxToMkv: FfmpegBuilderNode + { + public override int Execute(NodeParameters args) + { + base.Init(args); + this.Model.Extension = "mkv"; + return 1; + } + } +} diff --git a/VideoNodes/FfmpegBuilderNodes/Video/FfmpegBuilderScaler.cs b/VideoNodes/FfmpegBuilderNodes/Video/FfmpegBuilderScaler.cs new file mode 100644 index 00000000..562967ae --- /dev/null +++ b/VideoNodes/FfmpegBuilderNodes/Video/FfmpegBuilderScaler.cs @@ -0,0 +1,64 @@ +namespace FileFlows.VideoNodes.FfmpegBuilderNodes +{ + public class FfmpegBuilderScaler : FfmpegBuilderNode + { + [Boolean(2)] + public bool Force { get; set; } + + + [Select(nameof(ResolutionOptions), 1)] + public string Resolution { get; set; } + + + private static List _ResolutionOptions; + public static List ResolutionOptions + { + get + { + if (_ResolutionOptions == null) + { + _ResolutionOptions = new List + { + // we use -2 here so the width is divisible by 2 and automatically scaled to + // the appropriate height, if we forced the height it could be stretched + new ListOption { Value = "640:-2", Label = "480P"}, + new ListOption { Value = "1280:-2", Label = "720P"}, + new ListOption { Value = "1920:-2", Label = "1080P"}, + new ListOption { Value = "3840:-2", Label = "4K" } + }; + } + return _ResolutionOptions; + } + } + public override int Outputs => 2; + public override int Execute(NodeParameters args) + { + base.Init(args); + + string ffmpeg = GetFFMpegExe(args); + if (string.IsNullOrEmpty(ffmpeg)) + return -1; + + var videoInfo = GetVideoInfo(args); + if (videoInfo == null || videoInfo.VideoStreams?.Any() != true) + return -1; + + if (Force == false) + { + var resolution = ResolutionHelper.GetResolution(videoInfo); + if (resolution == ResolutionHelper.Resolution.r1080p && Resolution.StartsWith("1920")) + return 2; + else if (resolution == ResolutionHelper.Resolution.r4k && Resolution.StartsWith("3840")) + return 2; + else if (resolution == ResolutionHelper.Resolution.r720p && Resolution.StartsWith("1280")) + return 2; + else if (resolution == ResolutionHelper.Resolution.r480p && Resolution.StartsWith("640")) + return 2; + } + + Model.VideoStreams[0].Filter.AddRange(new[] { $"scale={Resolution}:flags=lanczos" }); + + return 1; + } + } +} diff --git a/VideoNodes/FfmpegBuilderNodes/Video/FfmpegBuilderVideoEncode.cs b/VideoNodes/FfmpegBuilderNodes/Video/FfmpegBuilderVideoEncode.cs new file mode 100644 index 00000000..afdc685a --- /dev/null +++ b/VideoNodes/FfmpegBuilderNodes/Video/FfmpegBuilderVideoEncode.cs @@ -0,0 +1,62 @@ +namespace FileFlows.VideoNodes.FfmpegBuilderNodes +{ + public class FfmpegBuilderVideoEncode:FfmpegBuilderNode + { + public override int Outputs => 2; + + [DefaultValue("hevc")] + [TextVariable(1)] + public string VideoCodec { get; set; } + + [DefaultValue("hevc_nvenc -preset hq -crf 23")] + [TextVariable(2)] + public string VideoCodecParameters { get; set; } + + [Boolean(3)] + public bool Force { get; set; } + + public override int Execute(NodeParameters args) + { + base.Init(args); + + string codec = args.ReplaceVariables(VideoCodec ?? string.Empty); + string parameters = args.ReplaceVariables(VideoCodecParameters ?? codec); + + if (string.IsNullOrWhiteSpace(parameters)) + return 1; // nothing to do + + parameters = CheckVideoCodec(ffmpegExe, parameters); + + bool encoding = false; + foreach (var stream in Model.VideoStreams) + { + if(Force == false) + { + if (IsSameVideoCodec(stream.Stream.Codec, this.VideoCodec)) + continue; + } + stream.EncodingParameters.Clear(); + stream.EncodingParameters.AddRange(SplitCommand(parameters)); + encoding = true; + } + return encoding ? 1 : 2; + } + + protected bool IsSameVideoCodec(string current, string wanted) + { + wanted = ReplaceCommon(wanted); + current = ReplaceCommon(current); + + return wanted == current; + + string ReplaceCommon(string input) + { + input = input.ToLower(); + input = Regex.Replace(input, "^(divx|xvid|m(-)?peg(-)4)$", "mpeg4", RegexOptions.IgnoreCase); + input = Regex.Replace(input, "^(hevc|h[\\.x\\-]?265)$", "h265", RegexOptions.IgnoreCase); + input = Regex.Replace(input, "^(h[\\.x\\-]?264)$", "h264", RegexOptions.IgnoreCase); + return input; + } + } + } +} diff --git a/VideoNodes/GlobalUsings.cs b/VideoNodes/GlobalUsings.cs new file mode 100644 index 00000000..8c2a7e49 --- /dev/null +++ b/VideoNodes/GlobalUsings.cs @@ -0,0 +1,9 @@ +global using System; +global using System.Linq; +global using System.Collections.Generic; +global using System.Text.RegularExpressions; +global using System.ComponentModel.DataAnnotations; +global using FileFlows.Plugin; +global using FileFlows.Plugin.Attributes; +global using System.ComponentModel; + diff --git a/VideoNodes/LogicalNodes/DetectBlackBars.cs b/VideoNodes/LogicalNodes/DetectBlackBars.cs index 75654e64..6c532cba 100644 --- a/VideoNodes/LogicalNodes/DetectBlackBars.cs +++ b/VideoNodes/LogicalNodes/DetectBlackBars.cs @@ -40,20 +40,8 @@ namespace FileFlows.VideoNodes if (videoInfo == null || videoInfo.VideoStreams?.Any() != true) return -1; - int vidWidth = videoInfo.VideoStreams[0].Width; - int vidHeight = videoInfo.VideoStreams[0].Height; - if (vidWidth < 1) - { - args.Logger?.ELog("Failed to find video width"); - return -1; - } - if (vidHeight < 1) - { - args.Logger?.ELog("Failed to find video height"); - return -1; - } - string crop = Execute(ffmpeg, args.WorkingFile, args, vidWidth, vidHeight); + string crop = Detect(ffmpeg, videoInfo, args, this.CroppingThreshold); if (crop == string.Empty) return 2; @@ -66,7 +54,24 @@ namespace FileFlows.VideoNodes return 1; } - public string Execute(string ffplay, string file, NodeParameters args, int vidWidth, int vidHeight) + public static string Detect(string ffmpeg, VideoInfo videoInfo, NodeParameters args, int threshold) + { + int vidWidth = videoInfo.VideoStreams[0].Width; + int vidHeight = videoInfo.VideoStreams[0].Height; + if (vidWidth < 1) + { + args.Logger?.ELog("Failed to find video width"); + return string.Empty; + } + if (vidHeight < 1) + { + args.Logger?.ELog("Failed to find video height"); + return string.Empty; + } + return Execute(ffmpeg, args.WorkingFile, args, vidWidth, vidHeight, threshold); + } + + public static string Execute(string ffplay, string file, NodeParameters args, int vidWidth, int vidHeight, int threshold) { try { @@ -74,7 +79,7 @@ namespace FileFlows.VideoNodes int y = int.MaxValue; int width = 0; int height = 0; - foreach (int ss in new int[] { 60, 120, 240, 360 }) // check at multiple times + foreach (int ss in new int[] { 60, 100, 240, 360 }) // check at multiple times { using (var process = new Process()) { @@ -127,13 +132,13 @@ namespace FileFlows.VideoNodes if (y == int.MaxValue) y = 0; - if (CroppingThreshold < 0) - CroppingThreshold = 0; + if (threshold < 0) + threshold = 0; args.Logger?.DLog($"Video dimensions: {vidWidth}x{vidHeight}"); - var willCrop = TestAboveThreshold(vidWidth, vidHeight, width, height, CroppingThreshold); - args.Logger?.ILog($"Crop detection, x:{x}, y:{y}, width: {width}, height: {height}, total:{willCrop.diff}, threshold:{CroppingThreshold}, above threshold: {willCrop}"); + var willCrop = TestAboveThreshold(vidWidth, vidHeight, width, height, threshold); + args.Logger?.ILog($"Crop detection, x:{x}, y:{y}, width: {width}, height: {height}, total:{willCrop.diff}, threshold:{threshold}, above threshold: {willCrop}"); return willCrop.crop ? $"{width}:{height}:{x}:{y}" : string.Empty; } diff --git a/VideoNodes/Tests/FfmpegBuilderTests/FfmpegBuilder_BasicTests.cs b/VideoNodes/Tests/FfmpegBuilderTests/FfmpegBuilder_BasicTests.cs new file mode 100644 index 00000000..c7851974 --- /dev/null +++ b/VideoNodes/Tests/FfmpegBuilderTests/FfmpegBuilder_BasicTests.cs @@ -0,0 +1,313 @@ +#if(DEBUG) + +using FileFlows.VideoNodes.FfmpegBuilderNodes; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using VideoNodes.Tests; + +namespace FileFlows.VideoNodes.Tests.FfmpegBuilderTests +{ + [TestClass] + public class FfmpegBuilder_BasicTests + { + [TestMethod] + public void FfmpegBuilder_AddAc3Aac() + { + const string file = @"D:\videos\unprocessed\basic.mkv"; + var logger = new TestLogger(); + const string ffmpeg = @"C:\utils\ffmpeg\ffmpeg.exe"; + var vi = new VideoInfoHelper(ffmpeg, logger); + var vii = vi.Read(file); + var args = new NodeParameters(file, logger, false, string.Empty); + args.GetToolPathActual = (string tool) => ffmpeg; + args.TempPath = @"D:\videos\temp"; + args.Parameters.Add("VideoInfo", vii); + + + FfmpegBuilderStart ffStart = new (); + Assert.AreEqual(1, ffStart.Execute(args)); + + FfmpegBuilderVideoEncode ffEncode = new (); + ffEncode.VideoCodec = "h264"; + ffEncode.Execute(args); + + + FfmpegBuilderAudioAddTrack ffAddAudio = new (); + ffAddAudio.Codec = "ac3"; + ffAddAudio.Index = 1; + ffAddAudio.Execute(args); + + FfmpegBuilderAudioAddTrack ffAddAudio2 = new(); + ffAddAudio2.Codec = "aac"; + ffAddAudio2.Index = 2; + ffAddAudio2.Execute(args); + + FfmpegBuilderExecutor ffExecutor = new(); + int result = ffExecutor.Execute(args); + + string log = logger.ToString(); + Assert.AreEqual(1, result); + } + + [TestMethod] + public void FfmpegBuilder_AddAc3AacMp4NoSubs() + { + const string file = @"D:\videos\unprocessed\basic.mkv"; + var logger = new TestLogger(); + const string ffmpeg = @"C:\utils\ffmpeg\ffmpeg.exe"; + var vi = new VideoInfoHelper(ffmpeg, logger); + var vii = vi.Read(file); + var args = new NodeParameters(file, logger, false, string.Empty); + args.GetToolPathActual = (string tool) => ffmpeg; + args.TempPath = @"D:\videos\temp"; + args.Parameters.Add("VideoInfo", vii); + + + FfmpegBuilderStart ffStart = new(); + Assert.AreEqual(1, ffStart.Execute(args)); + + FfmpegBuilderVideoEncode ffEncode = new(); + ffEncode.VideoCodec = "h264"; + ffEncode.Execute(args); + + FfmpegBuilderRemuxToMP4 ffMp4 = new(); + ffMp4.Execute(args); + + + FfmpegBuilderSubtitleFormatRemover ffSubRemover = new(); + ffSubRemover.RemoveAll = true; + ffSubRemover.Execute(args); + + + FfmpegBuilderAudioAddTrack ffAddAudio = new(); + ffAddAudio.Codec = "ac3"; + ffAddAudio.Index = 1; + ffAddAudio.Execute(args); + + FfmpegBuilderAudioAddTrack ffAddAudio2 = new(); + ffAddAudio2.Codec = "aac"; + ffAddAudio2.Index = 2; + ffAddAudio2.Execute(args); + + FfmpegBuilderExecutor ffExecutor = new(); + int result = ffExecutor.Execute(args); + + string log = logger.ToString(); + Assert.AreEqual(1, result); + } + + [TestMethod] + public void FfmpegBuilder_AddAc3AacMp4NoSubs_BlackBars() + { + const string file = @"D:\videos\unprocessed\blackbars.mkv"; + var logger = new TestLogger(); + const string ffmpeg = @"C:\utils\ffmpeg\ffmpeg.exe"; + var vi = new VideoInfoHelper(ffmpeg, logger); + var vii = vi.Read(file); + var args = new NodeParameters(file, logger, false, string.Empty); + args.GetToolPathActual = (string tool) => ffmpeg; + args.TempPath = @"D:\videos\temp"; + args.Parameters.Add("VideoInfo", vii); + + + FfmpegBuilderStart ffStart = new(); + Assert.AreEqual(1, ffStart.Execute(args)); + + FfmpegBuilderVideoEncode ffEncode = new(); + ffEncode.VideoCodec = "h265"; + ffEncode.Execute(args); + + FfmpegBuilderRemuxToMP4 ffMp4 = new(); + ffMp4.Execute(args); + + FfmpegBuilderCropBlackBars ffCropBlackBars = new(); + ffCropBlackBars.CroppingThreshold = 10; + ffCropBlackBars.Execute(args); + + FfmpegBuilderSubtitleFormatRemover ffSubRemover = new(); + ffSubRemover.RemoveAll = true; + ffSubRemover.Execute(args); + + + FfmpegBuilderAudioAddTrack ffAddAudio = new(); + ffAddAudio.Codec = "ac3"; + ffAddAudio.Index = 1; + ffAddAudio.Execute(args); + + FfmpegBuilderAudioAddTrack ffAddAudio2 = new(); + ffAddAudio2.Codec = "aac"; + ffAddAudio2.Index = 2; + ffAddAudio2.Execute(args); + + FfmpegBuilderExecutor ffExecutor = new(); + int result = ffExecutor.Execute(args); + + string log = logger.ToString(); + Assert.AreEqual(1, result); + } + + [TestMethod] + public void FfmpegBuilder_AddAc3AacMp4NoSubs_BlackBars_Scaled480p() + { + const string file = @"D:\videos\unprocessed\blackbars.mkv"; + var logger = new TestLogger(); + const string ffmpeg = @"C:\utils\ffmpeg\ffmpeg.exe"; + var vi = new VideoInfoHelper(ffmpeg, logger); + var vii = vi.Read(file); + var args = new NodeParameters(file, logger, false, string.Empty); + args.GetToolPathActual = (string tool) => ffmpeg; + args.TempPath = @"D:\videos\temp"; + args.Parameters.Add("VideoInfo", vii); + + + FfmpegBuilderStart ffStart = new(); + Assert.AreEqual(1, ffStart.Execute(args)); + + FfmpegBuilderVideoEncode ffEncode = new(); + ffEncode.VideoCodec = "h265"; + ffEncode.Execute(args); + + FfmpegBuilderRemuxToMP4 ffMp4 = new(); + ffMp4.Execute(args); + + FfmpegBuilderCropBlackBars ffCropBlackBars = new(); + ffCropBlackBars.CroppingThreshold = 10; + ffCropBlackBars.Execute(args); + + FfmpegBuilderScaler ffScaler = new(); + ffScaler.Resolution = "640:-2"; + ffScaler.Execute(args); + + FfmpegBuilderSubtitleFormatRemover ffSubRemover = new(); + ffSubRemover.RemoveAll = true; + ffSubRemover.Execute(args); + + + FfmpegBuilderAudioAddTrack ffAddAudio = new(); + ffAddAudio.Codec = "ac3"; + ffAddAudio.Index = 1; + ffAddAudio.Execute(args); + + FfmpegBuilderAudioAddTrack ffAddAudio2 = new(); + ffAddAudio2.Codec = "aac"; + ffAddAudio2.Index = 2; + ffAddAudio2.Execute(args); + + FfmpegBuilderExecutor ffExecutor = new(); + int result = ffExecutor.Execute(args); + + string log = logger.ToString(); + Assert.AreEqual(1, result); + } + + + [TestMethod] + public void FfmpegBuilder_AddAc3AacMp4NoSubs_BlackBars_Scaled4k() + { + const string file = @"D:\videos\unprocessed\blackbars.mkv"; + var logger = new TestLogger(); + const string ffmpeg = @"C:\utils\ffmpeg\ffmpeg.exe"; + var vi = new VideoInfoHelper(ffmpeg, logger); + var vii = vi.Read(file); + var args = new NodeParameters(file, logger, false, string.Empty); + args.GetToolPathActual = (string tool) => ffmpeg; + args.TempPath = @"D:\videos\temp"; + args.Parameters.Add("VideoInfo", vii); + + + FfmpegBuilderStart ffStart = new(); + Assert.AreEqual(1, ffStart.Execute(args)); + + FfmpegBuilderVideoEncode ffEncode = new(); + ffEncode.VideoCodec = "h265"; + ffEncode.Execute(args); + + FfmpegBuilderRemuxToMP4 ffMp4 = new(); + ffMp4.Execute(args); + + FfmpegBuilderCropBlackBars ffCropBlackBars = new(); + ffCropBlackBars.CroppingThreshold = 10; + ffCropBlackBars.Execute(args); + + FfmpegBuilderScaler ffScaler = new(); + ffScaler.Resolution = "3840:-2"; + ffScaler.Execute(args); + + FfmpegBuilderSubtitleFormatRemover ffSubRemover = new(); + ffSubRemover.RemoveAll = true; + ffSubRemover.Execute(args); + + + FfmpegBuilderAudioAddTrack ffAddAudio = new(); + ffAddAudio.Codec = "ac3"; + ffAddAudio.Index = 1; + ffAddAudio.Execute(args); + + FfmpegBuilderAudioAddTrack ffAddAudio2 = new(); + ffAddAudio2.Codec = "aac"; + ffAddAudio2.Index = 2; + ffAddAudio2.Execute(args); + + FfmpegBuilderExecutor ffExecutor = new(); + int result = ffExecutor.Execute(args); + + string log = logger.ToString(); + Assert.AreEqual(1, result); + } + + [TestMethod] + public void FfmpegBuilder_AddAc3AacMp4NoSubs_BlackBars_Scaled480p2() + { + const string file = @"D:\videos\unprocessed\basic.mkv"; + var logger = new TestLogger(); + const string ffmpeg = @"C:\utils\ffmpeg\ffmpeg.exe"; + var vi = new VideoInfoHelper(ffmpeg, logger); + var vii = vi.Read(file); + var args = new NodeParameters(file, logger, false, string.Empty); + args.GetToolPathActual = (string tool) => ffmpeg; + args.TempPath = @"D:\videos\temp"; + args.Parameters.Add("VideoInfo", vii); + + + FfmpegBuilderStart ffStart = new(); + Assert.AreEqual(1, ffStart.Execute(args)); + + FfmpegBuilderVideoEncode ffEncode = new(); + ffEncode.VideoCodec = "h265"; + ffEncode.Execute(args); + + FfmpegBuilderRemuxToMP4 ffMp4 = new(); + ffMp4.Execute(args); + + FfmpegBuilderCropBlackBars ffCropBlackBars = new(); + ffCropBlackBars.CroppingThreshold = 10; + ffCropBlackBars.Execute(args); + + FfmpegBuilderScaler ffScaler = new(); + ffScaler.Resolution = "640:-2"; + ffScaler.Execute(args); + + FfmpegBuilderSubtitleFormatRemover ffSubRemover = new(); + ffSubRemover.RemoveAll = true; + ffSubRemover.Execute(args); + + + FfmpegBuilderAudioAddTrack ffAddAudio = new(); + ffAddAudio.Codec = "ac3"; + ffAddAudio.Index = 1; + ffAddAudio.Execute(args); + + FfmpegBuilderAudioAddTrack ffAddAudio2 = new(); + ffAddAudio2.Codec = "aac"; + ffAddAudio2.Index = 2; + ffAddAudio2.Execute(args); + + FfmpegBuilderExecutor ffExecutor = new(); + int result = ffExecutor.Execute(args); + + string log = logger.ToString(); + Assert.AreEqual(1, result); + } + } +} + +#endif \ No newline at end of file diff --git a/VideoNodes/VideoNodes.en.json b/VideoNodes/VideoNodes.en.json index 8e21dfa5..05dfcdc5 100644 --- a/VideoNodes/VideoNodes.en.json +++ b/VideoNodes/VideoNodes.en.json @@ -5,7 +5,7 @@ "Outputs": { "1": "Audio track added and saved to temporary file" }, - "Description": "Adds a new audio track to ta video file, all other audio tracks will remain. This will use the first audio track of the file as the source audio track to convert.", + "Description": "Adds a new audio track to the video file, all other audio tracks will remain. This will use the first audio track of the file as the source audio track to convert.", "Fields": { "Index": "Index", "Index-Help": "The index where to insert the new audio track. 1 based, so to insert the new audio track as the first track set this to 1.", @@ -75,7 +75,7 @@ } }, "AudioTrackSetLanguage": { - "Label": "Audio: Set Language", + "Label": "Audio Set Language", "Outputs": { "1": "Audio tracks updated to new temporary file", "2": "Audio tracks NOT updated" @@ -144,6 +144,140 @@ "CommandLine-Help": "The command line to run with FFMPEG.\n'{WorkingFile}': the working file of the flow\n'{Output}': The output file that will be passed as the last parameter to FFMPEG including the extension defined above." } }, + "FfmpegBuilderStart": { + "Label": "FFMPEG Builder: Start", + "Outputs": { + "1": "FFMPEG Builder created and ready to add FFMPEG Builder nodes to" + }, + "Description": "Creates an instance of the FFMPEG Builder which can build a FFMPEG argument to then execute with the FFMPEG Executor." + }, + "FfmpegBuilderExecutor": { + "Label": "FFMPEG Builder: Executor", + "Outputs": { + "1": "FFMPEG Builder ran successfully and created new temporary file", + "2": "No changes detected in FFMPEG Builder, file not created" + }, + "Description": "Executes a FFMPEG Builder command created by other FFMPEG Builder nodes." + }, + "FfmpegBuilderAudioAddTrack": { + "Label": "FFMPEG Builder: Audio Add Track", + "Outputs": { + "1": "Added audio track to FFMPEG Builder" + }, + "Description": "Adds a new audio track to FFMPEG Builder which will be added once the builder is executed.", + "Fields": { + "Index": "Index", + "Index-Help": "The index where to insert the new audio track. 1 based, so to insert the new audio track as the first track set this to 1.", + "Channels": "Channels", + "Channels-Help": "The number of channels to convert this audio track to.", + "Bitrate": "Bitrate", + "Bitrate-Help": "Bitrate of the new audio track", + "Language": "Language", + "Language-Help": "Optional ISO 639-2 language code to use. Will attempt to find an audio track with this language code if not the best audio track will be used.\nhttps://en.wikipedia.org/wiki/List_of_ISO_639-2_codes" + } + }, + "FfmpegBuilderAudioTrackRemover": { + "Label": "FFMPEG Builder: Audio Track Remover", + "Outputs": { + "1": "Audio tracks set to remove", + "2": "Audio tracks NOT set to removed" + }, + "Description": "Allows you to remove audio tracks based on either their title or their language codes.\n\nAny title (or language code if set to \"Use Language Code\") that is blank will NOT be removed regardless of the pattern.", + "Fields": { + "RemoveAll": "Remove All", + "RemoveAll-Help": "Remove all current tracks from the output file. You can use Add Audio Track afterwards to add tracks of specific codecs", + "Pattern": "Pattern", + "Pattern-Help": "A regular expression to match against, eg \"commentary\" to remove commentary tracks", + "NotMatching": "Not Matching", + "NotMatching-Help": "If audio tracks NOT matching the pattern should be removed", + "UseLanguageCode": "Use Language Code", + "UseLanguageCode-Help": "If the language code of the audio track should be used instead of the title" + } + }, + "FfmpegBuilderSubtitleFormatRemover": { + "Label": "FFMPEG Builder: Subtitle Format Remover", + "Description": "Removes subtitles from a video file if found.", + "Outputs": { + "1": "Subtitles marked for removal in FFMPEG Builder", + "2": "No subtitles to remove" + }, + "Fields": { + "SubtitlesToRemove": "Subtitles To Remove", + "RemoveAll": "Remove All", + "RemoveAll-Help": "When checked, all subtitles will be removed from the file, otherwise only those selected below will be" + } + }, + "FfmpegBuilderSubtitleTrackRemover": { + "Label": "FFMPEG Builder: Subtitle Track Remover", + "Outputs": { + "1": "Subtitles marked for removal in FFMPEG Builder", + "2": "No subtitles to remove" + }, + "Description": "Allows you to remove subtitles based on either their title or their language codes.\n\nAny language (or title if set to \"Use Title\") that is blank will NOT be removed regardless of the pattern.", + "Fields": { + "Pattern": "Pattern", + "Pattern-Help": "A regular expression to match against, eg \"eng\" to remove English tracks", + "NotMatching": "Not Matching", + "NotMatching-Help": "If subtitles NOT matching the pattern should be removed", + "UseLanguageCode": "Use Language Code", + "UseLanguageCode-Help": "If the language code of the audio track should be used instead of the title" + } + }, + "FfmpegBuilderCropBlackBars": { + "Label": "FFMPEG Builder: Crop Black Bars", + "Description": "Updated FFMPEG Builder to crop black bars if detected", + "Outputs": { + "1": "FFMPEG Builder updated to crop black bars", + "2": "No black bars detected, not cropping" + }, + "Fields": { + "CroppingThreshold": "Threshold", + "CroppingThreshold-Help": "The amount of pixels that must be greater than to crop. E.g. if there's only 5 pixels detected as black space, you may consider this too small to crop." + } + }, + "FfmpegBuilderRemuxToMkv": { + "Label": "FFMPEG Builder: Remux to MKV", + "Descritption": "Remuxes a video file into a MKV container.", + "Outputs": { + "1": "FFMPEG Builder set to remux to MKV" + } + }, + "FfmpegBuilderRemuxToMP4": { + "Label": "FFMPEG Builder: Remux to MP4", + "Descritption": "Remuxes a video file into a MP4 container.", + "Outputs": { + "1": "FFMPEG Builder set to remux to MP4" + } + }, + "FfmpegBuilderScaler": { + "Label": "FFMPEG Builder: Video Scaler", + "Description": "This allows you to scale a video to the specified dimensions. It will retain the aspect ratio of the video so if the video was 1920x1000 it would scale to 1280x668 if you select 720P.", + "Outputs": { + "1": "FFMPEG Builder scale filter added", + "2": "Video was already in/near the scaled resolution" + }, + "Fields": { + "Force": "Force", + "Force-Help": "When checked the video will be force scaled even if the working file is already in this resolution (or near this resolution).", + "Resolution": "Resolution" + } + }, + "FfmpegBuilderVideoEncode": { + "Label": "FFMPEG Builder: Video Encode", + "Description": "Encodes video streams in the specified codec", + "Outputs": { + "1": "FFMPEG Builder video streams set to encode", + "2": "Video already in target codec, will not re-encode" + }, + "Fields": { + "VideoCodec": "Video Codec", + "VideoCodec-Help": "The video codec the video should be in, for example hevc, h264.\nIf left empty all original video tracks will be copied.", + "VideoCodecParameters": "Video Codec Parameters", + "VideoCodecParameters-Help": "The parameters to use to encode the video, eg. \"hevc_nvenc -preset hq -crf 23\" to encode into hevc using the HQ preset a constant rate factor of 23 and using NVIDIA hardware acceleration.", + "Force": "Force Encode", + "Force-Help": "Will force a encode of the video even if it is already in the target Video Codec" + } + }, "RemuxToMKV": { "Descritption": "Remuxes a video file into a MKV container. All streams will be copied to the new container", "Outputs": { @@ -214,7 +348,6 @@ "UseTitle": "Use Title", "UseTitle-Help": "If the title of the subtitle should be used for matching instead of the language" } - }, "VideoCodec": { "Description": "This node will check the codecs in the input file, and trigger when matched.\n\nOutput 1: Matches\nOutput 2: Does not match", diff --git a/build/utils/PluginInfoGenerator/FileFlows.Plugin.dll b/build/utils/PluginInfoGenerator/FileFlows.Plugin.dll index c59b1137aff33ee3e6b1af8e529288a00052e17e..07202f7dc571acffa224a3253d5b68826340d2af 100644 GIT binary patch delta 18062 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 CV0?lA delta 17605 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 diff --git a/build/utils/PluginInfoGenerator/FileFlows.Plugin.pdb b/build/utils/PluginInfoGenerator/FileFlows.Plugin.pdb index 4857400e46648d687a1a0911b66c9a67019cee9d..4f87f7237d11ada5fe4b78af183680737b6d22da 100644 GIT binary patch delta 3574 zcmZ9O32;+Y6o$`B(=_S6rII$Kbb%>lfYJ>@pry1l>`S2vLoEfAQlykZLCY2>$|k7z z*knvny8h6czEr!$|)7o39n05A|u|DONP_uh^KPwD(P2<_re(Xx!~TD ztH*(--&_?}W7^+)@x+am3GZEB*m!2-8l>rogu_9W)Fr5WYzFr)={7QVu~8EA5mcFBqnXf$&;=+j(?-LfiO^JNLuM>BKnI~?&?)F+ z=n`}#6O;e%WvA~m?Q{qF4Y~_Gg#5GY6bwaW+1(V2ATf(a*(nuqXDA2i2^B&Epking zR1Q@@Q=l2%F|wPT<{-aao2vowX)XQ}aqDb5xw7M_6uOYjs&$57VIf#p2+enf(n@C-ZE}WVff2OR$&2TgjSeC1*$mBzCa-Cu^d670 zk0Lv5?O~@qP@~tk(NfQJ|C4_1N^H0SGUJbT9??*!P;eT!kKmgqV-7}r72+cJMc`0v z{2_17Op)LM&3D0%*8B+6_kF}}S|AdUh)XoKVn8h7K~O&oh|wkp1NRr)0Zo`&!LNrm z7a9PzY4wkR2MXp5Fv!Pl!pevS`w~P?f`@?PvfK&fD^=S9i|l?i5lj9?yLE|~qX8gqYd13sJd3Atdr zV4mP9!R${E%zlMn_9qHvzfv&!lbYGv-@AZkS`>j*$<2~z5JmNaRIMivDVE$Ij7Mus3Bbb}dZNc*d=fihX5a9@IZg@*i zfYTB4PUeDma5u!Wp!wj|T74;oF(-gG!8@+`iQs#P3p7pw$DuY6%yTAd<4uScLT)|5 zm+1Kx1uqC@pPxs1Jx{P$F#Ag=%;)phY$-TJYZwGxCU`#<1$I+g6jZ>BY)()+FyCu~ z#;M>s#C+<%hzZ(j6MkZ7=8oW*@RK0+JAvmS4%0XtybiT|RhPr>qKzMo+;Vb`c zC=PEro|sl5O71n70moEc7P)ZrT+~0dE$Z%6t4Rk?4qmErK(^TLpIoZxhUqvh9NN!8-&Of!`K95WG`x zG5XUkk*Gz1&b-6<6x!~R0u30i`+Upx3JwInBkFnlKEYL}x_7L45Tj^9jxD#4T8WnD@k4a1ZU4aJ^)%XU-M%=W+Dr zY4wp9f5FFi&T`_SFF`aJH(uwF@HY83b1zK5$1Fd1*v|(S_%$C)pP^xIt-&4mURLKu zb}=dxsHm4B+j%ZWmMLv-WSVGAmWj?~E3`N#7?mMVC=@`Q@KclDi`A0oL7}ePZ*2io zp4%qYL??4S2cmc}lgYqvc7F>hn--lM11Z1XFt z3%i|j^L!CILuqqk-~I9*5^iNjgjv$A4*9@(AYtUtU0c2y)7Z!V%ga~y zUGz+{7p4`};8S8Tmifm+%veOULY<6N0hDjVFTFgYIgXr0a{>)A@Z~a4shQF!(OeVC zUJ3;m@Y!=sbusF%MX|O7drdIe4VWd0|0#yLI9$@S`M<02t~lj^>Tty?M)e<8E4A9S zLK%l>W9W&Ha4sBz?G(M>w7R435X<$tsn|U27Ti|uIY`NJ+yrwbZv)H}^|ZveZxe-B%WR&h}4Nl-=rI1Fk5$J(mU^ iGAjA%qG1!14(izAST&${l(NJ#wz$lo+)&>i(fvQ*ev)ng delta 3621 zcmZ9Pd2mxz6voecP1A(#X$nmmprI|r07L0Y8(OGT+Oh}~s0&a)7?7nv0jmMwwFN~K zk@`@PMRsLTwy^IgE)0v~G6TvuEHf}D)Bl)J1RcMVzE_UT%-5XX`Odlb=DnNy($%|^ z$2*l(Ioi<#qSVDi2c1Mw3gVIT=T4h7bsph$Hj&7J_teoN={({&9G8u*D8+kDd!p}( z!B5Ye{StUV?@_1IYnw{9jTki}rT^#&W$N`iNYitj4@cTN5n*V05$XjkfqsOp6SX51 z!6nGobZU}P67(uARlA+(qJN>J-Yz-~jn8t?Oz0$Z9?H#jQ5jSXO@W%S-Lw_j1?`89 zLT8|Fpv&19{C_VGUCZ{+4d^b^3_XAzL28bN>^UBd;t+Jm;Vut#LYxlufU==Ns01p9 zhCx-(IA}67Bh({j^Uz%6*BfJPMSRSN?<02RdZ;M3BUM7@bGa{(ym?w8b<0bnUQixX z1eHRAq4m&Ks31R);`=1hPtZ;1aUX70D5*fD?obv~P!L9g3M^Cwje{mbGoZOp0IDwt zr*+U4XeV?PYKFqJ!f$g*-za3TkBk>+Z)BVv{STq(EO?CblTIP=>-5-Yq#?wYWFP>j6+KsrV1zHjZLZ*wl z_I;9l5_xECUk`18_J({HX~B8n=fbq%*l+{LMiHT%M>HHgrGkCnX9V9sJ#!@LYY-2F zKM)*k^dAc4Y!nY3VEAS56AV8d_2o}kqXv<1ARcY76Af;}L!k;ZBpL(6f(Hrij!Bq1 zz;A@N2pSAdGU_{khX^hJ4{c?Q$ce> zj}pv2f5^~ym>*LSRgt}Qf;jLPvjFehFjg?v^CIPfss*z@UNHAh5X}BWgL(eY1bjA` z19HJ6!92iZ!R${F%>Got?7u9S{b_>PpWecu`9lks(WYRgU>=}GFb^J8#d4Oet*?)^-TPNVJ*>Z5IF<~V5ZNdAoD6mFpD3}V<(UPEa zFyCv{;0$m8F`xSHV1RDMfEU#k?g6fY-vwg7CwLL!Sc7|k*PxcK>I(RoM*kS(R*)7; zSx9t5aU{eA+2B;fkBo*~a5rOscj4z51Ms=AQZOG@@3rA3ifgU6!+#$#`}vCLy#LJv z@51BnamBJ2_t2g+5|t}xMFGG&4Rn}9^WDoJyEb#a29x*;C%3Q!Tc!uRB#FSGrqMrNr3AXY5-!Bqe za6m9m5XA9hPQVAz!8Q{d63lz#uwY)mkv4o3IdeRXCp;z+JmGQC5C=Yir?=U_6Py%2 z^Qks`S}-r*93F9IKhO6SWUz+KLG-mKIEb&FWImyp3vmk?5c8h+7VI}}3D-;JdgdZg ze;G$_u~F|p|CLt8bCwg|wMf&JiHCzE!+F`vHU^Yw0#enm*kC8 zF9q+oUr|y<{<8UW&-^d1cDXowc-^66<$K0oSozP`JnymiI>)Wthh2jUJf%KuDn1w1 zV6Q(F%|HK~3YA!DqNtz6mP~$&tuqy=IL+{X$5f+HgS>@>uw{q*`0Qjkk)E>FZbS3b;s zEieZgt!u+=-BmFs%Zd*aMNXB<)tdHr@YoyUktrf)XUbD+;wc6z!wqmXCa3u@r@b*< zKT+XRGWEL^e=C9DtwEWJvRQWwxvFdq{xRf`MakDUjhLcj=nE^|y0`KLWod9>WwokY L*MA*Vh$q8;sRD@o diff --git a/build/utils/PluginInfoGenerator/FileFlows.ServerShared.dll b/build/utils/PluginInfoGenerator/FileFlows.ServerShared.dll index 9f5180ac9b665281f31062a18aef57a5148ea86a..dfc865576cdee016f607d62b0267120329fb0905 100644 GIT binary patch delta 107 zcmZoT!qjktX+j6fv&ah%H}>pl5;(hjqm}oj6OMb6A~#)Cd-Z#BMDrIh7LLOuO_Kv= z1qk>TuKW{rtIJ`*3&pONo0B>xZ01TL1t6 delta 107 zcmZoT!qjktX+j5!P7L4VjXiss1UmM-6#bF?$Gb0F!{(-G{_f2Y&0oY=zMB>|Ob(b8 zAkcAej&xU|rNgEd8yh_Q8G0sfn3bpi6=X1i3IbI#+-W+PwY+ZgVbfbXCT%vHeVz>f DTxBv8 diff --git a/build/utils/PluginInfoGenerator/FileFlows.ServerShared.pdb b/build/utils/PluginInfoGenerator/FileFlows.ServerShared.pdb index 67138b71e780cd9936aed3fb22fb43a6516ac7a6..020191626059218750180443591fdf012b16b330 100644 GIT binary patch delta 110 zcmV-!0FnQ|paH<30g!wYPjRXIR@#a{py(lr>9b;q2*Y%Ov5=h{N7WMY!vMel07(5Y zIGSR}L4=as_1_vveKIGMOkYod5s; diff --git a/build/utils/PluginInfoGenerator/FileFlows.Shared.dll b/build/utils/PluginInfoGenerator/FileFlows.Shared.dll index 6c451020fd075029d31457065c761fe0dbf4afe9..5fe58097199e1af3d7c92d0233730b90bbb7d63f 100644 GIT binary patch literal 65536 zcmeFacX(A*{x$wN=iJs3>?f0ntflZQN-EDy%NSn#kl{l$XAPM+74e|lr| zq{fB?`EwdhJ1x2}|MePtp8NBATeF+tOu%77io(cGM9E;b=&Q3f)r zD0J59HerAj83?&)5TH0KIvATIJ3f+!ZJFPW48gX-cFOE%zFNhbGm$7#0NWoOs!F;Y zEyUJYr~&>+0tRQ9fAefFmYYGIz4;HjD~V17qdjzq%4POujMRb5)s26!vq-Z`3|35O zDn_()Uocvt+qm)pX>rkEYWmZBY5r&gCX`ZaDnHngnHIMQZMD(^X@Ls-Md6IlJ88Z$ z$BvZ41hp{kZFxzCd?_eGE zv;x_&ZXt(NODs-0%fM|#geD`WtI>P6njavW)Cq` zY1?HPWvBRTr?}EzLOnVP`XRo9qkCXm_IDpg#%66_OeZ8c(a{KYqI+s2$LGe1i|nOT z(nt7ZE9!urOLHP)pd|!y_DBk3RUH+`smk5!ZiEE}SA_yY5^6hOXOuaSv6_-jODr#T z6XjOr25qOm=(y~bz(T-y8@rTMMyJ^$m(d;U?bYPXQyxeaJ zNOIGZ_~U;g-|@(%-0$XQ@_xW&jc~>3ZoZdbU|W%W5w)pSi_LAS!$$YY3A)W)f;r3w z+Ge)y&Yim?WMpo0mtfXmEN;)YBNLIh!cYH~*%9n$e(f1MBOJjF_89{BD&<=$Y%3J+`>k@;y5BZbgqf-zT zoeD_zmDdgMMKGOV57OG|P}?7@B5YVZglQW606=-hFV*Q#k->r3RIcBWJixarj4}th zLwSo+vPA+}vl0USOXuzT$De=xS?3#naiv{S*0<9fhsrWnw^lQdlTJN2%7h{Z18$BU z0z@M%OhmR+$&41kc+_96G+rW3DT2~R#EXtA9RAXp4n_RFX=^4(ar7`?Q#!(
#+ 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 delta 12145 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 + + + Gets or sets the number of seconds that have to pass between changes to the folder for it to be scanned into the library + + Gets or sets if hidden files and folders should be excluded from the library diff --git a/build/utils/PluginInfoGenerator/PluginInfoGenerator.dll b/build/utils/PluginInfoGenerator/PluginInfoGenerator.dll index 6a545092e40b25645f80e620271c6f69ce5b91b6..87b73d5bd54ec0a1787819949a1c1d5dd2774e05 100644 GIT binary patch delta 105 zcmZpuXsDRb!J?L(aA9LlhPD8w$dLsahSOcAG@e>oDzas_1_v+N6$C|OP_ A&Hw-a diff --git a/build/utils/spellcheck/ignoredwords.txt b/build/utils/spellcheck/ignoredwords.txt index b9c6b882..d4f6ec69 100644 --- a/build/utils/spellcheck/ignoredwords.txt +++ b/build/utils/spellcheck/ignoredwords.txt @@ -63,4 +63,6 @@ normalization doesn''t * % -eng \ No newline at end of file +eng +remux +Scaler \ No newline at end of file diff --git a/ref/FileFlows.Plugin.dll b/ref/FileFlows.Plugin.dll index 68e74035fdb61ebefc35fc9bfa6eace57c52d7a3..99314939ca62edf6906ef50e95be4d57ff815972 100644 GIT binary patch delta 6205 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 delta 6151 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