@pieda/video-dl 1.10.2 → 1.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,6 +5,7 @@ type TUseInstagram = {
5
5
  dest: string;
6
6
  tempDir: string;
7
7
  ffmpegConfig: {
8
+ ffmpegDir: string;
8
9
  ffmpegPath: string;
9
10
  ffmpegProbePath: string;
10
11
  ffmpegPlayPath: string;
@@ -5,6 +5,7 @@ type TUseVimeo = {
5
5
  dest: string;
6
6
  tempDir: string;
7
7
  ffmpegConfig: {
8
+ ffmpegDir: string;
8
9
  ffmpegPath: string;
9
10
  ffmpegProbePath: string;
10
11
  ffmpegPlayPath: string;
@@ -5,6 +5,7 @@ type TUseYoutube = {
5
5
  debug?: boolean;
6
6
  tempDir: string;
7
7
  ffmpegConfig: {
8
+ ffmpegDir: string;
8
9
  ffmpegPath: string;
9
10
  ffmpegProbePath: string;
10
11
  ffmpegPlayPath: string;
@@ -1,14 +1,14 @@
1
1
  type TUseYtdlp = {
2
2
  debug?: boolean;
3
- ytdlpPath: string;
4
- videoUrl: string;
3
+ command: string;
5
4
  dest: string;
6
5
  tempDir: string;
7
6
  ffmpegConfig: {
7
+ ffmpegDir: string;
8
8
  ffmpegPath: string;
9
9
  ffmpegProbePath: string;
10
10
  ffmpegPlayPath: string;
11
11
  };
12
12
  };
13
- export declare const useYtdlp: ({ debug, ytdlpPath, videoUrl, dest, tempDir, ffmpegConfig, }: TUseYtdlp) => Promise<[Error | null, boolean | null]>;
13
+ export declare const useYtdlp: ({ debug, command, dest, tempDir, ffmpegConfig, }: TUseYtdlp) => Promise<[Error | null, boolean | null]>;
14
14
  export {};
package/dist/video-dl.cjs CHANGED
@@ -1 +1,5 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const h=(e=!1)=>({log:(...o)=>{e&&console.log(...o)},error:(...o)=>{e&&console.error(...o)}});var s=[];for(var S=0;S<256;++S)s.push((S+256).toString(16).slice(1));function E(e,o=0){return(s[e[o+0]]+s[e[o+1]]+s[e[o+2]]+s[e[o+3]]+"-"+s[e[o+4]]+s[e[o+5]]+"-"+s[e[o+6]]+s[e[o+7]]+"-"+s[e[o+8]]+s[e[o+9]]+"-"+s[e[o+10]]+s[e[o+11]]+s[e[o+12]]+s[e[o+13]]+s[e[o+14]]+s[e[o+15]]).toLowerCase()}var $,I=new Uint8Array(16);function K(){if(!$&&($=typeof crypto<"u"&&crypto.getRandomValues&&crypto.getRandomValues.bind(crypto),!$))throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");return $(I)}var _=typeof crypto<"u"&&crypto.randomUUID&&crypto.randomUUID.bind(crypto);const D={randomUUID:_};function C(e,o,n){if(D.randomUUID&&!o&&!e)return D.randomUUID();e=e||{};var r=e.random||(e.rng||K)();if(r[6]=r[6]&15|64,r[8]=r[8]&63|128,o){n=n||0;for(var t=0;t<16;++t)o[n+t]=r[t];return o}return E(r)}const w=e=>new Promise((o,n)=>{import(e).then(r=>o(r.default)).catch(n)}),P=async(e,o,n)=>{const r=await w("fs"),t=await w("axios");return console.log(`Downloading ${e} to ${o}`),new Promise((c,i)=>{t.get(e,{responseType:"stream",headers:n}).then(a=>{const u=a.data,g=r.createWriteStream(o);u.pipe(g),g.on("finish",function(){g.close(()=>{c()})})}).catch(i)})},U=async({debug:e=!1,ytdlpPath:o,videoUrl:n,dest:r,tempDir:t,ffmpegConfig:c})=>{const i=h(e);i.log(`Downloading video: ${n} to: ${r}`);const a=await w("fs"),u=await w("path"),{exec:g,execSync:y}=await w("child_process"),l=u.join(t,C()),p=()=>new Promise((d,b)=>{const m=`${o} -f mergeall --audio-multistreams --video-multistreams ${n} --downloader ffmpeg -o "${l}/%(id)s.%(ext)s" --merge-output-format mp4`;i.log(`[yt-dlp] 執行命令: ${m}`),g(m,(v,f,k)=>{if(i.log(`[yt-dlp] stdout: ${f}`),v){b(v);return}d()})});return a.existsSync(u.dirname(r))||(a.mkdirSync(u.dirname(r),{recursive:!0},777),i.log(`建立 ${u.dirname(r)}`)),new Promise(async(d,b)=>{try{await p();let m=!1;const v=a.readdirSync(l);for(const f of v){const k=u.join(l,f),j=`${c.ffmpegProbePath} -v error -show_entries stream=codec_type -of csv=p=0 "${k}"`,x=y(j).toString("utf-8"),R=x.includes("video"),W=x.includes("audio");if(R&&W){m=!0,a.copyFileSync(k,r);break}}if(!m)throw new Error("無法下載影音正常的影片");i.log(`下載完成 ${r}`),d([null,!0])}catch(m){i.error(`下載失敗: ${m.message}`),d([m,null])}})},A=async({debug:e=!1,ytdlpPath:o,videoId:n,dest:r,tempDir:t,ffmpegConfig:c})=>{h(e).log(`Downloading video: ${n} to: ${r}`);const a=`https://www.youtube.com/watch?v=${n}`;return U({debug:e,ytdlpPath:o,videoUrl:a,dest:r,tempDir:t,ffmpegConfig:c})},F=async({videoId:e,dest:o,debug:n=!1,headless:r=!0})=>{try{const t=h(n);t.log(`Downloading video: ${e} to: ${o}`);const c=`https://m.facebook.com/watch/?v=${e}`,i=await w("fs"),a=await w("path"),u=await w("puppeteer");i.existsSync(a.dirname(o))||(i.mkdirSync(a.dirname(o),{recursive:!0},777),t.log(`建立 ${a.dirname(o)}`));const g={};r||(g.headless=!1);const y=await u.launch(g);t.log("啟動 puppeteer");const l=await y.newPage();await l.setUserAgent("'Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Mobile/15E148 Safari/604.1'"),t.log("開新分頁"),t.log(`前往 ${c}`),await Promise.race([l.goto(c,{waitUntil:"networkidle0"}),l.waitForSelector("video")]),t.log("找到 video 元素");const p=await l.$eval("video",d=>d.getAttribute("src"));return t.log(`取得 video url: ${p}`),await y.close(),t.log("關閉 puppeteer"),await P(p,o),t.log(`下載完成 ${p}`),[null,!0]}catch(t){return[t,null]}},M=async({debug:e=!1,ytdlpPath:o,videoId:n,dest:r,tempDir:t,ffmpegConfig:c})=>{h(e).log(`Downloading video: ${n} to: ${r}`);const a=`https://vimeo.com/${n}`;return U({debug:e,ytdlpPath:o,videoUrl:a,dest:r,tempDir:t,ffmpegConfig:c})},T=async({debug:e=!1,ytdlpPath:o,videoId:n,dest:r,tempDir:t,ffmpegConfig:c})=>{h(e).log(`Downloading video: ${n} to: ${r}`);const a=`https://www.instagram.com/reel/${n}/`;return U({debug:e,ytdlpPath:o,videoUrl:a,dest:r,tempDir:t,ffmpegConfig:c})},V=async({videoLink:e,dest:o,debug:n=!1,headless:r=!0})=>{try{const t=h(n);t.log(`Downloading video: ${e} to: ${o}`);const c=e,i=await w("fs"),a=await w("path"),u=await w("puppeteer");i.existsSync(a.dirname(o))||(i.mkdirSync(a.dirname(o),{recursive:!0},777),t.log(`建立 ${a.dirname(o)}`));const g={};r||(g.headless=!1);const y=await u.launch(g);t.log("啟動 puppeteer");const l=await y.newPage();t.log("開新分頁"),t.log(`前往 ${c}`),await Promise.race([l.goto(c,{waitUntil:"networkidle0"}),l.waitForSelector("video")]),t.log("找到 video 元素");const p=await l.$eval('video source[src^="https://www.tiktok.com/aweme/v1/play/"]',d=>d.getAttribute("src"));return t.log(`取得 video url: ${p}`),await y.close(),t.log("關閉 puppeteer"),await P(p,o,{"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",Referer:"https://www.tiktok.com/"}),t.log(`下載完成 ${p}`),[null,!0]}catch(t){return[t,null]}},H=({type:e})=>{switch(e){case"youtube":case"yt-short":return A;case"facebook":case"fb-reel":return F;case"vimeo":return M;case"instagram":case"ig":case"ig-reel":return T;case"tiktok":case"tiktok-reel":return V;default:throw new Error(`Invalid video type: ${e}`)}};exports.useDownloader=H;exports.useFacebook=F;exports.useInstagram=T;exports.useTiktok=V;exports.useVimeo=M;exports.useYoutube=A;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const v=(t=!1)=>({log:(...o)=>{t&&console.log(...o)},error:(...o)=>{t&&console.error(...o)}}),u=t=>new Promise((o,a)=>{import(t).then(i=>o(i.default)).catch(a)}),P=async(t,o,a)=>{const i=await u("fs"),e=await u("axios");return console.log(`Downloading ${t} to ${o}`),new Promise((n,c)=>{e.get(t,{responseType:"stream",headers:a}).then(s=>{const l=s.data,g=i.createWriteStream(o);l.pipe(g),g.on("finish",function(){g.close(()=>{n()})})}).catch(c)})},x=async({debug:t=!1,command:o,dest:a,tempDir:i,ffmpegConfig:e})=>{const n=v(t),c=await u("fs"),s=await u("path"),{exec:l,execSync:g}=await u("child_process"),d=()=>new Promise((r,p)=>{n.log(`[yt-dlp] 執行命令: ${o}`),l(o,(w,b,f)=>{if(n.log(`[yt-dlp] stdout: ${b}`),w){p(w);return}r()})});return c.existsSync(s.dirname(a))||(c.mkdirSync(s.dirname(a),{recursive:!0},777),n.log(`建立 ${s.dirname(a)}`)),new Promise(async(r,p)=>{try{await d();let w=!1;const b=c.readdirSync(i);for(const f of b){const h=s.join(i,f),y=`${e.ffmpegProbePath} -v error -show_entries stream=codec_type -of csv=p=0 "${h}"`,m=g(y).toString("utf-8"),$=m.includes("video"),S=m.includes("audio");if($&&S){w=!0,c.copyFileSync(h,a);break}}if(!w)throw new Error("無法下載影音正常的影片");n.log(`下載完成 ${a}`),r([null,!0])}catch(w){n.error(`下載失敗: ${w.message}`),r([w,null])}})},T=async({debug:t=!1,ytdlpPath:o,videoId:a,dest:i,tempDir:e,ffmpegConfig:n})=>{const c=v(t);c.log(`Downloading video: ${a} to: ${i}`);const s=await u("fs"),l=await u("path"),g=await u("puppeteer"),d=`https://www.youtube.com/watch?v=${a}`,r=l.join(l.dirname(o),"cookies.txt"),p=(f=[])=>{const h=["# Netscape HTTP Cookie File","# https://curl.se/docs/http-cookies.html","# This file was generated by Puppeteer",""].join(`
2
+ `),y=f.map(m=>{const $=m.domain.startsWith(".")?m.domain:`.${m.domain}`,S="TRUE",k=m.path||"/",A=m.secure?"TRUE":"FALSE",M=m.expires?Math.floor(m.expires):Math.floor(Date.now()/1e3)+3600*24*365,E=m.name,_=m.value;return[$,S,k,A,M,E,_].join(" ")});return h+`
3
+ `+y.join(`
4
+ `)+`
5
+ `};await(async()=>{const f=l.join(l.dirname(o),"chrome-profile");if(s.existsSync(f)||s.mkdirSync(f,{recursive:!0},777),s.existsSync(r)&&s.statSync(r).mtimeMs+6e5>Date.now()){c.log("Using existing cookies.txt");return}console.log("Launching Chrome…");const h=await g.launch({headless:!1,userDataDir:f,defaultViewport:null,args:["--disable-blink-features=AutomationControlled"]}),y=await h.newPage();await y.goto("https://www.youtube.com",{waitUntil:"networkidle2"}),console.log("If not logged in, please log in manually."),console.log("Waiting 30 seconds…"),await new Promise(k=>setTimeout(k,3e4));const $=(await y.cookies()).filter(k=>k.domain.includes("youtube.com")||k.domain.includes("google.com"));$.length===0&&(console.error("No YouTube cookies found. Are you logged in?"),process.exit(1));const S=p($);s.writeFileSync(r,S,{encoding:"utf8"}),console.log(`cookies.txt written to ${r}`),console.log(`Cookies count: ${$.length}`),await h.close()})();const b=[`${o}`,'-f "bv*[vcodec^=avc1][acodec^=mp4a][height<=1080]/ba/b"',"--js-runtimes node",`${d}`,`-o "${e}/%(id)s.%(ext)s"`,`--cookies ${r}`,n.ffmpegDir?`--ffmpeg-location "${n.ffmpegDir}"`:""];return x({debug:t,command:b.join(" "),dest:i,tempDir:e,ffmpegConfig:n})},U=async({videoId:t,dest:o,debug:a=!1,headless:i=!0})=>{try{const e=v(a);e.log(`Downloading video: ${t} to: ${o}`);const n=`https://m.facebook.com/watch/?v=${t}`,c=await u("fs"),s=await u("path"),l=await u("puppeteer");c.existsSync(s.dirname(o))||(c.mkdirSync(s.dirname(o),{recursive:!0},777),e.log(`建立 ${s.dirname(o)}`));const g={};i||(g.headless=!1);const d=await l.launch(g);e.log("啟動 puppeteer");const r=await d.newPage();await r.setUserAgent("'Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Mobile/15E148 Safari/604.1'"),e.log("開新分頁"),e.log(`前往 ${n}`),await r.goto(n,{waitUntil:"networkidle2"}),await r.waitForSelector("video"),e.log("找到 video 元素");const p=await r.$eval("video",w=>w.getAttribute("src"));return e.log(`取得 video url: ${p}`),await d.close(),e.log("關閉 puppeteer"),await P(p,o),e.log(`下載完成 ${p}`),[null,!0]}catch(e){return[e,null]}},j=async({debug:t=!1,ytdlpPath:o,videoId:a,dest:i,tempDir:e,ffmpegConfig:n})=>{v(t).log(`Downloading video: ${a} to: ${i}`);const s=`https://vimeo.com/${a}`,l=[`${o}`,'-f "bv*[vcodec^=avc1][acodec^=mp4a][height<=1080]/ba/b"',"--js-runtimes node",`${s}`,`-o "${e}/%(id)s.%(ext)s"`];return x({debug:t,command:l.join(" "),dest:i,tempDir:e,ffmpegConfig:n})},D=async({debug:t=!1,ytdlpPath:o,videoId:a,dest:i,tempDir:e,ffmpegConfig:n})=>{v(t).log(`Downloading video: ${a} to: ${i}`);const s=`https://www.instagram.com/reel/${a}/`,l=[`${o}`,"-f mergeall","--audio-multistreams","--video-multistreams","--js-runtimes node",`${s}`,"--downloader ffmpeg",`-o "${e}/%(id)s.%(ext)s"`,"--merge-output-format mp4",n.ffmpegDir?`--ffmpeg-location "${n.ffmpegDir}"`:""];return x({debug:t,command:l.join(" "),dest:i,tempDir:e,ffmpegConfig:n})},F=async({videoLink:t,dest:o,debug:a=!1,headless:i=!0})=>{try{const e=v(a);e.log(`Downloading video: ${t} to: ${o}`);const n=t,c=await u("fs"),s=await u("path"),l=await u("puppeteer");c.existsSync(s.dirname(o))||(c.mkdirSync(s.dirname(o),{recursive:!0},777),e.log(`建立 ${s.dirname(o)}`));const g={};i||(g.headless=!1);const d=await l.launch(g);e.log("啟動 puppeteer");const r=await d.newPage();e.log("開新分頁"),e.log(`前往 ${n}`),await r.goto(n,{waitUntil:"networkidle2"}),await r.waitForSelector("video"),e.log("找到 video 元素");const p=await r.$eval('video source[src^="https://www.tiktok.com/aweme/v1/play/"]',w=>w.getAttribute("src"));return e.log(`取得 video url: ${p}`),await d.close(),e.log("關閉 puppeteer"),await P(p,o,{"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",Referer:"https://www.tiktok.com/"}),e.log(`下載完成 ${p}`),[null,!0]}catch(e){return[e,null]}},W=({type:t})=>{switch(t){case"youtube":case"yt-short":return T;case"facebook":case"fb-reel":return U;case"vimeo":return j;case"instagram":case"ig":case"ig-reel":return D;case"tiktok":case"tiktok-reel":return F;default:throw new Error(`Invalid video type: ${t}`)}};exports.useDownloader=W;exports.useFacebook=U;exports.useInstagram=D;exports.useTiktok=F;exports.useVimeo=j;exports.useYoutube=T;
package/dist/video-dl.js CHANGED
@@ -1,239 +1,261 @@
1
- const h = (e = !1) => ({
1
+ const v = (t = !1) => ({
2
2
  log: (...o) => {
3
- e && console.log(...o);
3
+ t && console.log(...o);
4
4
  },
5
5
  error: (...o) => {
6
- e && console.error(...o);
6
+ t && console.error(...o);
7
7
  }
8
- });
9
- var c = [];
10
- for (var U = 0; U < 256; ++U)
11
- c.push((U + 256).toString(16).slice(1));
12
- function V(e, o = 0) {
13
- return (c[e[o + 0]] + c[e[o + 1]] + c[e[o + 2]] + c[e[o + 3]] + "-" + c[e[o + 4]] + c[e[o + 5]] + "-" + c[e[o + 6]] + c[e[o + 7]] + "-" + c[e[o + 8]] + c[e[o + 9]] + "-" + c[e[o + 10]] + c[e[o + 11]] + c[e[o + 12]] + c[e[o + 13]] + c[e[o + 14]] + c[e[o + 15]]).toLowerCase();
14
- }
15
- var v, T = new Uint8Array(16);
16
- function R() {
17
- if (!v && (v = typeof crypto < "u" && crypto.getRandomValues && crypto.getRandomValues.bind(crypto), !v))
18
- throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");
19
- return v(T);
20
- }
21
- var W = typeof crypto < "u" && crypto.randomUUID && crypto.randomUUID.bind(crypto);
22
- const P = {
23
- randomUUID: W
24
- };
25
- function j(e, o, n) {
26
- if (P.randomUUID && !o && !e)
27
- return P.randomUUID();
28
- e = e || {};
29
- var r = e.random || (e.rng || R)();
30
- if (r[6] = r[6] & 15 | 64, r[8] = r[8] & 63 | 128, o) {
31
- n = n || 0;
32
- for (var t = 0; t < 16; ++t)
33
- o[n + t] = r[t];
34
- return o;
35
- }
36
- return V(r);
37
- }
38
- const w = (e) => new Promise((o, n) => {
39
- import(e).then((r) => o(r.default)).catch(n);
40
- }), b = async (e, o, n) => {
41
- const r = await w("fs"), t = await w("axios");
42
- return console.log(`Downloading ${e} to ${o}`), new Promise((s, i) => {
43
- t.get(e, {
8
+ }), u = (t) => new Promise((o, a) => {
9
+ import(t).then((i) => o(i.default)).catch(a);
10
+ }), U = async (t, o, a) => {
11
+ const i = await u("fs"), e = await u("axios");
12
+ return console.log(`Downloading ${t} to ${o}`), new Promise((s, c) => {
13
+ e.get(t, {
44
14
  responseType: "stream",
45
- headers: n
46
- }).then((a) => {
47
- const u = a.data, g = r.createWriteStream(o);
48
- u.pipe(g), g.on("finish", function() {
15
+ headers: a
16
+ }).then((n) => {
17
+ const l = n.data, g = i.createWriteStream(o);
18
+ l.pipe(g), g.on("finish", function() {
49
19
  g.close(() => {
50
20
  s();
51
21
  });
52
22
  });
53
- }).catch(i);
23
+ }).catch(c);
54
24
  });
55
- }, S = async ({
56
- debug: e = !1,
57
- ytdlpPath: o,
58
- videoUrl: n,
59
- dest: r,
60
- tempDir: t,
61
- ffmpegConfig: s
25
+ }, x = async ({
26
+ debug: t = !1,
27
+ command: o,
28
+ dest: a,
29
+ tempDir: i,
30
+ ffmpegConfig: e
62
31
  }) => {
63
- const i = h(e);
64
- i.log(`Downloading video: ${n} to: ${r}`);
65
- const a = await w("fs"), u = await w("path"), { exec: g, execSync: y } = await w("child_process"), l = u.join(t, j()), p = () => new Promise((d, x) => {
66
- const m = `${o} -f mergeall --audio-multistreams --video-multistreams ${n} --downloader ffmpeg -o "${l}/%(id)s.%(ext)s" --merge-output-format mp4`;
67
- i.log(`[yt-dlp] 執行命令: ${m}`), g(m, ($, f, k) => {
68
- if (i.log(`[yt-dlp] stdout: ${f}`), $) {
69
- x($);
32
+ const s = v(t), c = await u("fs"), n = await u("path"), { exec: l, execSync: g } = await u("child_process"), d = () => new Promise((r, m) => {
33
+ s.log(`[yt-dlp] 執行命令: ${o}`), l(o, (w, b, f) => {
34
+ if (s.log(`[yt-dlp] stdout: ${b}`), w) {
35
+ m(w);
70
36
  return;
71
37
  }
72
- d();
38
+ r();
73
39
  });
74
40
  });
75
- return a.existsSync(u.dirname(r)) || (a.mkdirSync(u.dirname(r), { recursive: !0 }, 777), i.log(`建立 ${u.dirname(r)}`)), new Promise(async (d, x) => {
41
+ return c.existsSync(n.dirname(a)) || (c.mkdirSync(n.dirname(a), { recursive: !0 }, 777), s.log(`建立 ${n.dirname(a)}`)), new Promise(async (r, m) => {
76
42
  try {
77
- await p();
78
- let m = !1;
79
- const $ = a.readdirSync(l);
80
- for (const f of $) {
81
- const k = u.join(l, f), A = `${s.ffmpegProbePath} -v error -show_entries stream=codec_type -of csv=p=0 "${k}"`, D = y(A).toString("utf-8"), F = D.includes("video"), M = D.includes("audio");
82
- if (F && M) {
83
- m = !0, a.copyFileSync(k, r);
43
+ await d();
44
+ let w = !1;
45
+ const b = c.readdirSync(i);
46
+ for (const f of b) {
47
+ const h = n.join(i, f), y = `${e.ffmpegProbePath} -v error -show_entries stream=codec_type -of csv=p=0 "${h}"`, p = g(y).toString("utf-8"), $ = p.includes("video"), S = p.includes("audio");
48
+ if ($ && S) {
49
+ w = !0, c.copyFileSync(h, a);
84
50
  break;
85
51
  }
86
52
  }
87
- if (!m)
53
+ if (!w)
88
54
  throw new Error("無法下載影音正常的影片");
89
- i.log(`下載完成 ${r}`), d([null, !0]);
90
- } catch (m) {
91
- i.error(`下載失敗: ${m.message}`), d([m, null]);
55
+ s.log(`下載完成 ${a}`), r([null, !0]);
56
+ } catch (w) {
57
+ s.error(`下載失敗: ${w.message}`), r([w, null]);
92
58
  }
93
59
  });
94
- }, E = async ({
95
- debug: e = !1,
60
+ }, D = async ({
61
+ debug: t = !1,
96
62
  ytdlpPath: o,
97
- videoId: n,
98
- dest: r,
99
- tempDir: t,
63
+ videoId: a,
64
+ dest: i,
65
+ tempDir: e,
100
66
  ffmpegConfig: s
101
67
  }) => {
102
- h(e).log(`Downloading video: ${n} to: ${r}`);
103
- const a = `https://www.youtube.com/watch?v=${n}`;
104
- return S({
105
- debug: e,
106
- ytdlpPath: o,
107
- videoUrl: a,
108
- dest: r,
109
- tempDir: t,
68
+ const c = v(t);
69
+ c.log(`Downloading video: ${a} to: ${i}`);
70
+ const n = await u("fs"), l = await u("path"), g = await u("puppeteer"), d = `https://www.youtube.com/watch?v=${a}`, r = l.join(l.dirname(o), "cookies.txt"), m = (f = []) => {
71
+ const h = [
72
+ "# Netscape HTTP Cookie File",
73
+ "# https://curl.se/docs/http-cookies.html",
74
+ "# This file was generated by Puppeteer",
75
+ ""
76
+ ].join(`
77
+ `), y = f.map((p) => {
78
+ const $ = p.domain.startsWith(".") ? p.domain : `.${p.domain}`, S = "TRUE", k = p.path || "/", A = p.secure ? "TRUE" : "FALSE", P = p.expires ? Math.floor(p.expires) : Math.floor(Date.now() / 1e3) + 3600 * 24 * 365, T = p.name, j = p.value;
79
+ return [$, S, k, A, P, T, j].join(" ");
80
+ });
81
+ return h + `
82
+ ` + y.join(`
83
+ `) + `
84
+ `;
85
+ };
86
+ await (async () => {
87
+ const f = l.join(l.dirname(o), "chrome-profile");
88
+ if (n.existsSync(f) || n.mkdirSync(f, { recursive: !0 }, 777), n.existsSync(r) && n.statSync(r).mtimeMs + 6e5 > Date.now()) {
89
+ c.log("Using existing cookies.txt");
90
+ return;
91
+ }
92
+ console.log("Launching Chrome…");
93
+ const h = await g.launch({
94
+ headless: !1,
95
+ // 必須 false
96
+ userDataDir: f,
97
+ // 關鍵
98
+ defaultViewport: null,
99
+ args: ["--disable-blink-features=AutomationControlled"]
100
+ }), y = await h.newPage();
101
+ await y.goto("https://www.youtube.com", {
102
+ waitUntil: "networkidle2"
103
+ }), console.log("If not logged in, please log in manually."), console.log("Waiting 30 seconds…"), await new Promise((k) => setTimeout(k, 3e4));
104
+ const $ = (await y.cookies()).filter((k) => k.domain.includes("youtube.com") || k.domain.includes("google.com"));
105
+ $.length === 0 && (console.error("No YouTube cookies found. Are you logged in?"), process.exit(1));
106
+ const S = m($);
107
+ n.writeFileSync(r, S, { encoding: "utf8" }), console.log(`cookies.txt written to ${r}`), console.log(`Cookies count: ${$.length}`), await h.close();
108
+ })();
109
+ const b = [
110
+ `${o}`,
111
+ '-f "bv*[vcodec^=avc1][acodec^=mp4a][height<=1080]/ba/b"',
112
+ "--js-runtimes node",
113
+ `${d}`,
114
+ `-o "${e}/%(id)s.%(ext)s"`,
115
+ `--cookies ${r}`,
116
+ s.ffmpegDir ? `--ffmpeg-location "${s.ffmpegDir}"` : ""
117
+ ];
118
+ return x({
119
+ debug: t,
120
+ command: b.join(" "),
121
+ dest: i,
122
+ tempDir: e,
110
123
  ffmpegConfig: s
111
124
  });
112
- }, K = async ({
113
- videoId: e,
125
+ }, F = async ({
126
+ videoId: t,
114
127
  dest: o,
115
- debug: n = !1,
116
- headless: r = !0
128
+ debug: a = !1,
129
+ headless: i = !0
117
130
  }) => {
118
131
  try {
119
- const t = h(n);
120
- t.log(`Downloading video: ${e} to: ${o}`);
121
- const s = `https://m.facebook.com/watch/?v=${e}`, i = await w("fs"), a = await w("path"), u = await w("puppeteer");
122
- i.existsSync(a.dirname(o)) || (i.mkdirSync(a.dirname(o), { recursive: !0 }, 777), t.log(`建立 ${a.dirname(o)}`));
132
+ const e = v(a);
133
+ e.log(`Downloading video: ${t} to: ${o}`);
134
+ const s = `https://m.facebook.com/watch/?v=${t}`, c = await u("fs"), n = await u("path"), l = await u("puppeteer");
135
+ c.existsSync(n.dirname(o)) || (c.mkdirSync(n.dirname(o), { recursive: !0 }, 777), e.log(`建立 ${n.dirname(o)}`));
123
136
  const g = {};
124
- r || (g.headless = !1);
125
- const y = await u.launch(g);
126
- t.log("啟動 puppeteer");
127
- const l = await y.newPage();
128
- await l.setUserAgent(
137
+ i || (g.headless = !1);
138
+ const d = await l.launch(g);
139
+ e.log("啟動 puppeteer");
140
+ const r = await d.newPage();
141
+ await r.setUserAgent(
129
142
  "'Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Mobile/15E148 Safari/604.1'"
130
- ), t.log("開新分頁"), t.log(`前往 ${s}`), await Promise.race([
131
- l.goto(s, {
132
- waitUntil: "networkidle0"
133
- }),
134
- l.waitForSelector("video")
135
- ]), t.log("找到 video 元素");
136
- const p = await l.$eval("video", (d) => d.getAttribute("src"));
137
- return t.log(`取得 video url: ${p}`), await y.close(), t.log("關閉 puppeteer"), await b(p, o), t.log(`下載完成 ${p}`), [null, !0];
138
- } catch (t) {
139
- return [t, null];
143
+ ), e.log("開新分頁"), e.log(`前往 ${s}`), await r.goto(s, {
144
+ waitUntil: "networkidle2"
145
+ }), await r.waitForSelector("video"), e.log("找到 video 元素");
146
+ const m = await r.$eval("video", (w) => w.getAttribute("src"));
147
+ return e.log(`取得 video url: ${m}`), await d.close(), e.log("關閉 puppeteer"), await U(m, o), e.log(`下載完成 ${m}`), [null, !0];
148
+ } catch (e) {
149
+ return [e, null];
140
150
  }
141
- }, _ = async ({
142
- debug: e = !1,
151
+ }, M = async ({
152
+ debug: t = !1,
143
153
  ytdlpPath: o,
144
- videoId: n,
145
- dest: r,
146
- tempDir: t,
154
+ videoId: a,
155
+ dest: i,
156
+ tempDir: e,
147
157
  ffmpegConfig: s
148
158
  }) => {
149
- h(e).log(`Downloading video: ${n} to: ${r}`);
150
- const a = `https://vimeo.com/${n}`;
151
- return S({
152
- debug: e,
153
- ytdlpPath: o,
154
- videoUrl: a,
155
- dest: r,
156
- tempDir: t,
159
+ v(t).log(`Downloading video: ${a} to: ${i}`);
160
+ const n = `https://vimeo.com/${a}`, l = [
161
+ `${o}`,
162
+ '-f "bv*[vcodec^=avc1][acodec^=mp4a][height<=1080]/ba/b"',
163
+ "--js-runtimes node",
164
+ `${n}`,
165
+ `-o "${e}/%(id)s.%(ext)s"`
166
+ ];
167
+ return x({
168
+ debug: t,
169
+ command: l.join(" "),
170
+ dest: i,
171
+ tempDir: e,
157
172
  ffmpegConfig: s
158
173
  });
159
- }, C = async ({
160
- debug: e = !1,
174
+ }, E = async ({
175
+ debug: t = !1,
161
176
  ytdlpPath: o,
162
- videoId: n,
163
- dest: r,
164
- tempDir: t,
177
+ videoId: a,
178
+ dest: i,
179
+ tempDir: e,
165
180
  ffmpegConfig: s
166
181
  }) => {
167
- h(e).log(`Downloading video: ${n} to: ${r}`);
168
- const a = `https://www.instagram.com/reel/${n}/`;
169
- return S({
170
- debug: e,
171
- ytdlpPath: o,
172
- videoUrl: a,
173
- dest: r,
174
- tempDir: t,
182
+ v(t).log(`Downloading video: ${a} to: ${i}`);
183
+ const n = `https://www.instagram.com/reel/${a}/`, l = [
184
+ `${o}`,
185
+ "-f mergeall",
186
+ "--audio-multistreams",
187
+ "--video-multistreams",
188
+ "--js-runtimes node",
189
+ `${n}`,
190
+ "--downloader ffmpeg",
191
+ `-o "${e}/%(id)s.%(ext)s"`,
192
+ "--merge-output-format mp4",
193
+ s.ffmpegDir ? `--ffmpeg-location "${s.ffmpegDir}"` : ""
194
+ ];
195
+ return x({
196
+ debug: t,
197
+ command: l.join(" "),
198
+ dest: i,
199
+ tempDir: e,
175
200
  ffmpegConfig: s
176
201
  });
177
- }, H = async ({
178
- videoLink: e,
202
+ }, _ = async ({
203
+ videoLink: t,
179
204
  dest: o,
180
- debug: n = !1,
181
- headless: r = !0
205
+ debug: a = !1,
206
+ headless: i = !0
182
207
  }) => {
183
208
  try {
184
- const t = h(n);
185
- t.log(`Downloading video: ${e} to: ${o}`);
186
- const s = e, i = await w("fs"), a = await w("path"), u = await w("puppeteer");
187
- i.existsSync(a.dirname(o)) || (i.mkdirSync(a.dirname(o), { recursive: !0 }, 777), t.log(`建立 ${a.dirname(o)}`));
209
+ const e = v(a);
210
+ e.log(`Downloading video: ${t} to: ${o}`);
211
+ const s = t, c = await u("fs"), n = await u("path"), l = await u("puppeteer");
212
+ c.existsSync(n.dirname(o)) || (c.mkdirSync(n.dirname(o), { recursive: !0 }, 777), e.log(`建立 ${n.dirname(o)}`));
188
213
  const g = {};
189
- r || (g.headless = !1);
190
- const y = await u.launch(g);
191
- t.log("啟動 puppeteer");
192
- const l = await y.newPage();
193
- t.log("開新分頁"), t.log(`前往 ${s}`), await Promise.race([
194
- l.goto(s, {
195
- waitUntil: "networkidle0"
196
- }),
197
- l.waitForSelector("video")
198
- ]), t.log("找到 video 元素");
199
- const p = await l.$eval(
214
+ i || (g.headless = !1);
215
+ const d = await l.launch(g);
216
+ e.log("啟動 puppeteer");
217
+ const r = await d.newPage();
218
+ e.log("開新分頁"), e.log(`前往 ${s}`), await r.goto(s, {
219
+ waitUntil: "networkidle2"
220
+ }), await r.waitForSelector("video"), e.log("找到 video 元素");
221
+ const m = await r.$eval(
200
222
  'video source[src^="https://www.tiktok.com/aweme/v1/play/"]',
201
- (d) => d.getAttribute("src")
223
+ (w) => w.getAttribute("src")
202
224
  );
203
- return t.log(`取得 video url: ${p}`), await y.close(), t.log("關閉 puppeteer"), await b(p, o, {
225
+ return e.log(`取得 video url: ${m}`), await d.close(), e.log("關閉 puppeteer"), await U(m, o, {
204
226
  "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
205
227
  Referer: "https://www.tiktok.com/"
206
228
  // 設定 Referer
207
- }), t.log(`下載完成 ${p}`), [null, !0];
208
- } catch (t) {
209
- return [t, null];
229
+ }), e.log(`下載完成 ${m}`), [null, !0];
230
+ } catch (e) {
231
+ return [e, null];
210
232
  }
211
- }, I = ({ type: e }) => {
212
- switch (e) {
233
+ }, W = ({ type: t }) => {
234
+ switch (t) {
213
235
  case "youtube":
214
236
  case "yt-short":
215
- return E;
237
+ return D;
216
238
  case "facebook":
217
239
  case "fb-reel":
218
- return K;
240
+ return F;
219
241
  case "vimeo":
220
- return _;
242
+ return M;
221
243
  case "instagram":
222
244
  case "ig":
223
245
  case "ig-reel":
224
- return C;
246
+ return E;
225
247
  case "tiktok":
226
248
  case "tiktok-reel":
227
- return H;
249
+ return _;
228
250
  default:
229
- throw new Error(`Invalid video type: ${e}`);
251
+ throw new Error(`Invalid video type: ${t}`);
230
252
  }
231
253
  };
232
254
  export {
233
- I as useDownloader,
234
- K as useFacebook,
235
- C as useInstagram,
236
- H as useTiktok,
237
- _ as useVimeo,
238
- E as useYoutube
255
+ W as useDownloader,
256
+ F as useFacebook,
257
+ E as useInstagram,
258
+ _ as useTiktok,
259
+ M as useVimeo,
260
+ D as useYoutube
239
261
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pieda/video-dl",
3
3
  "private": false,
4
- "version": "1.10.2",
4
+ "version": "1.11.0",
5
5
  "type": "module",
6
6
  "main": "dist/video-dl.cjs",
7
7
  "module": "dist/video-dl.js",