You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
const{mkdirSync:mkdirSync}=require("fs"),chalk=require("chalk"),path=require("path");let ffmpegGlobalProcess=null;function decodeUnicodeEscape(e){return e.replace(/\\u([\d\w]{4})/gi,((e,t)=>String.fromCharCode(parseInt(t,16))))}const videoProcessing=async({liveUrl:e,rtmpServer:t=null,rtmpKey:o=null,isInfiniteMode:r=!1,streamDuration:s=null,baseFilePath:a="",mode:i="stream"})=>{console.log(chalk.blue("Starting the streaming process. Please wait..."));const n=require("child_process"),{existsSync:l}=require("fs");let c,d=null;"restream"===i?t&&o?(c=`${t}${o}`,d=`${a}.flv`):c=`${a}.flv`:c=`${t}${o}`;const generateUniqueFileName=(e,t)=>{let o=0,r=`${e}${o>0?`-${o}`:""}.${t}`;for(;l(r);)o++,r=`${e}${o>0?`-${o}`:""}.${t}`;return r};if(d&&l(d)&&(d=generateUniqueFileName(a,"flv")),l(c)){const e=c.split(".").pop();c=generateUniqueFileName(a,e)}process.stdout.clearLine(0),process.stdout.cursorTo(0);const p=[chalk.white("Live Url : ")+chalk.red(`${e}`),chalk.white("Output Destination : ")+chalk.yellow(`${c}`),chalk.white("Secondary Output : ")+chalk.green(`${d||"Not Applicable"}`)].join("\n"),m=Math.max(0,p.length-chalk.reset(p).length+20),u=chalk.blue("=".repeat(m));process.stdout.write(`${u}\n${p}\n${u}`);const h=["-re","-stream_loop","-1","-i",e,"-r","30","-b:v","2000k","-c:v","libx264","-preset","veryfast","-c:a","aac","-f","flv","-loglevel","info","-hide_banner",c];d&&h.push(d),s&&h.push("-t",s.toString()),ffmpegGlobalProcess=n.spawn("ffmpeg",h);let f=0,g=0,S=0;ffmpegGlobalProcess.stderr.on("data",(e=>{const t=e.toString(),o=t.match(/Duration: (\d{2}):(\d{2}):(\d{2})\.\d{2},/),r=t.match(/time=(\d{2}):(\d{2}):(\d{2})\.\d{2}/);if(o&&(g=3600*parseInt(o[1])+60*parseInt(o[2])+parseInt(o[3])),r){S=3600*parseInt(r[1])+60*parseInt(r[2])+parseInt(r[3]);const e=(S/g*100).toFixed(2);s&&S>=s&&ffmpegGlobalProcess.kill("SIGINT"),process.stdout.clearLine(0),process.stdout.cursorTo(0),process.stdout.write(chalk.green(`Streaming Progress: [${e}%] | Time Elapsed: ${S} seconds`))}t.includes("error")&&(f+=1,f>5&&(console.log(chalk.red("We encountered a problem and need to stop. Please try again.")),ffmpegGlobalProcess.kill("SIGINT")))})),ffmpegGlobalProcess.on("close",((e,n)=>{const l=`Streaming process exited with code: ${e} and signal: ${n}`;0!==e?(console.log(chalk.magenta(l)),console.log(chalk.yellow("The streaming process has ended unexpectedly. Attempting to restart..."))):console.log(chalk.green("Streaming completed successfully.")),(r||f<=5&&"restream"===i&&t&&o)&&(console.log(chalk.blue("Restarting in 3 seconds...")),setTimeout((()=>{try{videoProcessing({liveUrl:d,rtmpServer:t,rtmpKey:o,isInfiniteMode:r,streamDuration:s,baseFilePath:a,mode:i})}catch(e){console.error(chalk.red(`Error restarting streaming process: ${e.message}`))}}),3e3))})),process.on("SIGINT",(()=>{console.log(chalk.magenta("Streaming has been stopped. Thank you for using our service.")),ffmpegGlobalProcess&&ffmpegGlobalProcess.kill("SIGINT"),process.exit()}))},reStreamShopee=async({videoUrl:e,rtmpServer:t=null,rtmpKey:o=null,isInfiniteMode:r=!1,streamDuration:s=null})=>new Promise((async(a,i)=>{try{let a=null,i=`${__dirname}/../${e}`,n="stream";if(!e.includes("mp4")&&!e.includes("flv")){if(a=await getShopeeStreamDetails(e),!a)throw new Error("Stream details could not be retrieved.");i=decodeURIComponent(a.play_url),n="restream"}const l=`${__dirname}/../stream_output/${a?.room_id}-${a?.username}`;console.info("\nCtrl+C to stop downloading and exit"),await videoProcessing({liveUrl:i,rtmpServer:t,rtmpKey:o,isInfiniteMode:r,streamDuration:s,baseFilePath:l,mode:n})}catch(e){i(e),console.error("Error in reStreamShopee function: ",e)}})),streamDownloader=async({videoUrl:e,durasiVideo:t=null,rtmpServer:o=null,rtmpKey:r=null,isInfinite:s=!1})=>{try{const a=getStreamProvider(e);let i=null;switch(a){case"shopee":case"filestream":i=await reStreamShopee({videoUrl:e,streamDuration:t?60*t:null,rtmpServer:o,rtmpKey:r,isInfiniteMode:s});break;case"tiktok":i=await tiktokDownload({videoUrl:e,duration:t?60*parseInt(t):null,rtmpServer:o,rtmpKey:r,isInfiniteMode:s});break;default:throw Error(`Platform "${a}" Not Supported Yet!`)}return i}catch(e){throw Error(e)}},getStreamProvider=e=>{let t="";if(e.includes("http")){t=new URL(e).hostname}else t="filestream";return t.includes("tiktok.com")?"tiktok":t.includes("youtube.com")?"youtube":t.includes("twitch.tv")?"twitch":t.includes("facebook.com")?"facebook":t.includes("tokopedia.com")?"tokopedia":t.includes("shopee.co.id")?"shopee":t.includes("file")?"filestream":t},getShopeeStreamDetails=async e=>{const t=new URL(e).searchParams.get("session");return fetch(`https://live.shopee.co.id/api/v1/session/${t}`,{headers:{Host:"live.shopee.co.id","Sec-Ch-Ua":'"Brave";v="119", "Chromium";v="119", "Not?A_Brand";v="24"',"Sec-Ch-Ua-Mobile":"?0","User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36","X-Api-Source":"pc","Content-Type":"application/json",Accept:"application/json","X-Shopee-Language":"id","X-Requested-With":"XMLHttpRequest","Sec-Ch-Ua-Platform":'"macOS"',"Sec-Gpc":"1","Accept-Language":"id-ID,id;q=0.6","Sec-Fetch-Site":"same-origin","Sec-Fetch-Mode":"cors","Sec-Fetch-Dest":"empty",Referer:"https://shopee.co.id/?is_from_login=true&is_from_login=true","Accept-Encoding":"gzip, deflate, br"}}).then((async e=>{const t=await e.json();return 0==t.err_code?t?.data?.session:t}))},tiktokStreamData=async e=>{const t=`https://www.tiktok.com/@${e}/live`,o=(await fetch(t,{headers:{"User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36"}}).then((e=>e.text()))).match(/room_id=(\d+)/);if(!o)throw new Error("No live stream found");const r=o[1];console.info(`\nFound live stream with room id ${r}!`);const s=`https://www.tiktok.com/api/live/detail/?aid=1988&roomID=${r}`,{LiveRoomInfo:a}=await fetch(s).then((e=>e.json())),{title:i,liveUrl:n}=a;return{title:i,liveUrl:n}},tiktokDownload=async({videoUrl:e,output:t="stream_output",format:o="mp4",duration:r=0,rtmpKey:s=null,rtmpServer:a=null,isInfiniteMode:i=!1})=>new Promise((async(n,l)=>{try{const n=e.split("/"),l=n[n.length-2].replace("@",""),{title:c,liveUrl:d}=await tiktokStreamData(l),p=t.endsWith(o)?t:`${t.replace(/\/$/,"")}/${l}-${Date.now()}.${o}`;mkdirSync(path.dirname(p),{recursive:!0}),console.info("\nCtrl+C to stop downloading and exit"),await videoProcessing({liveUrl:d,rtmpServer:a,rtmpKey:s,isInfiniteMode:i,streamDuration:r,baseFilePath:p,mode:"restream"})}catch(e){l(e)}}));module.exports={ffmpegGlobalProcess:ffmpegGlobalProcess,reStreamShopee:reStreamShopee,getShopeeStreamDetails:getShopeeStreamDetails,decodeUnicodeEscape:decodeUnicodeEscape,streamDownloader:streamDownloader};
0 commit comments