(()=>{var t={413:(t,n,o)=>{const{time:i}=o(540),r=o(24);function a(e,t,n){const o=c(1e3*t),i=c(1e3*n);return"+"===e?Math.abs(c((i+o)/1e3)):Math.abs(c((i-o)/1e3))}function s(e,t){if(t.includes("!ignore!"))return!1;if(t.includes("!remove!"))return!0;const{swearWordString:n,ignoreWords:o}=e,i=new RegExp(`\\b(?:${n})\\b`,"iu");if(i.test(t)){if(o.length>0){const e=new RegExp(`\\b${o.join("|")}\\b`,"ig");return i.test(t.replaceAll(e,""))}return!0}return!1}function c(e){return+(Math.round(e+"e+3")+"e-3")}async function d(e,t){const n=["-hide_banner","-show_format","-show_streams","-of","json",e],o=await p("ffprobe",n,t,!1);t.write(`Video ${e} Specifications ----------------------------------\n${o}\n\n`)}function u(e){const t=e.split("."),n=t.pop().toLowerCase();return{name:t.join("."),ext:n}}async function l(e,t,n){const o=["-v","quiet","-hide_banner","-print_format","flat","-show_entries","mkv"===t||"avi"===t?"format=duration":"stream=duration","-of","default=noprint_wrappers=1:nokey=1","-select_streams","v:0",`${e}.${t}`];let i=await p("ffprobe",o,n,!1);if(i=+i.trim(),Number.isNaN(i))throw n.write(`Duration was NaN: ${i}\n\n`),new Error(`Duration was NaN: ${i}`);return n.write(`Video Length: Seconds.Milliseconds: ${i}, Time: ${m(i)}\n\n`),i}function m(e){const[t,n="000"]=e.toString().split("."),o=Math.floor(t/3600),i=o.toString().padStart(2,"0"),r=Math.floor(t%3600/60);return`${i}:${r.toString().padStart(2,"0")}:${(t-(3600*o+60*r)).toString().padStart(2,"0")},${n.toString().padEnd(3,"0")}`}async function p(t,n,i,r=!0){const{spawn:a}=o(421);return new Promise(((o,s)=>{let c="";try{const e=`Running command: ${t} ${n.join(" ")}\n\n`;i.write(e),console.log(e);const d=a(t,n);d.stdout.on("data",(e=>{r&&console.log(e.toString()),c+=e})),d.stderr.on("data",(e=>{r&&console.log(e.toString()),c+=e})),d.on("close",(e=>{if(0===e)o(c);else{const o=new Error(`Command "${t} ${n.join(" ")}" exited with code ${e}`);o.code=e,o.stdout=c,i.write(`Error: Command "${t} ${n.join(" ")}" exited with code ${e}.\n${c}\n\n`),s(o)}})),d.on("error",(e=>{i.write(e.toString()),s(e)}))}catch(o){i.write(`Error: Command "${t} ${n.join(" ")}" exited with error.\nSTDOUT: ${c}\nERROR: ${e}\n\n`),s(o)}}))}function w(e){if(!e)return"";const[t,n,o,i=0]=e.split(/:|,/);return+`${60*+t*60+60*+n+ +o}.${i}`}t.exports={addSwearWords:function(e,t=[]){const n=Array.isArray(t)?t:t.split(" ");return e.concat(n).join("|")},containsSwearWords:s,convertMsToTime:function(e){function t(e){return e.toString().padStart(2,"0")}let n=Math.floor(e/1e3),o=Math.floor(n/60),i=Math.floor(o/60);return n%=60,o%=60,i%=24,`${t(i)}:${t(o)}:${t(n)}`},deleteFiles:function(e,t){t.write(`Deleting files: ${e.join(", ")}\n`);for(const n of e)r.existsSync(n)?(r.unlinkSync(n),console.log("",`${n} was deleted`),console.log("",""),t.write(`${n} was deleted\n`)):(console.log("",`File ${n} was not found!`),console.log("",""),t.write(`File ${n} was not found!\n`));t.write("\n\n")},extractSubtitle:async function(e,t){const{video:n,args:o,subName:i}=e,r=["-hide_banner","-v","error","-i",n,"-map",`0:s:${o["subtitle-number"]?o["subtitle-number"]:0}`,i];console.log("",`Could not find Subtitle. Trying to Extract ${i}`),console.log("",""),t.write(`Could not find subtitle ${i}. Trying to extract from ${n}.\n\n`);try{const e=await p("ffmpeg",r,t);return t.write(`extractSubtitles:\nstdout: ${e}\n\n`),console.log("",`Extracted ${i}`),console.log("",""),t.write(`Extracted ${i} from ${n}`),!0}catch(e){return console.log("No srt found!",e),t.write(`No srt found! ${e}\n\n`),!1}},filterGraphAndEncode:async function(e,t,n){const{args:o,ext:i,name:a,subName:s,cleanSubName:c,cleanVideoName:u,video:l,videoMeta:m}=e,{keeps:w,blurs:$}=n,f=!o?.cpu;let g=o?.quality?+o.quality:27;Number.isNaN(g)&&(g=26);const b=o?.["audio-number"]?o["audio-number"]:0,h=["-pix_fmt","yuv420p","-profile",o?.h264?"high":"main","-c:v",o?.h264?"h264_nvenc":"hevc_nvenc"],S=[...o?.["10-bit"]?["-pix_fmt","p010le","-profile:v","main10","-c:v","hevc_nvenc"]:h,"-r",m.frameRate,...o?.smallest?["-preset","p7","-multipass",2]:["-preset","p1","-multipass",0],"-b:v",0,"-bf",0,"-g",300,"-me_range",36,"-subq",9,"-keyint_min",1,"-qdiff",20,"-qcomp",.9,"-qmin",17,"-qmax",51,"-qp",g,"-rc","constqp","-tune","hq","-rc-lookahead",4,"-keyint_min",1,"-qdiff",20,"-qcomp",.9],v=["-c:v",o?.h265?"libx265":"libx264","-crf",g],N=["-c:a",m.audioCodec,"-b:a",m.audioBitRate,"-ar",m.audioSampleRate],x=["-map_metadata","-1",...o?.["no-chapters"]?["-map_chapters","-1"]:""],y=["-metadata",`creation_time=${(new Date).toISOString()}`,"-metadata",`title='${a}'`],_=r.existsSync(c),T=["-c:s",o?.copy&&"mkv"===i?"srt":"mov_text","-metadata:s:s:0","language=eng"],k=$.map((e=>`between(t,${e.join(",")})`));let R=["-map","0:v","-map",`0:a:${b}`];if(!o?.skip&&w.length>0){let e="",t=0,n="";for(const[o,[i,r]]of w.entries())e+=`[b${o}v]trim=start=${i}:end=${r},setpts=PTS-STARTPTS[${o}v];[0:a:${b}]atrim=start=${i}:end=${r},asetpts=PTS-STARTPTS[${o}a];`,n+=`[${o}v][${o}a]`,t++;if(R=["-filter_complex",`${e}${n} concat=n=${t}:v=1:a=1[outv][outa]`,"-map","[outv]","-map","[outa]"],k.length>0){const e=R.splice(1,1),n=[];for(let e=0;e""!==e)):M.filter((e=>""!==e)),t);t.write(`filterGraphAndEncode: --------------------------------------------------\n${j}\n\n`),o?.copy?await d(`${a}-withSubtitle.${i}`,t):await d(e.cleanVideoName,t)},getArgs:function(){return Object.fromEntries(process.argv.slice(2).join(" ").split(/ -{1,2}/).map((e=>{console.log("arg",e);const t=e.trim().toLowerCase().replace(/^-{1,2}/,"").split(/=/);return t.length<2&&t.push(!0),t})).filter((e=>!!e[0])))},getCuts:async function(e,t){const{name:n,args:o,ext:i,frameRate:c,subName:d,cleanSubName:u,videoMeta:l}=e,p=function(e,t){try{const n=r.readFileSync(e,"utf-8");t.write(`originalSubtitles:\n${n}\n\n`);const o=n.split(/\r?\n\r?\n\d{1,5}\r?\n?$/m).map(((e,t)=>{let n="",o="";t>0?[n,...o]=e.trim().split(/\r?\n/):[_,n,...o]=e.trim().split(/\r?\n/);const[i,r]=n.split(" --\x3e ");return{id:t+1,start:i,end:r,text:o.join("\n").trim()}}));return t.write(`splitSubtitles:\n${JSON.stringify(o)}\n\n`),o}catch(n){const o=`\n${e} Read Error:\nThere was a problem parsing the subtitle ${e}.\nCheck the first line in ${e} is a number.\nThe problem could also be file header corruption:\n Copy ${e} contents into a new file and save. \n Delete old srt file.\n Rename new file as: ${e}\n\n\n\n`;throw t.write(`${o}`),new Error(o)}}(d,t);t.write("Swear Words Keeps ----------------------------------------------\n\tSRT Time\tSeconds\tFrame\n");const $=[],f=p.filter((({text:e})=>e.includes("!blur!"))),g=p.filter((({text:e})=>!e.includes("!blur")));for(const e of f){const t=Math.floor(w(e.start)),n=Math.ceil(w(e.end));$.push([t,n])}const b=[],h=[];let S=0,v="";const N=[];let x=0;for(const[n,o]of g.entries()){let{id:i,start:r,end:d,text:u}=o;const l=Math.floor(w(r)),p=Math.ceil(w(d));if(s(e,u)){if(0===l){x=p;const e=Math.abs(p-l);S+=e,v+=`Start time was zero. Seconds removed: ${e}.\t`}else{if(Math.abs(l-x)>=2){const e=`between(t,${x},${l})`;N.push([x,l]);const n=Math.abs(p-l);S+=n,v+=`${e}\tSecondsRemoved: ${n}\t`,t.write(`Start:\t${r}\t${l}\t${l*c}\nEnd:\t${d}\t${p}\t${p*c}\n\n`)}else{const e=Math.abs(p-x);S+=e,v+=`Time was less than two seconds! Skipping!\t\tSecondsRemoved: ${e}\t`}x=p}h.push({id:i,start:r,end:d,text:u})}else{const e=m(a("-",w(r),S)),t=m(a("-",w(d),S));u.includes("!ignore!")&&(u=u.replace("!ignore!","")),b.push({id:i,start:e,end:t,text:u})}v+=`index: ${n}, \tid: ${i}\ts: ${x},\tstart: ${l}s, ${r}\tend: ${p}s, ${d}\t\tTotalSecondsRemoved: ${S}s.\n`}xe+`${JSON.stringify(t)}\n`),"");t.write(`Keep Video Segments ---------------------------------------\n${y}\n\n`),t.write(`getCuts: -keepStr. This logs the time removed from subtitles.\n${v}\n\n`);const T=b.map(((e,t)=>({...e,id:t+1}))).reduce(((e,t)=>{const{id:n,start:o,end:i,text:r}=t;return e+`${n}\n${o} --\x3e ${i}\n${r}\n\n`}),"");r.writeFileSync(u,T),t.write(`${u}\n${T}\n\n`);const k=h.reduce(((e,t)=>{const{id:n,start:o,end:i,text:r}=t;return e+`${o} - ${i} \t${r.trim().replace(/\r?\n/g," ")}\n`}),"");return t.write(`getCuts: -Swear Words\n${k}\n\n`),{keeps:N,blurs:$}},getName:u,getMetadata:async function(e,t,n){const{name:o,ext:i}=u(e),r=["-v","quiet","-hide_banner","-show_format","-show_streams","-of","json",e],a=await p("ffprobe",r,n,!1),s=JSON.parse(a),c=Number.isNaN(+t?.["audio-number"])?0:+t["audio-number"]>0?+t["audio-number"]-1:0,d=[],w=[];for(const e of s?.streams||[])"video"===e?.codec_type?.toLowerCase()&&d.push(e),"audio"===e?.codec_type?.toLowerCase()&&w.push(e);const $=d[0],f=w[c];let g=24;const b=$?.r_frame_rate,h=b?.split("/");if(2!==h.length)throw new Error("Video Frame Rate not Found.");const[S,v]=h;Number.isNaN(+S)||Number.isNaN(+v)?n.write("Frame Rate Not Found. -----------------------------\n\n"):g=+(+S/+v).toFixed(3);let N=+f?.sample_rate,x=t?.["audio-bitrate"]?t["audio-bitrate"]:+f?.bit_rate,y=t?.["audio-codec"]?t["audio-codec"]:f?.codec_name;(Number.isNaN(N)||Number.isNaN(x)||!y)&&(n.write(`Audio Codec Problem. Using defaults. -----------------------\nAudio Sample Rate: ${N}\nAudio Bit Rate: ${x}\nAudio Codec: ${y}\n\n`),n.write(`Audio Codec Problem. Audio meta not found.\nAudio Sample Rate: ${N}\nAudio Bit Rate: ${x}\nAudio Codec: ${y}\n\n`),N=48e3,x=t?.["audio-bitrate"]?t["audio-bitrate"]:"448k",y="ac3",n.write(`Using Audio defaults. -----------------------\nAudio Sample Rate: ${N}\nAudio Bit Rate: ${x}\nAudio Codec: ${y}\n\n`));const _=await l(o,i,n);return{frameRateString:b,frameRate:g,audioSampleRate:N,audioBitRate:x,audioCodec:y,duration:_,time:m(_)}},getVideoDuration:l,getVideoNames:function(){const e=["\\.mp4$","\\.mkv$","\\.avi$","\\.webm$"],t=new RegExp(e.join("|")),n=new RegExp(["output","clean","temp","sanitize","withSubtitle"].map((t=>e.map((e=>`${t}${e}`)).join("|"))).join("|")),o=r.readdirSync(process.cwd()).filter((e=>t.test(e))).filter((e=>!n.test(e)));return console.log(o),o},recordMetadata:d,spawnShell:p,transcribeVideo:async function(e,t){const{video:n}=e,o=`Could not extract subtitles from ${n}.\nStarting Docker with AI transcription.\nThis will take a few minutes to transcribe video.\n\n`;t.write(o),console.log("",o),console.log("","");const i=["run","--rm","-v",`${process.cwd()}:/data`,"-v",`${process.cwd()}/.whisper:/app/.whisper`,"jveldboom/video-swear-jar:v1","clean","--input",n,"--model","tiny.en","--language","en"],r=await p("docker",i,t);t.write(`transcribeVideo:\nstdout: ${r}\n\n`)},zipVideo:async function(e,t){const{video:n,subName:o,name:i}=e,r=await p("7z",["a","-t7z",`${i}.7z`,n,o],t);t.write(`zip:\nstdout: ${r}\n\n`)}}},421:e=>{"use strict";e.exports=require("node:child_process")},540:e=>{"use strict";e.exports=require("node:console")},24:e=>{"use strict";e.exports=require("node:fs")},937:e=>{"use strict";e.exports=JSON.parse('["arse","ass","ass.?h\\\\w*","ass.?f\\\\w*","bitch\\\\w*","bullshit\\\\w*","blow.?job","chicken.?shit\\\\w*","cock","cocksucker.*","cum (?!laude)","cumming","cumshot.?","cunnilingus","cunt","(?{const e=o(24),{addSwearWords:t,convertMsToTime:n,deleteFiles:i,extractSubtitle:r,filterGraphAndEncode:a,getArgs:s,getCuts:c,getName:d,getMetadata:u,getVideoNames:l,recordMetadata:m,transcribeVideo:p,zipVideo:w}=o(413),$=o(937),f=s(),g=l();for(const o of g){const s=(new Date).getTime();console.log(o);const{name:l,ext:b}=d(o),h=`${l}.log`,S=e.createWriteStream(h);f.clean=!0===f?.zip;try{S.write(`Video Names ------------------------------------\n${g.join("\n")}\n\n`);const d=await u(o,f,S),v={args:f,cleanSubName:`${l}-clean.srt`,cleanVideoName:`${l}-clean.mp4`,ext:b,name:l,logName:h,subName:`${l}.srt`,swearWordString:t($,f?.add?.split(" ")??[]),ignoreWords:f?.ignore?.split(" ")??[],transcribeVideo:!1,video:o,videoMeta:d};if(S.write(`State -------------------------------------------\n${JSON.stringify(v,null,2)}\n\n`),await m(v.video,S),!f?.skip)if(e.existsSync(v.subName))S.write(`Subtitle ${v.subName} found!\n\n`);else if(!await r(v,S)){v.args.skip=!0;const e=await p(v,S);if(console.log("out",e),/No swear words found/.test(e?.stdout||"")){console.log("","Transcription done! No swear words found."),console.log("",""),S.write("Transcription done! No swear words found.\n\n");const e=(new Date).getTime();S.write(`Time to Complete ${o}: ${n(e-s)}\n\n`),S.end();continue}v.video=`${v.name}-output.${v.ext}`,await a(v,S);const t=[`${l}-cut.txt`,`${l}.json`,v.video,`${l}-cut-words.txt`];f.debug||i(t,S);const r=(new Date).getTime();S.write(`Time to Complete ${o}: ${n(r-s)}\n\n`),S.end();continue}const N=f?.skip||f?.copy?{keeps:[],blurs:[]}:await c(v,S);S.write(`TimeStamps:\n${JSON.stringify(N)}\n\n`),await a(v,S,N),f?.zip&&await w(v,S);const x=[v.cleanSubName];f?.clean&&x.push(v.video,v.subName,"clean.txt"),f?.["clean-all"]&&x.push(v.video,v.subName,v.logName),f?.debug||i(x,S);const y=(new Date).getTime();S.write(`Time to Complete ${o}: ${n(y-s)}\n\n`),S.end()}catch(e){S.end(),S.on("close",(()=>{throw new Error(e)}))}}})()})();