Files
create-vod/bin/cli.js

87 lines
55 KiB
JavaScript
Raw Normal View History

#!/usr/bin/env node
import{createRequire as jF}from"node:module";var MF=Object.create;var{getPrototypeOf:LF,defineProperty:uu,getOwnPropertyNames:OF}=Object;var RF=Object.prototype.hasOwnProperty;var $D=(u,D,E)=>{E=u!=null?MF(LF(u)):{};let F=D||!u||!u.__esModule?uu(E,"default",{value:u,enumerable:!0}):E;for(let C of OF(u))if(!RF.call(F,C))uu(F,C,{get:()=>u[C],enumerable:!0});return F};var I=(u,D)=>()=>(D||u((D={exports:{}}).exports,D),D.exports);var n=jF(import.meta.url);var qu=I((j3,Hu)=>{class zu{constructor(u,D,E){this.etaBufferLength=u||100,this.valueBuffer=[E],this.timeBuffer=[D],this.eta="0"}update(u,D,E){this.valueBuffer.push(D),this.timeBuffer.push(u),this.calculate(E-D)}getTime(){return this.eta}calculate(u){let D=this.valueBuffer.length,E=Math.min(this.etaBufferLength,D),F=this.valueBuffer[D-1]-this.valueBuffer[D-E],C=this.timeBuffer[D-1]-this.timeBuffer[D-E],B=F/C;this.valueBuffer=this.valueBuffer.slice(-this.etaBufferLength),this.timeBuffer=this.timeBuffer.slice(-this.etaBufferLength);let A=Math.ceil(u/B/1000);if(isNaN(A))this.eta="NULL";else if(!isFinite(A))this.eta="INF";else if(A>1e7)this.eta="INF";else if(A<0)this.eta=0;else this.eta=A}}Hu.exports=zu});var wD=I((w3,Iu)=>{var p=n("readline");class xu{constructor(u){this.stream=u,this.linewrap=!0,this.dy=0}cursorSave(){if(!this.stream.isTTY)return;this.stream.write("\x1B7")}cursorRestore(){if(!this.stream.isTTY)return;this.stream.write("\x1B8")}cursor(u){if(!this.stream.isTTY)return;if(u)this.stream.write("\x1B[?25h");else this.stream.write("\x1B[?25l")}cursorTo(u=null,D=null){if(!this.stream.isTTY)return;p.cursorTo(this.stream,u,D)}cursorRelative(u=null,D=null){if(!this.stream.isTTY)return;this.dy=this.dy+D,p.moveCursor(this.stream,u,D)}cursorRelativeReset(){if(!this.stream.isTTY)return;p.moveCursor(this.stream,0,-this.dy),p.cursorTo(this.stream,0,null),this.dy=0}clearRight(){if(!this.stream.isTTY)return;p.clearLine(this.stream,1)}clearLine(){if(!this.stream.isTTY)return;p.clearLine(this.stream,0)}clearBottom(){if(!this.stream.isTTY)return;p.clearScreenDown(this.stream)}newline(){this.stream.write(`
`),this.dy++}write(u,D=!1){if(this.linewrap===!0&&D===!1)this.stream.write(u.substr(0,this.getWidth()));else this.stream.write(u)}lineWrapping(u){if(!this.stream.isTTY)return;if(this.linewrap=u,u)this.stream.write("\x1B[?7h");else this.stream.write("\x1B[?7l")}isTTY(){return this.stream.isTTY===!0}getWidth(){return this.stream.columns||(this.stream.isTTY?80:200)}}Iu.exports=xu});var Mu=I((S3,Vu)=>{Vu.exports=({onlyFirst:u=!1}={})=>{let D=["[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)","(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))"].join("|");return new RegExp(D,u?void 0:"g")}});var Ou=I((T3,Lu)=>{var eF=Mu();Lu.exports=(u)=>typeof u==="string"?u.replace(eF(),""):u});var ju=I((b3,SD)=>{var Ru=(u)=>{if(Number.isNaN(u))return!1;if(u>=4352&&(u<=4447||u===9001||u===9002||11904<=u&&u<=12871&&u!==12351||12880<=u&&u<=19903||19968<=u&&u<=42182||43360<=u&&u<=43388||44032<=u&&u<=55203||63744<=u&&u<=64255||65040<=u&&u<=65049||65072<=u&&u<=65131||65281<=u&&u<=65376||65504<=u&&u<=65510||110592<=u&&u<=110593||127488<=u&&u<=127569||131072<=u&&u<=262141))return!0;return!1};SD.exports=Ru;SD.exports.default=Ru});var Su=I((y3,wu)=>{wu.exports=function(){return/\uD83C\uDFF4\uDB40\uDC67\uDB40\uDC62(?:\uDB40\uDC65\uDB40\uDC6E\uDB40\uDC67|\uDB40\uDC73\uDB40\uDC63\uDB40\uDC74|\uDB40\uDC77\uDB40\uDC6C\uDB40\uDC73)\uDB40\uDC7F|\uD83D\uDC68(?:\uD83C\uDFFC\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68\uD83C\uDFFB|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFF\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFE])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFE\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFD])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFD\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB\uDFFC])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\u200D(?:\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D)?\uD83D\uDC68|(?:\uD83D[\uDC68\uDC69])\u200D(?:\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67]))|\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67])|(?:\uD83D[\uDC68\uDC69])\u200D(?:\uD83D[\uDC66\uDC67])|[\u2695\u2696\u2708]\uFE0F|\uD83D[\uDC66\uDC67]|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|(?:\uD83C\uDFFB\u200D[\u2695\u2696\u2708]|\uD83C\uDFFF\u200D[\u2695\u2696\u2708]|\uD83C\uDFFE\u200D[\u2695\u2696\u2708]|\uD83C\uDFFD\u200D[\u2695\u2696\u2708]|\uD83C\uDFFC\u200D[\u2695\u2696\u2708])\uFE0F|\uD83C\uDFFB\u200D(?:\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C[\uDFFB-\uDFFF])|(?:\uD83E\uDDD1\uD83C\uDFFB\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFC\u200D\uD83E\uDD1D\u200D\uD83D\uDC69)\uD83C\uDFFB|\uD83E\uDDD1(?:\uD83C\uDFFF\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1(?:\uD83C[\uDFFB-\uDFFF])|\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1)|(?:\uD83E\uDDD1\uD83C\uDFFE\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFF\u200D\uD83E\uDD1D\u200D(?:\uD83D[\uDC68\uDC69]))(?:\uD83C[\uDFFB-\uDFFE])|(?:\uD83E\uDDD1\uD83C\uDFFC\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFD\u200D\uD83E\uDD1D\u200D\uD83D\uDC69)(?:\uD83C[\uDFFB\uDFFC])|\uD83D\uDC69(?:\uD83C\uDFFE\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFD\uDFFF])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFC\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB\uDFFD-\uDFFF])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD
`).map((C)=>C.trim()).filter(Boolean),E=[],F={cuda:"nvenc",qsv:"qsv",vaapi:"vaapi",videotoolbox:"videotoolbox",v4l2m2m:"v4l2",dxva2:"amf"};for(let C of D){let B=F[C];if(B)E.push({accelerator:B})}return E}async function l(u,D,E){let C=`
=== FFmpeg Command [${new Date().toISOString()}] ===
ffmpeg ${u.join(" ")}
`;return await f(C),new Promise((B,A)=>{let Z=s("ffmpeg",u),J="";Z.stderr.on("data",(G)=>{let X=G.toString();if(J+=X,D&&E){let Y=X.match(/time=(\d{2}):(\d{2}):(\d{2}\.\d{2})/);if(Y){let $=parseInt(Y[1]),N=parseInt(Y[2]),K=parseFloat(Y[3]),_=$*3600+N*60+K,W=Math.min(100,_/E*100);D(W)}}}),Z.on("error",(G)=>{f(`ERROR: ${G.message}
`),A(Error(`FFmpeg error: ${G.message}`))}),Z.on("close",(G)=>{if(G===0){let Y=J.split(`
`).filter(($)=>$.trim()).slice(-10).join(`
`);f(`SUCCESS: Exit code ${G}
--- Last 10 lines of output ---
${Y}
`),B()}else f(`FAILED: Exit code ${G}
--- Full error output ---
${J}
`),A(Error(`FFmpeg failed with exit code ${G}
${J}`))})})}async function ID(u){let E=`
=== MP4Box Command [${new Date().toISOString()}] ===
MP4Box ${u.join(" ")}
`;return await f(E),new Promise((F,C)=>{let B=s("MP4Box",u),A="",Z="";B.stdout.on("data",(J)=>{A+=J.toString()}),B.stderr.on("data",(J)=>{Z+=J.toString()}),B.on("error",(J)=>{f(`ERROR: ${J.message}
`),C(Error(`MP4Box error: ${J.message}`))}),B.on("close",(J)=>{if(J===0){let Y=(A||Z).split(`
`).filter(($)=>$.trim()).slice(-10).join(`
`);f(`SUCCESS: Exit code ${J}
--- Last 10 lines of output ---
${Y}
`),F()}else{let G=Z||A;f(`FAILED: Exit code ${J}
--- Full error output ---
${G}
`),C(Error(`MP4Box failed with exit code ${J}
${G}`))}})})}import{spawn as SF}from"node:child_process";async function t(u){return new Promise((D,E)=>{let F=SF("ffprobe",["-v","error","-show_entries","stream=width,height,duration,r_frame_rate,codec_name,codec_type,bit_rate","-show_entries","format=duration","-of","json",u]),C="";F.stdout.on("data",(B)=>{C+=B.toString()}),F.on("error",(B)=>{E(Error(`ffprobe error: ${B.message}`))}),F.on("close",(B)=>{if(B!==0){E(Error(`ffprobe failed with exit code ${B}`));return}try{let A=JSON.parse(C),Z=A.streams.find((_)=>_.codec_type==="video"),J=A.streams.find((_)=>_.codec_type==="audio"),G=A.format;if(!Z){E(Error("No video stream found in input file"));return}let X=30;if(Z.r_frame_rate){let[_,W]=Z.r_frame_rate.split("/").map(Number);if(_&&W&&W!==0)X=_/W}let Y=parseFloat(Z.duration||G.duration||"0"),$=A.streams.find((_)=>_.codec_type==="audio"&&_.bit_rate),N=$?.bit_rate?Math.round(parseInt($.bit_rate)/1000):void 0,K=Z.bit_rate?Math.round(parseInt(Z.bit_rate)/1000):void 0;D({width:Z.width,height:Z.height,duration:Y,fps:X,codec:Z.codec_name,hasAudio:Boolean(J),audioBitrate:N,videoBitrate:K})}catch(A){E(Error(`Failed to parse ffprobe output: ${A}`))}})})}function JD(u,D=256){if(!u)return`${D}k`;let E=Math.min(u,D);if(E<=64)return"64k";if(E<=96)return"96k";if(E<=128)return"128k";if(E<=192)return"192k";return"256k"}function KD(u){let D=Math.floor(u/3600),E=Math.floor(u%3600/60),F=u%60;return`${String(D).padStart(2,"0")}:${String(E).padStart(2,"0")}:${F.toFixed(3).padStart(6,"0")}`}import{mkdir as TF,access as bF,constants as yF}from"node:fs/promises";async function d(u){try{await bF(u,yF.F_OK)}catch{await TF(u,{recursive:!0})}}function hF(u,D){let E=u*D;if(E<=230400)return 0.08;if(E<=409920)return 0.075;if(E<=921600)return 0.07;if(E<=2073600)return 0.065;if(E<=3686400)return 0.06;return 0.055}function v(u,D,E=30,F){let C=hF(u,D),B=Math.round(u*D*E*C/1000);if(F&&B>F)B=F;return`${B}k`}var VD=[{name:"360p",width:640,height:360,videoBitrate:v(640,360,30),audioBitrate:"192k"},{name:"480p",width:854,height:480,videoBitrate:v(854,480,30),audioBitrate:"192k"},{name:"720p",width:1280,height:720,videoBitrate:v(1280,720,30),audioBitrate:"192k"},{name:"1080p",width:1920,height:1080,videoBitrate:v(1920,1080,30),audioBitrate:"256k"},{name:"1440p",width:2560,height:1440,videoBitrate:v(2560,1440,30),audioBitrate:"256k"},{name:"2160p",width:3840,height:2160,videoBitrate:v(3840,2160,30),audioBitrate:"256k"}];function CD(u,D,E=30,F){let C=[],B=VD.filter((A)=>{return A.width<=u&&A.height<=D});for(let A of B)C.push({...A,videoBitrate:v(A.width,A.height,30,F),fps:30});return C}function fF(u,D,E){return{...u,name:`${u.name}-${D}`,videoBitrate:v(u.width,u.height,D,E),fps:D}}function Fu(u){let E=u.trim().match(/^(\d+)p?(?:[@-](\d+))?$/i);if(!E)return null;let F=E[1]+"p",C=E[2]?parseInt(E[2]):30;return{resolution:F,fps:C}}function Eu(u,D=30,E){let F=VD.find((C)=>C.name===u);if(!F)return null;if(D===30)return{...F,videoBitrate:v(F.width,F.height,30,E),fps:30};return fF(F,D,E)}function gF(u,D,E,F){let C=Fu(u);if(!C)return{error:`Invalid profile format: ${u}. Use format like: 360, 720@60, 1080-60`};let B=Eu(C.resolution,C.fps);if(!B)return{error:`Unknown resolution: ${C.resolution}. Available: 360, 480, 720, 1080, 1440, 2160`};if(B.width>D||B.height>E)return{error:`Source resolution (${D}x${E}) is lower than ${u} (${B.width}x${B.height})`};let A=120,Z=C.fps,J;if(C.fps>F)Z=Math.min(F,A),J=`Requested ${C.fps} FPS in ${u}, but source is ${F} FPS. Using ${Z} FPS instead`;else if(C.fps>A)Z=A,J=`Requested ${C.fps} FPS in ${u} exceeds maximum ${A} FPS. Using ${Z} FPS instead`;return J?{warning:J,adjustedFps:Z}:{}}function XD(u,D,E,F,C){let B=[],A=[],Z=[];for(let J of u){let G=gF(J,D,E,F);if(G.error){A.push(G.error);continue}if(G.warning)Z.push(G.warning);let X=Fu(J);if(!X)continue;let Y=G.adjustedFps!==void 0?G.adjustedFps:X.fps,$=Eu(X.resolution,Y,C);if($)B.push($)}return{profiles:B,errors:A,warnings:Z}}import{join as g}from"node:path";import{readdir as mF,unlink as Cu,rmdir as cF,writeFile as Bu}from"node:fs/promises";async function Au(u,D,E="00:00:00"){let F=g(D,"po
`;for(let Z=0;Z<u;Z++){let J=Z*D,G=(Z+1)*D,X=Math.floor(Z/C),$=Z%C*E,N=X*F;A+=`${KD(J)} --> ${KD(G)}
`,A+=`${B}#xywh=${$},${N},${E},${F}
`}return A}import{join as dF}from"node:path";function pF(u,D,E){if(E)if(D==="h264")return 32;else return 42;else if(D==="h264"){if(u<=360)return 25;if(u<=480)return 24;if(u<=720)return 23;if(u<=1080)return 22;if(u<=1440)return 21;return 20}else{if(u<=360)return 40;if(u<=480)return 38;if(u<=720)return 35;if(u<=1080)return 32;if(u<=1440)return 30;return 28}}async function $u(u,D,E,F,C,B,A,Z,J,G,X,Y,$){let N=dF(D,`video_${J}_${E.name}.mp4`),K=["-y"];if(Y){if(Y==="nvenc")K.push("-hwaccel","cuda","-hwaccel_output_format","cuda");else if(Y==="qsv")K.push("-hwaccel","qsv");else if(Y==="vaapi")K.push("-hwaccel","vaapi");else if(Y==="videotoolbox")K.push("-hwaccel","videotoolbox");else if(Y==="v4l2")K.push("-hwaccel","v4l2")}K.push("-i",u,"-c:v",F);let _=F.includes("nvenc")||F.includes("qsv")||F.includes("amf")||F.includes("vaapi")||F.includes("videotoolbox")||F.includes("v4l2"),W;if(_&&G?.cq!==void 0)W=G.cq;else if(!_&&G?.crf!==void 0)W=G.crf;else W=pF(E.height,J,_);if(F==="h264_nvenc")K.push("-rc:v","vbr"),K.push("-cq",String(W)),K.push("-preset",C),K.push("-2pass","0");else if(F==="av1_nvenc")K.push("-rc:v","vbr"),K.push("-cq",String(W)),K.push("-preset",C),K.push("-2pass","0");else if(F==="av1_qsv")K.push("-preset",C),K.push("-global_quality",String(W));else if(F==="h264_qsv")K.push("-preset",C),K.push("-global_quality",String(W));else if(F==="av1_amf")K.push("-quality","balanced"),K.push("-rc","cqp"),K.push("-qp_i",String(W)),K.push("-qp_p",String(W));else if(F==="h264_amf")K.push("-quality","balanced"),K.push("-rc","cqp"),K.push("-qp_i",String(W)),K.push("-qp_p",String(W));else if(F==="libsvtav1")K.push("-crf",String(W)),K.push("-preset",C),K.push("-svtav1-params","tune=0:enable-overlays=1");else if(F==="libx264")K.push("-crf",String(W)),K.push("-preset",C);else K.push("-preset",C);let U=J==="av1"?0.6:1,k=Math.round(parseInt(E.videoBitrate)*U*1.5);K.push("-maxrate",`${k}k`),K.push("-bufsize",`${k*2}k`);let q=E.fps||30,V=Math.round(q*A);K.push("-g",String(V),"-keyint_min",String(V),"-sc_threshold","0");let L=[`scale=${E.width}:${E.height}`];if(X){if(X.deinterlace)L.push("yadif");if(X.denoise)L.push("hqdn3d");if(X.customFilters)L.push(...X.customFilters)}K.push("-vf",L.join(","));let y=parseInt(E.audioBitrate)||256,P=JD(Z,y);if(K.push("-c:a","aac","-b:a",P),X?.audioNormalize)K.push("-af","loudnorm");return K.push("-f","mp4",N),await l(K,$,B),N}async function Ju(u,D,E,F,C,B,A,Z,J,G,X,Y,$,N,K){let _=new Map;if(J&&E.length>1)for(let W=0;W<E.length;W+=G){let U=E.slice(W,W+G),k=U.map((V)=>$u(u,D,V,F,C,B,A,Z,X,Y,$,N,(L)=>{if(K)K(V.name,L)}));(await Promise.all(k)).forEach((V,L)=>{let y=U[L];_.set(y.name,V)})}else for(let W of E){let U=await $u(u,D,W,F,C,B,A,Z,X,Y,$,N,(k)=>{if(K)K(W.name,k)});_.set(W.name,U)}return _}import{join as O}from"node:path";import{readdir as Uu,rename as G3,mkdir as U3,writeFile as RD}from"node:fs/promises";import{readFile as MD,writeFile as LD}from"node:fs/promises";async function Ku(u){let D=await MD(u,"utf-8");D=D.replace(/\/\/>/g,"/>"),D=D.replace(/\/\s+\/>/g,"/>"),D=D.replace(/(<Representation[^>]+)\s+\/>/g,"$1/>"),D=D.replace(/<Representation\s+([^>]+)\/>\s*<\/Representation>/g,"<Representation $1/>"),D=D.replace(/<Representation\s+([^>]+)\/>\s*(<AudioChannelConfiguration[^>]*\/>)/g,`<Representation $1>
$2
</Representation>`),D=D.replace(/<Representation\s+([^>]+)>\s*(?=<(?:Representation|\/AdaptationSet))/g,`<Representation $1/>
`),await LD(u,D,"utf-8")}async function Xu(u,D,E){let F=await MD(u,"utf-8");F=F.replace(/media="\$RepresentationID\$_\$Number\$\.m4s"/g,'media="$RepresentationID$/$RepresentationID$_$Number$.m4s"'),F=F.replace(/initialization="\$RepresentationID\$_\.mp4"/g,'initialization="$RepresentationID$/$RepresentationID$_.mp4"'),await LD(u,F,"utf-8")}async function Yu(u){let E=(await MD(u,"utf-8")).split(`
`),F=[],C=0;while(C<E.length){let B=E[C];if(B.includes("<AdaptationSet")&&B.includes("maxWidth")){let A=C,Z=[B],J=[],G=[],X=[],Y=!1;C++;while(C<E.length&&!E[C].includes("</AdaptationSet>")){let $=E[C];if($.includes("<SegmentTemplate"))Y=!0;if(Y){if(J.push($),$.includes("</SegmentTemplate>"))Y=!1}else if($.includes("<Representation")&&$.includes("-h264"))G.push($);else if($.includes("<Representation")&&$.includes("-av1"))X.push($);C++}if(G.length>0&&X.length>0)F.push(B),J.forEach(($)=>F.push($)),G.forEach(($)=>F.push($)),F.push(" </AdaptationSet>"),F.push(B),J.forEach(($)=>F.push($)),X.forEach(($)=>F.push($)),F.push(" </AdaptationSet>");else{F.push(B);for(let $=A+1;$<C;$++)F.push(E[$]);F.push(E[C])}C++}else F.push(B),C++}await LD(u,F.join(`
`),"utf-8")}function OD(u,D,E){let F=`#EXTM3U
`;F+=`#EXT-X-VERSION:6
`,F+=`#EXT-X-TARGETDURATION:${Math.ceil(E)}
`,F+=`#EXT-X-MEDIA-SEQUENCE:1
`,F+=`#EXT-X-INDEPENDENT-SEGMENTS
`,F+=`#EXT-X-MAP:URI="${D}"
`;for(let C of u)F+=`#EXTINF:${E},
`,F+=`${C}
`;return F+=`#EXT-X-ENDLIST
`,F}function Gu(u,D){let E=`#EXTM3U
`;if(E+=`#EXT-X-VERSION:6
`,E+=`#EXT-X-INDEPENDENT-SEGMENTS
`,D)E+=`#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio",NAME="audio",AUTOSELECT=YES,URI="audio/playlist.m3u8",CHANNELS="2"
`;for(let F of u){let C=D?"avc1.4D4020,mp4a.40.2":"avc1.4D4020";if(E+=`#EXT-X-STREAM-INF:BANDWIDTH=${F.bandwidth},CODECS="${C}",RESOLUTION=${F.resolution},FRAME-RATE=${F.fps}`,D)E+=',AUDIO="audio"';E+=`
`,E+=`${F.path}
`}return E}async function nF(u,D,E,F,C,B){let A=O(D,"manifest.mpd"),Z=["-dash",String(F*1000),"-frag",String(F*1000),"-rap","-segment-timeline","-segment-name","$RepresentationID$_$Number$","-out",A],J=!0;for(let[G,X]of u.entries())for(let Y of E){let $=X.get(Y.name);if(!$)throw Error(`MP4 file not found for profile: ${Y.name}, codec: ${G}`);let N=C==="dual"?`${Y.name}-${G}`:Y.name;if(Z.push(`${$}#video:id=${N}`),J&&B)Z.push(`${$}#audio:id=audio`),J=!1}if(await ID(Z),await sF(D,E,C,B),await Xu(A,E,C),C==="dual")await Yu(A);return await Ku(A),A}async function sF(u,D,E,F){let{readdir:C,rename:B,mkdir:A}=await import("node:fs/promises"),Z=[];if(E==="h264"||E==="dual")Z.push("h264");if(E==="av1"||E==="dual")Z.push("av1");let J=[];for(let Y of Z)for(let $ of D){let N=E==="dual"?`${$.name}-${Y}`:$.name;J.push(N);let K=O(u,N);await A(K,{recursive:!0})}let G=O(u,"audio");if(F)await A(G,{recursive:!0});let X=await C(u);for(let Y of X){if(Y==="manifest.mpd")continue;if(F&&(Y.startsWith("audio_")||Y==="audio_init.m4s")){let $=O(u,Y),N=O(G,Y);await B($,N);continue}for(let $ of J)if(Y.startsWith(`${$}_`)){let N=O(u,Y),K=O(u,$,Y);await B(N,K);break}}}async function Wu(u,D,E,F,C,B,A){let Z,J;if(B==="dash"||B==="both")Z=await nF(u,D,E,F,C,A);if(B==="hls"||B==="both")J=await aF(D,E,F,C,A);return{manifestPath:Z,hlsManifestPath:J}}async function aF(u,D,E,F,C){let B=O(u,"master.m3u8"),A=[];for(let X of D){let Y=F==="dual"?`${X.name}-h264`:X.name,$=O(u,Y),N=await Uu($),K=N.filter((q)=>q.endsWith(".m4s")).sort((q,V)=>{let L=parseInt(q.match(/_(\d+)\.m4s$/)?.[1]||"0"),y=parseInt(V.match(/_(\d+)\.m4s$/)?.[1]||"0");return L-y}),_=N.find((q)=>q.endsWith("_.mp4"));if(!_||K.length===0)continue;let W=OD(K,_,E),U=O($,"playlist.m3u8");await RD(U,W,"utf-8");let k=parseInt(X.videoBitrate)*1000;A.push({path:`${Y}/playlist.m3u8`,bandwidth:k,resolution:`${X.width}x${X.height}`,fps:X.fps||30})}let Z,J=[];if(C){let X=O(u,"audio"),Y=[];try{Y=await Uu(X)}catch{Y=[]}if(J=Y.filter(($)=>$.endsWith(".m4s")).sort(($,N)=>{let K=parseInt($.match(/_(\d+)\.m4s$/)?.[1]||"0"),_=parseInt(N.match(/_(\d+)\.m4s$/)?.[1]||"0");return K-_}),Z=Y.find(($)=>$.endsWith("_.mp4")),Z&&J.length>0){let $=OD(J,Z,E);await RD(O(X,"playlist.m3u8"),$,"utf-8")}}let G=Gu(A,C&&Z!==void 0&&J.length>0);return await RD(B,G,"utf-8"),B}async function jD(u){let{input:D,outputDir:E,segmentDuration:F=2,profiles:C,customProfiles:B,codec:A="dual",format:Z="both",hardwareDecoder:J,hardwareAccelerator:G,quality:X,generateThumbnails:Y=!0,thumbnailConfig:$={},generatePoster:N=!0,posterTimecode:K="00:00:00",parallel:_=!0,onProgress:W}=u,U=YD("/tmp",`dash-converter-${iF()}`);await d(U);let k=Nu(D,Qu(D)),q=YD(E,k);await d(q);let V=YD(q,"conversion.log");xD(V);let{writeFile:L}=await import("node:fs/promises"),y=`===========================================
DASH Conversion Log
Started: ${new Date().toISOString()}
Input: ${D}
Output: ${q}
Codec: ${A}
Format: ${Z}
===========================================
`;await L(V,y,"utf-8");try{return await rF(D,E,U,F,C,B,A,Z,G,J,X,Y,$,N,K,_,W)}finally{let{appendFile:P}=await import("node:fs/promises");try{await P(V,`
Completed: ${new Date().toISOString()}
`,"utf-8")}catch(QD){}try{await _u(U,{recursive:!0,force:!0})}catch(QD){console.warn(`Warning: Failed to cleanup temp directory: ${U}`)}}}async function rF(u,D,E,F,C,B,A,Z,J,G,X,Y,$,N,K,_,W){if(!await a())throw Error("FFmpeg is not installed or not in PATH");if(!await i())throw Error("MP4Box is not installed or not in PATH. Install gpac package.");let U=(z,x,zD,HD)=>{if(W)W({stage:z,percent:x,message:zD,currentProfile:HD})};U("analyzing",0,"Analyzing input video...");let k=await t(u),q=k.hasAudio,V=J&&J!=="auto"?J:"auto",L=await r(),y=await o(),{selected:P,h264Encoder:QD,av1Encoder:UF,warnings:nD}=oF(L,V,A);if(nD.length>0)for(let z of nD)console.warn(` ${z}`);let{selected:ZD}=tF(y,G||"auto"),WF=L.some((z)=>z.av1Encoder),c=A;if(A==="dual"&&!WF)console.warn("⚠️ AV1 hardware encoder not detected. Switching to H.264 only."),c="h264";let R;if(B&&B.length>0){let z=XD(B,k.width,k.height,k.fps,k.videoBitrate);if(z.errors.length>0){console.warn(`
Profile errors:`);for(let x of z.errors)console.warn(` - ${x}`);console.warn("")}if(z.warnings.length>0){console.warn(`
Profile warnings:`);for(let x of z.warnings)console.warn(` - ${x}`);console.warn("")}if(R=z.profiles,R.length===0)throw Error("No valid profiles found in custom list. Check errors above.")}else if(C)R=C;else R=CD(k.width,k.height,k.fps,k.videoBitrate);if(R.length===0)throw Error("No suitable profiles found for input video resolution");let kF=Nu(u,Qu(u)),FD=YD(D,kF);try{await _u(FD,{recursive:!0,force:!0})}catch(z){}await d(FD);let h=[];if(c==="h264"||c==="dual"){let z=QD||"libx264",x=ku(z,"h264");h.push({type:"h264",codec:z,preset:x})}if(c==="av1"||c==="dual"){let z=UF||"libsvtav1",x=ku(z,"av1");h.push({type:"av1",codec:z,preset:x})}let NF=h.map((z)=>z.type.toUpperCase()).join(" + "),QF=P==="cpu"?"CPU":P.toUpperCase();U("analyzing",20,`Using ${NF} encoding (${QF}, decoder ${ZD.toUpperCase()})`,void 0);let _F=P==="cpu"?2:3,_D=new Map;for(let z=0;z<h.length;z++){let{type:x,codec:zD,preset:HD}=h[z],oD=z/h.length,qF=1/h.length;U("encoding",25+oD*40,`Stage 1: Encoding ${x.toUpperCase()} (${R.length} profiles)...`);let xF=x==="h264"?X?.h264:X?.av1,IF=await Ju(u,E,R,zD,HD,k.duration,F,k.audioBitrate,_,_F,x,xF,void 0,ZD==="cpu"?void 0:ZD,(ED,tD)=>{let f8=R.findIndex((VF)=>VF.name===ED),eD=25+oD*40,Du=tD/100*(40*qF/R.length);if(U("encoding",eD+Du,`Encoding ${x.toUpperCase()} ${ED}...`,`${x}-${ED}`),W)W({stage:"encoding",percent:eD+Du,currentProfile:`${x}-${ED}`,profilePercent:tD,message:`Encoding ${x.toUpperCase()} ${ED}...`})});_D.set(x,IF)}U("encoding",65,"Stage 1 complete: All codecs and profiles encoded"),U("encoding",70,"Stage 2: Creating segments and manifests...");let{manifestPath:zF,hlsManifestPath:HF}=await Wu(_D,FD,R,F,c,Z,q),sD=[];for(let z of _D.values())sD.push(...Array.from(z.values()));U("encoding",80,"Stage 2 complete: All formats packaged");let aD,iD;if(Y){U("thumbnails",80,"Generating thumbnail sprites...");let z={width:$.width||160,height:$.height||90,interval:$.interval||1,columns:$.columns||10},x=await Zu(u,FD,k.duration,z);aD=x.spritePath,iD=x.vttPath,U("thumbnails",90,"Thumbnails generated")}let rD;if(N)U("thumbnails",92,"Generating poster image..."),rD=await Au(u,FD,K),U("thumbnails",95,"Poster generated");return U("manifest",95,"Finalizing..."),U("complete",100,"Conversion complete!"),{manifestPath:zF,hlsManifestPath:HF,videoPaths:sD,thumbnailSpritePath:aD,thumbnailVttPath:iD,posterPath:rD,duration:k.duration,profiles:R,usedNvenc:h.some((z)=>z.codec.includes("nvenc")),selectedAccelerator:P,selectedDecoder:ZD,codecType:c,format:Z}}var e={nvenc:100,qsv:90,amf:80,vaapi:70,videotoolbox:65,v4l2:60,cpu:1};function oF(u,D,E){let F=E==="h264"||E==="dual",C=E==="av1"||E==="dual",B=[],A=new Set(["nvenc","qsv","amf"]),Z=u.filter((U)=>F&&U.h264Encoder||C&&U.av1Encoder),J=Z.filter((U)=>A.has(U.accelerator)),G=(U)=>Z.find((k)=>k.accelerator===U),X;if(D!=="auto"){if(D==="cpu")X=void 0;else if(!A.has(D))B.push(`Ускоритель "${D}" пока не поддерживается, использую CPU`);else if(X=G(D),!X)throw Error(`Аппаратный ускоритель "${D}" недоступен в системе`)}else if(X=(J.length>0?J:[]).sort((k,q)=>(e[q.accelerator]||0)-(e[k.accelerator]||0))[0],!X&&Z.length>0)B.push("Доступен аппаратный ускоритель, но он пока не поддерживается пайплайном, использую CPU");let $=(J.length>0?J:[]).sort((U,k)=>(e[k.accelerator]||0)-(e[U.accelerator]||0)),N=(U)=>{let k=U==="h264"?X?.h264Encoder:X?.av1Encoder;if(k)return{encoder:k,accel:X?.accelerator};let q=$.find((V)=>U==="h264"?V.h264Encoder:V.av1Encoder);if(q){if(D!=="auto"&&X)B.push(`Выбранный ускоритель "${X.accelerator}" не поддерживает ${U.toUpperCase()}, использую ${q.accelerator}`);return{encoder:U==="h264"?q.h264Encoder:q.av1Encoder,accel:q.accelerator}}if(D!=="auto"&&D!=="cpu")B.push(`Ускоритель "${D}" не поддерживает ${U.toUpperCase()}, использую CPU`);return{encoder:void 0,accel:"cpu"}},K=F?N("h264"):{encoder:void 0,accel:X?.accelerator},_=C?N("av1"):{encoder
Options:`),console.error(" -r, --resolutions Video resolutions (e.g., 360,480,720 or 720@60,1080@60)"),console.error(" -c, --codec Video codec: av1, h264, or dual (default: dual)"),console.error(" -f, --format Streaming format: dash, hls, or both (default: both)"),console.error(" -p, --poster Poster timecode (e.g., 00:00:05 or 10)"),console.error(" -e, --encoder <type> Hardware encoder: auto|nvenc|qsv|amf|vaapi|videotoolbox|v4l2|cpu (default: auto)"),console.error(" -d, --decoder <type> Hardware decoder: auto|nvenc|qsv|amf|vaapi|videotoolbox|v4l2|cpu (default: auto)"),console.error(`
Quality Options (override defaults):`),console.error(" --h264-cq <value> H.264 GPU CQ value (0-51, lower = better, default: auto)"),console.error(" --h264-crf <value> H.264 CPU CRF value (0-51, lower = better, default: auto)"),console.error(" --av1-cq <value> AV1 GPU CQ value (0-51, lower = better, default: auto)"),console.error(" --av1-crf <value> AV1 CPU CRF value (0-63, lower = better, default: auto)"),console.error(`
Examples:`),console.error(" create-vod video.mp4"),console.error(" create-vod video.mp4 ./output"),console.error(" create-vod video.mp4 -r 360,480,720"),console.error(" create-vod video.mp4 -c av1 --av1-cq 40"),console.error(" create-vod video.mp4 -c dual --h264-cq 30 --av1-cq 39"),console.error(" create-vod video.mp4 -f hls"),console.error(" create-vod video.mp4 -c dual -f both"),console.error(" create-vod video.mp4 -r 720@60,1080@60,2160@60 -c av1 -f dash"),console.error(" create-vod video.mp4 -p 00:00:05"),console.error(" create-vod video.mp4 ./output -r 720,1080 -c dual -f both -p 10 --h264-cq 28 --av1-cq 37"),process.exit(1);console.log(`\uD83D\uDD0D Checking system...
`);var XF=await a(),YF=await i(),pD=await r(),GF=pD.some((u)=>u.av1Encoder),ND=await o(),BF={nvenc:100,qsv:90,amf:80,vaapi:70,videotoolbox:65,v4l2:60},AF=pD.slice().sort((u,D)=>(BF[D.accelerator]||0)-(BF[u.accelerator]||0))[0];console.log(`FFmpeg: ${XF?"✅":"❌"}`);console.log(`MP4Box: ${YF?"✅":"❌"}`);var O8=Array.from(new Set(pD.map((u)=>u.accelerator.toUpperCase()))),AD=AF?AF.accelerator.toUpperCase():void 0,ZF=O8.filter((u)=>u!==AD),R8=AD?` ${AD}${ZF.length>0?` (${ZF.join(", ")})`:""}`:"❌";console.log(`Hardware: ${R8}`);if(ND.length>0){let u=Array.from(new Set(ND.map((D)=>D.accelerator.toUpperCase())));console.log(`Decoders: ${u.join(", ")}`)}console.log("");if(!XF)console.error("❌ FFmpeg not found. Please install FFmpeg first."),process.exit(1);if(!YF)console.error("❌ MP4Box not found. Please install: sudo pacman -S gpac"),process.exit(1);if((j==="av1"||j==="dual")&&!GF){if(j==="av1")console.error("⚠️ Warning: AV1 encoding requested but no hardware AV1 encoder found."),console.error(" CPU-based AV1 encoding (libsvtav1) will be VERY slow."),console.error(` Consider using --codec h264 for faster encoding.
`);else if(j==="dual")console.warn("⚠️ AV1 hardware encoder not detected. Using H.264 only (disable AV1)."),j="h264"}if((DD==="hls"||DD==="both")&&j==="av1")console.error("❌ Error: HLS format requires H.264 codec for Safari/iOS compatibility."),console.error(` Please use --codec h264 or --codec dual with --format hls
`),process.exit(1);console.log(`\uD83D\uDCCA Analyzing video...
`);var M=await t(uD),j8=L8(uD),w8=(j8.size/1048576).toFixed(2);console.log("\uD83D\uDCF9 Video Information:");console.log(` File: ${uD}`);console.log(` Size: ${w8} MB`);console.log(` Resolution: ${M.width}x${M.height}`);console.log(` FPS: ${M.fps.toFixed(2)}`);console.log(` Duration: ${Math.floor(M.duration/60)}m ${Math.floor(M.duration%60)}s`);console.log(` Codec: ${M.codec}`);if(M.videoBitrate)console.log(` Video Bitrate: ${(M.videoBitrate/1000).toFixed(2)} Mbps`);if(M.audioBitrate)console.log(` Audio Bitrate: ${M.audioBitrate} kbps`);var cD=[];if(BD&&BD.length>0){let u=XD(BD,M.width,M.height,M.fps,M.videoBitrate);if(u.errors.length>0)console.error(`
Profile errors:`),u.errors.forEach((D)=>console.error(` - ${D}`)),process.exit(1);if(u.warnings.length>0)console.warn(`
Profile warnings:`),u.warnings.forEach((D)=>console.warn(` - ${D}`));cD=u.profiles.map((D)=>D.name)}else cD=CD(M.width,M.height,M.fps,M.videoBitrate).map((D)=>D.name);var S8=DD==="both"?"DASH (manifest.mpd), HLS (master.m3u8)":DD==="dash"?"DASH (manifest.mpd)":"HLS (master.m3u8)",T8=!0,b8=lD||"00:00:00",y8=j==="dual"?"dual (AV1 + H.264)":j,v8=j==="h264"&&!GF?" (AV1 disabled: no HW)":"",$F=WD?WD.toUpperCase():AD||"CPU",JF=kD?kD.toUpperCase():ND[0]?.accelerator.toUpperCase()||"CPU",P8=$F==="AUTO"?AD||"CPU":$F,h8=JF==="AUTO"?ND[0]?.accelerator.toUpperCase()||"CPU":JF;console.log(`
\uD83D\uDCE6 Parameters:`);console.log(` Input: ${uD}`);console.log(` Output: ${KF}`);console.log(` Codec: ${y8}${v8}`);console.log(` Profiles: ${cD.join(", ")}`);console.log(` Manifests: ${S8}`);console.log(` Poster: ${b8} (will be generated)`);console.log(` Thumbnails: ${T8?"yes (with VTT)":"no"}`);console.log(` Accelerator: ${P8}`);console.log(` Decoder: ${h8}`);var m;if(w!==void 0||S!==void 0||T!==void 0||b!==void 0){if(m={},w!==void 0||S!==void 0){if(m.h264={},w!==void 0)m.h264.cq=w;if(S!==void 0)m.h264.crf=S;console.log(`\uD83C\uDF9A H.264 Quality: ${w!==void 0?`CQ ${w}`:""}${S!==void 0?` CRF ${S}`:""}`)}if(T!==void 0||b!==void 0){if(m.av1={},T!==void 0)m.av1.cq=T;if(b!==void 0)m.av1.crf=b;console.log(`\uD83C\uDF9A AV1 Quality: ${T!==void 0?`CQ ${T}`:""}${b!==void 0?` CRF ${b}`:""}`)}}console.log(`
\uD83D\uDE80 Starting conversion...
`);var UD=new mD.default.MultiBar({format:"{stage} | {bar} | {percentage}% | {name}",barCompleteChar:"█",barIncompleteChar:"░",hideCursor:!0,clearOnComplete:!1,stopOnComplete:!0},mD.default.Presets.shades_classic),fD={},gD=null;try{let u=Date.now(),D=await jD({input:uD,outputDir:KF,customProfiles:BD,posterTimecode:lD,codec:j,format:DD,segmentDuration:2,hardwareAccelerator:WD,hardwareDecoder:kD,quality:m,generateThumbnails:!0,generatePoster:!0,parallel:!0,onProgress:(C)=>{let B=C.stage==="encoding"?"Encoding":C.stage==="thumbnails"?"Thumbnails":C.stage==="manifest"?"Manifest":C.stage==="analyzing"?"Analyzing":"Complete";if(C.stage==="encoding"&&C.currentProfile){if(!fD[C.currentProfile])fD[C.currentProfile]=UD.create(100,0,{stage:"Encode",name:C.currentProfile});let A=C.profilePercent??C.percent;fD[C.currentProfile].update(A,{stage:"Encode",name:C.currentProfile})}if(!gD)gD=UD.create(100,0,{stage:B,name:"Overall"});gD.update(C.percent,{stage:B,name:C.message||"Overall"})}});UD.stop();let F=((Date.now()-u)/1000).toFixed(2);console.log(`
Conversion completed successfully! (${F}s)
`)}catch(u){UD.stop(),console.error(`
Error during conversion:`),console.error(u),process.exit(1)}