Files
adaptive-video-converter/bin/cli.js

82 lines
49 KiB
JavaScript
Raw Normal View History

2025-11-09 01:28:42 +03:00
#!/usr/bin/env node
import{createRequire as FF}from"node:module";var tu=Object.create;var{getPrototypeOf:eu,defineProperty:gD,getOwnPropertyNames:DF}=Object;var uF=Object.prototype.hasOwnProperty;var e=(D,u,F)=>{F=D!=null?tu(eu(D)):{};let E=u||!D||!D.__esModule?gD(F,"default",{value:D,enumerable:!0}):F;for(let C of DF(D))if(!uF.call(E,C))gD(E,C,{get:()=>D[C],enumerable:!0});return E};var V=(D,u)=>()=>(u||D((u={exports:{}}).exports,u),u.exports);var f=FF(import.meta.url);var Eu=V((f3,Fu)=>{class uu{constructor(D,u,F){this.etaBufferLength=D||100,this.valueBuffer=[F],this.timeBuffer=[u],this.eta="0"}update(D,u,F){this.valueBuffer.push(u),this.timeBuffer.push(D),this.calculate(F-u)}getTime(){return this.eta}calculate(D){let u=this.valueBuffer.length,F=Math.min(this.etaBufferLength,u),E=this.valueBuffer[u-1]-this.valueBuffer[u-F],C=this.timeBuffer[u-1]-this.timeBuffer[u-F],A=E/C;this.valueBuffer=this.valueBuffer.slice(-this.etaBufferLength),this.timeBuffer=this.timeBuffer.slice(-this.etaBufferLength);let B=Math.ceil(D/A/1000);if(isNaN(B))this.eta="NULL";else if(!isFinite(B))this.eta="INF";else if(B>1e7)this.eta="INF";else if(B<0)this.eta=0;else this.eta=B}}Fu.exports=uu});var QD=V((g3,Bu)=>{var c=f("readline");class Cu{constructor(D){this.stream=D,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(D){if(!this.stream.isTTY)return;if(D)this.stream.write("\x1B[?25h");else this.stream.write("\x1B[?25l")}cursorTo(D=null,u=null){if(!this.stream.isTTY)return;c.cursorTo(this.stream,D,u)}cursorRelative(D=null,u=null){if(!this.stream.isTTY)return;this.dy=this.dy+u,c.moveCursor(this.stream,D,u)}cursorRelativeReset(){if(!this.stream.isTTY)return;c.moveCursor(this.stream,0,-this.dy),c.cursorTo(this.stream,0,null),this.dy=0}clearRight(){if(!this.stream.isTTY)return;c.clearLine(this.stream,1)}clearLine(){if(!this.stream.isTTY)return;c.clearLine(this.stream,0)}clearBottom(){if(!this.stream.isTTY)return;c.clearScreenDown(this.stream)}newline(){this.stream.write(`
`),this.dy++}write(D,u=!1){if(this.linewrap===!0&&u===!1)this.stream.write(D.substr(0,this.getWidth()));else this.stream.write(D)}lineWrapping(D){if(!this.stream.isTTY)return;if(this.linewrap=D,D)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)}}Bu.exports=Cu});var Zu=V((m3,Au)=>{Au.exports=({onlyFirst:D=!1}={})=>{let u=["[\\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(u,D?void 0:"g")}});var $u=V((c3,Yu)=>{var VF=Zu();Yu.exports=(D)=>typeof D==="string"?D.replace(VF(),""):D});var Ku=V((d3,qD)=>{var Ju=(D)=>{if(Number.isNaN(D))return!1;if(D>=4352&&(D<=4447||D===9001||D===9002||11904<=D&&D<=12871&&D!==12351||12880<=D&&D<=19903||19968<=D&&D<=42182||43360<=D&&D<=43388||44032<=D&&D<=55203||63744<=D&&D<=64255||65040<=D&&D<=65049||65072<=D&&D<=65131||65281<=D&&D<=65376||65504<=D&&D<=65510||110592<=D&&D<=110593||127488<=D&&D<=127569||131072<=D&&D<=262141))return!0;return!1};qD.exports=Ju;qD.exports.default=Ju});var Gu=V((l3,Xu)=>{Xu.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
=== FFmpeg Command [${new Date().toISOString()}] ===
ffmpeg ${D.join(" ")}
`;return await v(C),new Promise((A,B)=>{let Z=p("ffmpeg",D),Y="";Z.stderr.on("data",(K)=>{let G=K.toString();if(Y+=G,u&&F){let $=G.match(/time=(\d{2}):(\d{2}):(\d{2}\.\d{2})/);if($){let U=parseInt($[1]),J=parseInt($[2]),X=parseFloat($[3]),_=U*3600+J*60+X,W=Math.min(100,_/F*100);u(W)}}}),Z.on("error",(K)=>{v(`ERROR: ${K.message}
`),B(Error(`FFmpeg error: ${K.message}`))}),Z.on("close",(K)=>{if(K===0)v(`SUCCESS: Exit code ${K}
`),A();else v(`FAILED: Exit code ${K}
${Y}
`),B(Error(`FFmpeg failed with exit code ${K}
${Y}`))})})}async function GD(D){let F=`
=== MP4Box Command [${new Date().toISOString()}] ===
MP4Box ${D.join(" ")}
`;return await v(F),new Promise((E,C)=>{let A=p("MP4Box",D),B="",Z="";A.stdout.on("data",(Y)=>{B+=Y.toString()}),A.stderr.on("data",(Y)=>{Z+=Y.toString()}),A.on("error",(Y)=>{v(`ERROR: ${Y.message}
`),C(Error(`MP4Box error: ${Y.message}`))}),A.on("close",(Y)=>{if(Y===0)v(`SUCCESS: Exit code ${Y}
`),E();else{let K=Z||B;v(`FAILED: Exit code ${Y}
${K}
`),C(Error(`MP4Box failed with exit code ${Y}
${K}`))}})})}import{spawn as CF}from"node:child_process";async function i(D){return new Promise((u,F)=>{let E=CF("ffprobe",["-v","error","-show_entries","stream=width,height,duration,r_frame_rate,codec_name,codec_type,bit_rate","-show_entries","format=duration","-of","json",D]),C="";E.stdout.on("data",(A)=>{C+=A.toString()}),E.on("error",(A)=>{F(Error(`ffprobe error: ${A.message}`))}),E.on("close",(A)=>{if(A!==0){F(Error(`ffprobe failed with exit code ${A}`));return}try{let B=JSON.parse(C),Z=B.streams.find((X)=>X.codec_type==="video"),Y=B.streams.find((X)=>X.codec_type==="audio"&&X.bit_rate),K=B.format;if(!Z){F(Error("No video stream found in input file"));return}let G=30;if(Z.r_frame_rate){let[X,_]=Z.r_frame_rate.split("/").map(Number);if(X&&_&&_!==0)G=X/_}let $=parseFloat(Z.duration||K.duration||"0"),U=Y?.bit_rate?Math.round(parseInt(Y.bit_rate)/1000):void 0,J=Z.bit_rate?Math.round(parseInt(Z.bit_rate)/1000):void 0;u({width:Z.width,height:Z.height,duration:$,fps:G,codec:Z.codec_name,audioBitrate:U,videoBitrate:J})}catch(B){F(Error(`Failed to parse ffprobe output: ${B}`))}})})}function uD(D,u=256){if(!D)return`${u}k`;let F=Math.min(D,u);if(F<=64)return"64k";if(F<=96)return"96k";if(F<=128)return"128k";if(F<=192)return"192k";return"256k"}function FD(D){let u=Math.floor(D/3600),F=Math.floor(D%3600/60),E=D%60;return`${String(u).padStart(2,"0")}:${String(F).padStart(2,"0")}:${E.toFixed(3).padStart(6,"0")}`}import{mkdir as BF,access as AF,constants as ZF}from"node:fs/promises";async function m(D){try{await AF(D,ZF.F_OK)}catch{await BF(D,{recursive:!0})}}function YF(D,u){let F=D*u;if(F<=230400)return 0.08;if(F<=409920)return 0.075;if(F<=921600)return 0.07;if(F<=2073600)return 0.065;if(F<=3686400)return 0.06;return 0.055}function y(D,u,F=30,E){let C=YF(D,u),A=Math.round(D*u*F*C/1000);if(E&&A>E)A=E;return`${A}k`}var _D=[{name:"360p",width:640,height:360,videoBitrate:y(640,360,30),audioBitrate:"192k"},{name:"480p",width:854,height:480,videoBitrate:y(854,480,30),audioBitrate:"192k"},{name:"720p",width:1280,height:720,videoBitrate:y(1280,720,30),audioBitrate:"192k"},{name:"1080p",width:1920,height:1080,videoBitrate:y(1920,1080,30),audioBitrate:"256k"},{name:"1440p",width:2560,height:1440,videoBitrate:y(2560,1440,30),audioBitrate:"256k"},{name:"2160p",width:3840,height:2160,videoBitrate:y(3840,2160,30),audioBitrate:"256k"}];function UD(D,u,F=30,E){let C=[],A=_D.filter((B)=>{return B.width<=D&&B.height<=u});for(let B of A)C.push({...B,videoBitrate:y(B.width,B.height,30,E),fps:30});return C}function $F(D,u,F){return{...D,name:`${D.name}-${u}`,videoBitrate:y(D.width,D.height,u,F),fps:u}}function mD(D){let F=D.trim().match(/^(\d+)p?(?:[@-](\d+))?$/i);if(!F)return null;let E=F[1]+"p",C=F[2]?parseInt(F[2]):30;return{resolution:E,fps:C}}function cD(D,u=30,F){let E=_D.find((C)=>C.name===D);if(!E)return null;if(u===30)return{...E,videoBitrate:y(E.width,E.height,30,F),fps:30};return $F(E,u,F)}function JF(D,u,F,E){let C=mD(D);if(!C)return{error:`Invalid profile format: ${D}. Use format like: 360, 720@60, 1080-60`};let A=cD(C.resolution,C.fps);if(!A)return{error:`Unknown resolution: ${C.resolution}. Available: 360, 480, 720, 1080, 1440, 2160`};if(A.width>u||A.height>F)return{error:`Source resolution (${u}x${F}) is lower than ${D} (${A.width}x${A.height})`};let B=120,Z=C.fps,Y;if(C.fps>E)Z=Math.min(E,B),Y=`Requested ${C.fps} FPS in ${D}, but source is ${E} FPS. Using ${Z} FPS instead`;else if(C.fps>B)Z=B,Y=`Requested ${C.fps} FPS in ${D} exceeds maximum ${B} FPS. Using ${Z} FPS instead`;return Y?{warning:Y,adjustedFps:Z}:{}}function dD(D,u,F,E,C){let A=[],B=[],Z=[];for(let Y of D){let K=JF(Y,u,F,E);if(K.error){B.push(K.error);continue}if(K.warning)Z.push(K.warning);let G=mD(Y);if(!G)continue;let $=K.adjustedFps!==void 0?K.adjustedFps:G.fps,U=cD(G.resolution,$,C);if(U)A.push(U)}return{profiles:A,errors:B,warnings:Z}}import{join as h}from"node:path";import{readdir as KF,unlink as lD,rmdir as XF,writeFile as pD}from"node:fs/promises";async function nD(D,u,F="00:00:01"){let E=h(u,"poster.jpg"),C=/^\d+(\.\d+)?$/.test(F)?F:F;return await g(["-ss",C,"
2025-11-09 01:28:42 +03:00
`;for(let Z=0;Z<D;Z++){let Y=Z*u,K=(Z+1)*u,G=Math.floor(Z/C),U=Z%C*F,J=G*E;B+=`${FD(Y)} --> ${FD(K)}
`,B+=`${A}#xywh=${U},${J},${F},${E}
2025-11-09 01:28:42 +03:00
`}return B}import{join as _F}from"node:path";function UF(D,u,F){if(F)if(u==="h264")return 32;else return 42;else if(u==="h264"){if(D<=360)return 25;if(D<=480)return 24;if(D<=720)return 23;if(D<=1080)return 22;if(D<=1440)return 21;return 20}else{if(D<=360)return 40;if(D<=480)return 38;if(D<=720)return 35;if(D<=1080)return 32;if(D<=1440)return 30;return 28}}async function aD(D,u,F,E,C,A,B,Z,Y,K,G,$){let U=_F(u,`video_${Y}_${F.name}.mp4`),J=["-y","-i",D,"-c:v",E],X=E.includes("nvenc")||E.includes("qsv")||E.includes("amf"),_;if(X&&K?.cq!==void 0)_=K.cq;else if(!X&&K?.crf!==void 0)_=K.crf;else _=UF(F.height,Y,X);if(E==="h264_nvenc")J.push("-rc:v","vbr"),J.push("-cq",String(_)),J.push("-preset",C),J.push("-2pass","0");else if(E==="av1_nvenc")J.push("-rc:v","vbr"),J.push("-cq",String(_)),J.push("-preset",C),J.push("-2pass","0");else if(E==="av1_qsv")J.push("-preset",C),J.push("-global_quality",String(_));else if(E==="av1_amf")J.push("-quality","balanced"),J.push("-rc","cqp"),J.push("-qp_i",String(_)),J.push("-qp_p",String(_));else if(E==="libsvtav1")J.push("-crf",String(_)),J.push("-preset",C),J.push("-svtav1-params","tune=0:enable-overlays=1");else if(E==="libx264")J.push("-crf",String(_)),J.push("-preset",C);else J.push("-preset",C);let W=Y==="av1"?0.6:1,k=Math.round(parseInt(F.videoBitrate)*W*1.5);J.push("-maxrate",`${k}k`),J.push("-bufsize",`${k*2}k`);let R=F.fps||30,N=Math.round(R*B);J.push("-g",String(N),"-keyint_min",String(N),"-sc_threshold","0");let H=[`scale=${F.width}:${F.height}`];if(G){if(G.deinterlace)H.push("yadif");if(G.denoise)H.push("hqdn3d");if(G.customFilters)H.push(...G.customFilters)}J.push("-vf",H.join(","));let T=parseInt(F.audioBitrate)||256,I=uD(Z,T);if(J.push("-c:a","aac","-b:a",I),G?.audioNormalize)J.push("-af","loudnorm");return J.push("-f","mp4",U),await g(J,$,A),U}async function rD(D,u,F,E,C,A,B,Z,Y,K,G,$,U,J){let X=new Map;if(Y&&F.length>1)for(let _=0;_<F.length;_+=K){let W=F.slice(_,_+K),k=W.map((N)=>aD(D,u,N,E,C,A,B,Z,G,$,U,(H)=>{if(J)J(N.name,H)}));(await Promise.all(k)).forEach((N,H)=>{let T=W[H];X.set(T.name,N)})}else for(let _ of F){let W=await aD(D,u,_,E,C,A,B,Z,G,$,U,(k)=>{if(J)J(_.name,k)});X.set(_.name,W)}return X}import{join as x}from"node:path";import{readFile as WF,writeFile as ED,readdir as WD,rename as kD,mkdir as iD}from"node:fs/promises";async function kF(D){let{readFile:u,writeFile:F}=await import("node:fs/promises"),C=(await u(D,"utf-8")).split(`
`),A=[],B=0;while(B<C.length){let Z=C[B];if(Z.includes("<AdaptationSet")&&Z.includes("maxWidth")){let Y=B,K=[Z],G=[],$=[],U=[],J=!1;B++;while(B<C.length&&!C[B].includes("</AdaptationSet>")){let X=C[B];if(X.includes("<SegmentTemplate"))J=!0;if(J){if(G.push(X),X.includes("</SegmentTemplate>"))J=!1}else if(X.includes("<Representation")&&X.includes("-h264"))$.push(X);else if(X.includes("<Representation")&&X.includes("-av1"))U.push(X);B++}if($.length>0&&U.length>0)A.push(Z),G.forEach((X)=>A.push(X)),$.forEach((X)=>A.push(X)),A.push(" </AdaptationSet>"),A.push(Z),G.forEach((X)=>A.push(X)),U.forEach((X)=>A.push(X)),A.push(" </AdaptationSet>");else{A.push(Z);for(let X=Y+1;X<B;X++)A.push(C[X]);A.push(C[B])}B++}else A.push(Z),B++}await F(D,A.join(`
`),"utf-8")}async function oD(D,u,F,E,C,A){let B=x(u,".temp_manifest.mpd"),Z=["-dash",String(E*1000),"-frag",String(E*1000),"-rap","-segment-timeline","-segment-name","$RepresentationID$_$Number$","-out",B],Y=!0;for(let[$,U]of D.entries())for(let J of F){let X=U.get(J.name);if(!X)throw Error(`MP4 file not found for profile: ${J.name}, codec: ${$}`);let _=C==="dual"?`${J.name}-${$}`:J.name;if(Z.push(`${X}#video:id=${_}`),Y)Z.push(`${X}#audio:id=audio`),Y=!1}await GD(Z),await HF(u,F,C);let K,G;if(A==="dash"||A==="both"){if(K=x(u,"manifest.mpd"),await kD(B,K),await QF(K,F,C),C==="dual")await kF(K)}if(A==="hls"||A==="both")G=await qF(u,F,E,C);try{let{unlink:$}=await import("node:fs/promises");await $(B)}catch{}return{manifestPath:K,hlsManifestPath:G}}async function HF(D,u,F){let E=[],C=[];if(F==="h264"||F==="dual")C.push("h264");if(F==="av1"||F==="dual")C.push("av1");for(let Z of C)for(let Y of u){let K=F==="dual"?`${Y.name}-${Z}`:Y.name;E.push(K);let G=x(D,K);await iD(G,{recursive:!0})}let A=x(D,"audio");await iD(A,{recursive:!0});let B=await WD(D);for(let Z of B){if(Z.endsWith(".mpd")||Z.endsWith(".m3u8")||!Z.includes("_"))continue;if(Z.startsWith("audio_")){let Y=x(D,Z),K=x(A,Z);await kD(Y,K);continue}for(let Y of E)if(Z.startsWith(`${Y}_`)){let K=x(D,Z),G=x(D,Y,Z);await kD(K,G);break}}}async function QF(D,u,F){let E=await WF(D,"utf-8");E=E.replace(/media="\$RepresentationID\$_\$Number\$\.m4s"/g,'media="$RepresentationID$/$RepresentationID$_$Number$.m4s"'),E=E.replace(/initialization="\$RepresentationID\$_\.mp4"/g,'initialization="$RepresentationID$/$RepresentationID$_.mp4"'),await ED(D,E,"utf-8")}async function qF(D,u,F,E){let C=x(D,"master.m3u8"),A=[];for(let $ of u){let U=E==="dual"?`${$.name}-h264`:$.name,J=x(D,U),X=await WD(J),_=X.filter((H)=>H.endsWith(".m4s")).sort((H,T)=>{let I=parseInt(H.match(/_(\d+)\.m4s$/)?.[1]||"0"),O=parseInt(T.match(/_(\d+)\.m4s$/)?.[1]||"0");return I-O}),W=X.find((H)=>H.endsWith("_.mp4"));if(!W||_.length===0)continue;let k=`#EXTM3U
`;k+=`#EXT-X-VERSION:6
`,k+=`#EXT-X-TARGETDURATION:${Math.ceil(F)}
`,k+=`#EXT-X-MEDIA-SEQUENCE:1
`,k+=`#EXT-X-INDEPENDENT-SEGMENTS
`,k+=`#EXT-X-MAP:URI="${W}"
`;for(let H of _)k+=`#EXTINF:${F},
`,k+=`${H}
`;k+=`#EXT-X-ENDLIST
`;let R=x(J,"playlist.m3u8");await ED(R,k,"utf-8");let N=parseInt($.videoBitrate)*1000;A.push({path:`${U}/playlist.m3u8`,bandwidth:N,resolution:`${$.width}x${$.height}`,fps:$.fps||30})}let B=x(D,"audio"),Z=await WD(B),Y=Z.filter(($)=>$.endsWith(".m4s")).sort(($,U)=>{let J=parseInt($.match(/_(\d+)\.m4s$/)?.[1]||"0"),X=parseInt(U.match(/_(\d+)\.m4s$/)?.[1]||"0");return J-X}),K=Z.find(($)=>$.endsWith("_.mp4"));if(K&&Y.length>0){let $=`#EXTM3U
`;$+=`#EXT-X-VERSION:6
`,$+=`#EXT-X-TARGETDURATION:${Math.ceil(F)}
`,$+=`#EXT-X-MEDIA-SEQUENCE:1
`,$+=`#EXT-X-INDEPENDENT-SEGMENTS
`,$+=`#EXT-X-MAP:URI="${K}"
`;for(let U of Y)$+=`#EXTINF:${F},
`,$+=`${U}
`;$+=`#EXT-X-ENDLIST
`,await ED(x(B,"playlist.m3u8"),$,"utf-8")}let G=`#EXTM3U
`;G+=`#EXT-X-VERSION:6
`,G+=`#EXT-X-INDEPENDENT-SEGMENTS
`,G+=`#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio",NAME="audio",AUTOSELECT=YES,URI="audio/playlist.m3u8",CHANNELS="2"
`;for(let $ of A)G+=`#EXT-X-STREAM-INF:BANDWIDTH=${$.bandwidth},CODECS="avc1.4D4020,mp4a.40.2",RESOLUTION=${$.resolution},FRAME-RATE=${$.fps},AUDIO="audio"
`,G+=`${$.path}
`;return await ED(C,G,"utf-8"),C}async function HD(D){let{input:u,outputDir:F,segmentDuration:E=2,profiles:C,customProfiles:A,codec:B="dual",format:Z="both",useNvenc:Y,quality:K,generateThumbnails:G=!0,thumbnailConfig:$={},generatePoster:U=!0,posterTimecode:J="00:00:01",parallel:X=!0,onProgress:_}=D,W=CD("/tmp",`dash-converter-${zF()}`);await m(W);let k=tD(u,eD(u)),R=CD(F,k);await m(R);let N=CD(R,"conversion.log");XD(N);let{writeFile:H}=await import("node:fs/promises"),T=`===========================================
DASH Conversion Log
Started: ${new Date().toISOString()}
Input: ${u}
Output: ${R}
Codec: ${B}
Format: ${Z}
===========================================
`;await H(N,T,"utf-8");try{return await LF(u,F,W,E,C,A,B,Z,Y,K,G,$,U,J,X,_)}finally{let{appendFile:I}=await import("node:fs/promises");try{await I(N,`
Completed: ${new Date().toISOString()}
`,"utf-8")}catch(O){}try{await Du(W,{recursive:!0,force:!0})}catch(O){console.warn(`Warning: Failed to cleanup temp directory: ${W}`)}}}async function LF(D,u,F,E,C,A,B,Z,Y,K,G,$,U,J,X,_){if(!await n())throw Error("FFmpeg is not installed or not in PATH");if(!await s())throw Error("MP4Box is not installed or not in PATH. Install gpac package.");let W=(z,L,o,JD)=>{if(_)_({stage:z,percent:L,message:o,currentProfile:JD})};W("analyzing",0,"Analyzing input video...");let k=await i(D),R=Y!==!1?await a():!1,N=Y===!0?!0:Y===!1?!1:R;if(Y===!0&&!R)throw Error("NVENC requested but not available. Check NVIDIA drivers and GPU support.");let H;if(A&&A.length>0){let z=dD(A,k.width,k.height,k.fps,k.videoBitrate);if(z.errors.length>0){console.warn(`
Profile errors:`);for(let L of z.errors)console.warn(` - ${L}`);console.warn("")}if(z.warnings.length>0){console.warn(`
Profile warnings:`);for(let L of z.warnings)console.warn(` - ${L}`);console.warn("")}if(H=z.profiles,H.length===0)throw Error("No valid profiles found in custom list. Check errors above.")}else if(C)H=C;else H=UD(k.width,k.height,k.fps,k.videoBitrate);if(H.length===0)throw Error("No suitable profiles found for input video resolution");let T=tD(D,eD(D)),I=CD(u,T);try{await Du(I,{recursive:!0,force:!0})}catch(z){}await m(I);let O=[];if(B==="h264"||B==="dual"){let z=N?"h264_nvenc":"libx264",L=N?"p4":"medium";O.push({type:"h264",codec:z,preset:L})}if(B==="av1"||B==="dual"){let z=await r(),L=z.available?z.encoder:"libsvtav1",o=z.available?L==="av1_nvenc"?"p4":"medium":"8";O.push({type:"av1",codec:L,preset:o})}let lu=O.map((z)=>z.type.toUpperCase()).join(" + ");W("analyzing",20,`Using ${lu} encoding (${N?"GPU":"CPU"})`,void 0);let pu=N?3:2,$D=new Map;for(let z=0;z<O.length;z++){let{type:L,codec:o,preset:JD}=O[z],vD=z/O.length,au=1/O.length;W("encoding",25+vD*40,`Stage 1: Encoding ${L.toUpperCase()} (${H.length} profiles)...`);let ru=L==="h264"?K?.h264:K?.av1,iu=await rD(D,F,H,o,JD,k.duration,E,k.audioBitrate,X,pu,L,ru,void 0,(t,hD)=>{let D3=H.findIndex((ou)=>ou.name===t),PD=25+vD*40,fD=hD/100*(40*au/H.length);if(W("encoding",PD+fD,`Encoding ${L.toUpperCase()} ${t}...`,`${L}-${t}`),_)_({stage:"encoding",percent:PD+fD,currentProfile:`${L}-${t}`,profilePercent:hD,message:`Encoding ${L.toUpperCase()} ${t}...`})});$D.set(L,iu)}W("encoding",65,"Stage 1 complete: All codecs and profiles encoded"),W("encoding",70,"Stage 2: Creating segments and manifests...");let{manifestPath:nu,hlsManifestPath:su}=await oD($D,I,H,E,B,Z),bD=[];for(let z of $D.values())bD.push(...Array.from(z.values()));W("encoding",80,"Stage 2 complete: All formats packaged");let SD,TD;if(G){W("thumbnails",80,"Generating thumbnail sprites...");let z={width:$.width||160,height:$.height||90,interval:$.interval||1,columns:$.columns||10},L=await sD(D,I,k.duration,z);SD=L.spritePath,TD=L.vttPath,W("thumbnails",90,"Thumbnails generated")}let yD;if(U)W("thumbnails",92,"Generating poster image..."),yD=await nD(D,I,J),W("thumbnails",95,"Poster generated");return W("manifest",95,"Finalizing..."),W("complete",100,"Conversion complete!"),{manifestPath:nu,hlsManifestPath:su,videoPaths:bD,thumbnailSpritePath:SD,thumbnailVttPath:TD,posterPath:yD,duration:k.duration,profiles:H,usedNvenc:N,codecType:B,format:Z}}var MD=e(fu(),1);import{statSync as oF}from"node:fs";var Q=process.argv.slice(2),ZD,YD,l="dual",d="both",jD=[],M,w,j,b;for(let D=0;D<Q.length;D++)if(Q[D]==="-r"||Q[D]==="--resolutions"){let u=[];for(let E=D+1;E<Q.length;E++){if(Q[E].startsWith("-"))break;u.push(Q[E]),D=E}ZD=u.join(",").split(/[,\s]+/).map((E)=>E.trim()).filter((E)=>E.length>0)}else if(Q[D]==="-p"||Q[D]==="--poster")YD=Q[D+1],D++;else if(Q[D]==="-c"||Q[D]==="--codec"){let u=Q[D+1];if(u==="av1"||u==="h264"||u==="dual")l=u;else console.error(` Invalid codec: ${u}. Valid options: av1, h264, dual`),process.exit(1);D++}else if(Q[D]==="-f"||Q[D]==="--format"){let u=Q[D+1];if(u==="dash"||u==="hls"||u==="both")d=u;else console.error(` Invalid format: ${u}. Valid options: dash, hls, both`),process.exit(1);D++}else if(Q[D]==="--h264-cq"){if(M=parseInt(Q[D+1]),isNaN(M)||M<0||M>51)console.error(` Invalid H.264 CQ value: ${Q[D+1]}. Must be 0-51`),process.exit(1);D++}else if(Q[D]==="--h264-crf"){if(w=parseInt(Q[D+1]),isNaN(w)||w<0||w>51)console.error(` Invalid H.264 CRF value: ${Q[D+1]}. Must be 0-51`),process.exit(1);D++}else if(Q[D]==="--av1-cq"){if(j=parseInt(Q[D+1]),isNaN(j)||j<0||j>51)console.error(` Invalid AV1 CQ value: ${Q[D+1]}. Must be 0-51`),process.exit(1);D++}else if(Q[D]==="--av1-crf"){if(b=parseInt(Q[D+1]),isNaN(b)||b<0||b>63)console.error(` Invalid AV1 CRF value: ${Q[D+1]}. Must be 0-63`),process.exit(1);D++}else if(!Q[D].startsWith("-"))jD.push(Q[D]);var DD=jD[0],gu=jD[1]||".";if(!DD)console.error("❌ Usage: dvc-cli <input-video> [output-dir] [options]"),console.error(`
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(`
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(" dvc-cli video.mp4"),console.error(" dvc-cli video.mp4 ./output"),console.error(" dvc-cli video.mp4 -r 360,480,720"),console.error(" dvc-cli video.mp4 -c av1 --av1-cq 40"),console.error(" dvc-cli video.mp4 -c dual --h264-cq 30 --av1-cq 39"),console.error(" dvc-cli video.mp4 -f hls"),console.error(" dvc-cli video.mp4 -c dual -f both"),console.error(" dvc-cli video.mp4 -r 720@60,1080@60,2160@60 -c av1 -f dash"),console.error(" dvc-cli video.mp4 -p 00:00:05"),console.error(" dvc-cli 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 mu=await n(),cu=await a(),wD=await r(),du=await s();console.log(`FFmpeg: ${mu?"✅":"❌"}`);console.log(`NVENC (H.264): ${cu?"✅ (GPU acceleration)":"⚠️ (CPU only)"}`);if(wD.available)console.log(`AV1 Encoder: ${wD.encoder} (GPU acceleration)`);else console.log("AV1 Encoder: ⚠️ (not available, will use CPU fallback)");console.log(`MP4Box: ${du?"✅":"❌"}
`);if(!mu)console.error("❌ FFmpeg not found. Please install FFmpeg first."),process.exit(1);if(!du)console.error("❌ MP4Box not found. Please install: sudo pacman -S gpac"),process.exit(1);if((l==="av1"||l==="dual")&&!wD.available)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.
`);if((d==="hls"||d==="both")&&l==="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 S=await i(DD),tF=oF(DD),eF=(tF.size/1048576).toFixed(2);console.log("\uD83D\uDCF9 Video Information:");console.log(` File: ${DD}`);console.log(` Size: ${eF} MB`);console.log(` Resolution: ${S.width}x${S.height}`);console.log(` FPS: ${S.fps.toFixed(2)}`);console.log(` Duration: ${Math.floor(S.duration/60)}m ${Math.floor(S.duration%60)}s`);console.log(` Codec: ${S.codec}`);if(S.videoBitrate)console.log(` Video Bitrate: ${(S.videoBitrate/1000).toFixed(2)} Mbps`);if(S.audioBitrate)console.log(` Audio Bitrate: ${S.audioBitrate} kbps`);console.log(`
\uD83D\uDCC1 Output: ${gu}`);console.log(`\uD83C\uDFAC Codec: ${l}${l==="dual"?" (AV1 + H.264 for maximum compatibility)":""}`);console.log(`\uD83D\uDCFA Format: ${d}${d==="both"?" (DASH + HLS for maximum compatibility)":d==="hls"?" (H.264 only for Safari/iOS)":""}`);if(ZD)console.log(`\uD83C\uDFAF Custom profiles: ${ZD.join(", ")}`);if(YD)console.log(`\uD83D\uDDBC Poster timecode: ${YD}`);var P;if(M!==void 0||w!==void 0||j!==void 0||b!==void 0){if(P={},M!==void 0||w!==void 0){if(P.h264={},M!==void 0)P.h264.cq=M;if(w!==void 0)P.h264.crf=w;console.log(`\uD83C\uDF9A H.264 Quality: ${M!==void 0?`CQ ${M}`:""}${w!==void 0?` CRF ${w}`:""}`)}if(j!==void 0||b!==void 0){if(P.av1={},j!==void 0)P.av1.cq=j;if(b!==void 0)P.av1.crf=b;console.log(`\uD83C\uDF9A AV1 Quality: ${j!==void 0?`CQ ${j}`:""}${b!==void 0?` CRF ${b}`:""}`)}}console.log(`
2025-11-09 13:24:10 +03:00
\uD83D\uDE80 Starting conversion...
`);var AD=new MD.default.MultiBar({format:"{stage} | {bar} | {percentage}% | {name}",barCompleteChar:"█",barIncompleteChar:"░",hideCursor:!0,clearOnComplete:!1,stopOnComplete:!0},MD.default.Presets.shades_classic),ID={},OD=null;try{let D=await HD({input:DD,outputDir:gu,customProfiles:ZD,posterTimecode:YD,codec:l,format:d,segmentDuration:2,useNvenc:cu,quality:P,generateThumbnails:!0,generatePoster:!0,parallel:!0,onProgress:(u)=>{let F=u.stage==="encoding"?"Encoding":u.stage==="thumbnails"?"Thumbnails":u.stage==="manifest"?"Manifest":u.stage==="analyzing"?"Analyzing":"Complete";if(u.stage==="encoding"&&u.currentProfile){if(!ID[u.currentProfile])ID[u.currentProfile]=AD.create(100,0,{stage:"Encode",name:u.currentProfile});let E=u.profilePercent??u.percent;ID[u.currentProfile].update(E,{stage:"Encode",name:u.currentProfile})}if(!OD)OD=AD.create(100,0,{stage:F,name:"Overall"});OD.update(u.percent,{stage:F,name:u.message||"Overall"})}});if(AD.stop(),console.log(`
2025-11-09 01:28:42 +03:00
Conversion completed successfully!
`),console.log("\uD83D\uDCCA Results:"),D.manifestPath)console.log(` DASH Manifest: ${D.manifestPath}`);if(D.hlsManifestPath)console.log(` HLS Manifest: ${D.hlsManifestPath}`);if(console.log(` Duration: ${D.duration.toFixed(2)}s`),console.log(` Profiles: ${D.profiles.map((u)=>u.name).join(", ")}`),console.log(` Format: ${D.format}`),console.log(` Codec: ${D.codecType}${D.codecType==="dual"?" (AV1 + H.264)":""}`),console.log(` Encoder: ${D.usedNvenc?"⚡ GPU accelerated":"\uD83D\uDD27 CPU"}`),D.posterPath)console.log(` Poster: ${D.posterPath}`);if(D.thumbnailSpritePath)console.log(` Thumbnails: ${D.thumbnailSpritePath}`),console.log(` VTT file: ${D.thumbnailVttPath}`);console.log(`
\uD83C\uDF89 Done! You can now use the manifest file(s) in your video player.`)}catch(D){AD.stop(),console.error(`
2025-11-09 01:28:42 +03:00
Error during conversion:`),console.error(D),process.exit(1)}