@totallynotdavid/downloader 1.0.2 → 1.1.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.
package/dist/index.d.ts CHANGED
@@ -14,8 +14,14 @@ type MediaResult = {
14
14
  title: string;
15
15
  author: string;
16
16
  platform: string;
17
+ description?: string;
18
+ thumbnail?: string;
17
19
  views?: number;
18
20
  likes?: number;
21
+ comments?: number;
22
+ shares?: number;
23
+ reposts?: number;
24
+ timestamp?: number;
19
25
  };
20
26
  };
21
27
  declare function resolve(url: string, options?: ResolveOptions): Promise<MediaResult>;
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- class R extends Error{constructor(o){super(`No extractor found for URL: ${o}`);this.name="PlatformNotSupportedError"}}class p extends Error{statusCode;constructor(o,r){super(o);if(this.name="NetworkError",r!==void 0)this.statusCode=r}}class u extends Error{platform;constructor(o,r){super(`[${r}] ${o}`);this.name="ParseError",this.platform=r}}var D="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36",j=1e4;async function _(o,r={}){let t=new AbortController,a=setTimeout(()=>t.abort(),r.timeout??j);try{let n=await fetch(o,{headers:{"User-Agent":D,"Accept-Language":"en-US,en;q=0.9",...r.headers},signal:t.signal});if(!n.ok)throw new p(`HTTP ${n.status}: ${n.statusText}`,n.status);return n}catch(n){if(n.name==="AbortError")throw new p("Request timeout",408);if(n instanceof p)throw n;throw new p(n.message)}finally{clearTimeout(a)}}async function I(o,r,t={}){let a=new AbortController,n=setTimeout(()=>a.abort(),t.timeout??j);try{let e=await fetch(o,{method:"POST",headers:{"User-Agent":D,"Content-Type":r instanceof URLSearchParams?"application/x-www-form-urlencoded":"application/json",...t.headers},body:r.toString(),signal:a.signal});if(!e.ok)throw new p(`HTTP ${e.status}: ${e.statusText}`,e.status);return e}catch(e){if(e.name==="AbortError")throw new p("Request timeout",408);if(e instanceof p)throw e;throw new p(e.message)}finally{clearTimeout(n)}}var V="936619743392459",Y="8845758582119845",q="Instagram 309.0.0.15.109 Android (31/12; 480dpi; 1080x2228; samsung; SM-G996B; t2s; qcom; en_US; 544099989)",B=/(?:p|reel|tv)\/([A-Za-z0-9_-]+)/;function F(o,r,t){let a=o.is_video,n=a?o.video_url:o.display_url;if(!n)return null;let e=t>0?`-${t}`:"";return{type:a?"video":"image",url:n,filename:`instagram-${r}${e}.${a?"mp4":"jpg"}`}}function z(o,r){let t=o.__typename;if(t==="GraphSidecar"||t==="XDTGraphSidecar")return(o.edge_sidecar_to_children?.edges||[]).map((i,s)=>i?.node&&F(i.node,r,s+1)).filter((i)=>i!==null);let n=F(o,r,0);return n?[n]:[]}async function T(o,r){let t=o.match(B);if(!t?.[1])throw new u("Could not parse post shortcode","instagram");let a=t[1],n=new URLSearchParams({doc_id:Y,variables:JSON.stringify({shortcode:a})});try{let e={headers:{"User-Agent":q,"Content-Type":"application/x-www-form-urlencoded","X-IG-App-ID":V,"X-IG-WWW-Claim":"0","X-Requested-With":"XMLHttpRequest",Accept:"*/*","Accept-Language":"en-US,en;q=0.9",...r.headers}};if(r.timeout!==void 0)e.timeout=r.timeout;let d=(await(await I("https://www.instagram.com/graphql/query",n,e)).json())?.data?.xdt_shortcode_media;if(!d)throw new u("No media data found","instagram");let l=z(d,a);if(l.length===0)throw new u("No media found","instagram");let c=d.edge_media_to_caption?.edges?.[0]?.node?.text,m=d.owner?.username;return{urls:l,headers:{"User-Agent":q,Referer:"https://www.instagram.com/"},meta:{platform:"instagram",title:c||"Instagram post",author:m||"Unknown"}}}catch(e){if(e instanceof p||e instanceof u)throw e;throw new u(e.message,"instagram")}}var H=/<script id="__FRONTITY_CONNECT_STATE__" type="application\/json">([\s\S]*?)<\/script>/;function K(o){if(o.includes("\\u0026"))return o.replaceAll("\\u0026","&");if(o.includes("&amp;"))return o.replaceAll("&amp;","&");return o}function J(o,r){let t=o.match(H);if(!t?.[1])return;let n=JSON.parse(t[1]).source?.data||{},e=Object.keys(n).find((l)=>l.startsWith(`/embed/v2/${r}`)),i=e?n[e]?.videoData?.itemInfos:void 0;if(!i)return;let s={...i.id&&{id:i.id},...i.text&&{desc:i.text},...i.covers&&{covers:i.covers},...i.video?.urls?.[0]&&{video:{playAddr:i.video.urls[0]}},...i.musicInfos?.playUrl?.[0]&&{music:{playUrl:i.musicInfos.playUrl[0]}}},d=i.imagePostInfo?.displayImages?.map((l)=>({imageURL:{urlList:l.urlList}}));if(d?.length)s.imagePost={images:d};if(i.authorInfos?.nickName||i.authorInfos?.uniqueId)s.author={...i.authorInfos.nickName&&{nickname:i.authorInfos.nickName},...i.authorInfos.uniqueId&&{uniqueId:i.authorInfos.uniqueId}};if(i.diggCount!==void 0||i.playCount!==void 0)s.stats={...i.diggCount!==void 0&&{diggCount:i.diggCount},...i.playCount!==void 0&&{playCount:i.playCount}};return s}async function O(o,r){let t=o.match(/\/(?:video|photo)\/(\d+)/)?.[1];if(!t)throw new u("Could not parse TikTok post id","tiktok");try{let a=`https://www.tiktok.com/embed/v2/${t}`,n=await _(a,r).then((f)=>f.text()),e=J(n,t);if(!e)throw new u("Metadata not found","tiktok");let i=e.video?.bitrateInfo?.[0]?.PlayAddr?.UrlList?.[0]||e.video?.playAddr,s=n.match(/https:\/\/[^"\s]+tplv-photomode-image[^"\s]*/g)||[],d=[...new Set(s.map((f)=>K(f)).filter((f)=>f.includes("x-signature=")))],l=[...new Set([...e.imagePost?.images?.map((f)=>f.imageURL?.urlList?.[0]).filter((f)=>Boolean(f))||[],...e.covers||[],...d])],c=[];if(l.length>0&&!i){if(l.forEach((f,h)=>{c.push({type:"image",url:f,filename:`tiktok-${e.id}-${h+1}.jpg`})}),e.music?.playUrl)c.push({type:"audio",url:e.music.playUrl,filename:`tiktok-${e.id}-audio.mp3`})}else if(i)c.push({type:"video",url:i,filename:`tiktok-${e.id}.mp4`});if(!c.length)throw new u("No media content found","tiktok");let m={title:e.desc||"TikTok Post",author:e.author?.nickname||e.author?.uniqueId||"Unknown",platform:"tiktok"};if(e.stats?.diggCount!==void 0)m.likes=e.stats.diggCount;if(e.stats?.playCount!==void 0)m.views=e.stats.playCount;return{urls:c,headers:{Referer:"https://www.tiktok.com/"},meta:m}}catch(a){if(a instanceof p||a instanceof u)throw a;throw new u(a.message,"tiktok")}}var $="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36";function k(o){try{return o.replace(/\\u([\dA-Fa-f]{4})/g,(r,t)=>String.fromCharCode(Number.parseInt(t,16))).replace(/\\\//g,"/")}catch{return o}}function b(o,r,t){let a=o.indexOf(r);if(a===-1)return"";let n=a+r.length,e=o.indexOf(t,n);if(e===-1)return"";return o.slice(n,e)}function Z(o){let r=b(o,"\\\"video_id\\\":\\\"","\\\""),t=k(b(o,'"actors":[{"__typename":"User","name":"','","')),a=b(o,'"permalink_url"',"\\/Period>\\u003C\\/MPD>"),n=b(a,"AudioChannelConfiguration","BaseURL>\\u003C"),e=k(b(n,"BaseURL>","\\u003C\\/")),i={},s=a.split("FBQualityLabel=\\\"");for(let d=1;d<s.length;d++){let l=s[d];if(!l)continue;let c=l.split('"',1)[0];if(!c)continue;let m=l.split("BaseURL>",2)[1];if(!m)continue;let f=m.split("\\u003C\\/BaseURL>",1)[0];if(!f)continue;let h=k(f);if(h)i[c]=h}return{video_urls:i,audio_url:e,video_id:r,username:t}}function Q(o){let r=b(o,'"__isNode":"Photo","id":"','"'),t=k(b(o,'"owner":{"__typename":"User","name":"','"'));return{photo_url:k(b(o,',"image":{"uri":"','","')),photo_id:r,username:t}}async function E(o,r){try{let t={headers:{"User-Agent":$,Accept:"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8","Accept-Language":"en-US,en;q=0.9","Sec-Fetch-Dest":"document","Sec-Fetch-Mode":"navigate","Sec-Fetch-Site":"none",...r.headers}};if(r.timeout!==void 0)t.timeout=r.timeout;let n=await(await _(o,t)).text(),e=n.includes("\\\"video_id\\\":\\\""),i=[];if(e){let{video_urls:c,audio_url:m,video_id:f,username:h}=Z(n);if(Object.keys(c).length===0)throw new u("No video URLs found","facebook");let g=Object.keys(c).map((w)=>Number.parseInt(w.replace(/\D/g,""),10)||0).reduce((w,M)=>Math.max(w,M),0),y=Object.keys(c).find((w)=>w.includes(String(g))),v=y?c[y]:Object.values(c)[0];if(!v)throw new u("Failed to select video URL","facebook");if(i.push({type:"video",url:v,filename:`fb-${f||Date.now()}.mp4`}),m)i.push({type:"audio",url:m,filename:`fb-${f||Date.now()}.m4a`});return{urls:i,headers:{"User-Agent":$},meta:{title:"Facebook Video",author:h||"Unknown",platform:"facebook"}}}let{photo_url:s,photo_id:d,username:l}=Q(n);if(!s)throw new u("No photo URL found","facebook");return{urls:[{type:"image",url:s,filename:`fb-${d||Date.now()}.jpg`}],headers:{"User-Agent":$},meta:{title:"Facebook Photo",author:l||"Unknown",platform:"facebook"}}}catch(t){if(t instanceof p||t instanceof u)throw t;throw new u(t.message,"facebook")}}import ee from"node:path";var te="https://api.vxtwitter.com";async function x(o,r){try{let t=new URL(o),a=`${te}${t.pathname}`,e=await(await _(a,r)).json();if(!e?.media_extended||e.media_extended.length===0)throw new u("No media found in tweet","twitter");let i=e.media_extended.map((d,l)=>{let c=ee.extname(new URL(d.url).pathname)||".mp4";return{type:d.type==="video"||d.type==="gif"?"video":"image",url:d.url,filename:`twitter-${e.tweetID}-${l+1}${c}`}}),s={title:e.text||"Twitter post",author:`${e.user_name} (@${e.user_screen_name})`,platform:"twitter"};if(e.likes!==void 0)s.likes=e.likes;if(e.views!==void 0)s.views=e.views;return{urls:i,headers:{},meta:s}}catch(t){if(t instanceof p||t instanceof u)throw t;throw new u(t.message,"twitter")}}var oe="https://www.youtube.com/youtubei/v1/player?key=",S={clientName:"ANDROID",clientVersion:"21.02.35",androidSdkVersion:30,hl:"en",gl:"US",userAgent:"com.google.android.youtube/21.02.35 (Linux; U; Android 11) gzip"};function re(o){let r=[/(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/shorts\/)([a-zA-Z0-9_-]{11})/,/youtube\.com\/embed\/([a-zA-Z0-9_-]{11})/];for(let t of r){let a=o.match(t);if(a?.[1])return a[1]}return null}function ie(o){return o.replace(/[<>:"/\\|?*]/g,"").trim()}function ne(o){return o.match(/"INNERTUBE_API_KEY":"([^"]+)"/)?.[1]||null}function se(o,r){if(o.playabilityStatus?.status!=="OK")throw new u(o.playabilityStatus?.reason||"Video not playable","youtube");if(!o.streamingData)throw new u("No streaming data available","youtube");let t=[...o.streamingData.formats||[],...o.streamingData.adaptiveFormats||[]],a=[],n=t.filter((s)=>s.url&&s.audioChannels&&s.audioChannels>0&&s.width&&s.width>0).sort((s,d)=>(d.width||0)-(s.width||0))[0];if(n?.url)a.push({type:"video",url:n.url,filename:`youtube-${r}.mp4`});let e=t.filter((s)=>s.url&&s.width&&s.width>0&&(!s.audioChannels||s.audioChannels===0)&&s.mimeType.includes("video/mp4")).sort((s,d)=>(d.width||0)-(s.width||0))[0],i=t.filter((s)=>s.url&&s.audioChannels&&s.audioChannels>0&&(!s.width||s.width===0)&&s.mimeType.includes("audio/mp4")).sort((s,d)=>d.bitrate-s.bitrate)[0];if(e?.url&&e.width&&e.width>(n?.width||0)){let s=e.mimeType.includes("webm")?"webm":"mp4";if(a.push({type:"video",url:e.url,filename:`youtube-${r}-video.${s}`}),i?.url)a.push({type:"audio",url:i.url,filename:`youtube-${r}-audio.m4a`})}if(a.length===0)throw new u("No downloadable formats found","youtube");return a}async function P(o,r){try{let t=re(o);if(!t)throw new u("Could not extract video ID from URL","youtube");let a=r.timeout??15000,n={"Accept-Language":"en-US,en;q=0.9",...r.headers},i=await(await _(`https://www.youtube.com/watch?v=${t}`,{headers:n,timeout:a})).text(),s=ne(i);if(!s)throw new u("Could not find Innertube API key","youtube");let l=await(await I(`${oe}${s}`,JSON.stringify({videoId:t,context:{client:S},contentCheckOk:!0,racyCheckOk:!0}),{headers:{...n,"Content-Type":"application/json","User-Agent":S.userAgent,"X-Youtube-Client-Name":"3","X-Youtube-Client-Version":S.clientVersion},timeout:a})).json(),c=se(l,t),m=l.videoDetails?.title||"YouTube video",f=l.videoDetails?.author||"Unknown",h=l.videoDetails?.viewCount?Number.parseInt(l.videoDetails.viewCount,10):void 0,g={title:ie(m),author:f,platform:"youtube"};if(h!==void 0&&Number.isFinite(h))g.views=h;return{urls:c,headers:{},meta:g}}catch(t){if(t instanceof p||t instanceof u)throw t;throw new u(t.message,"youtube")}}var ae=/\.(mp4|mkv|webm)$/i;async function A(o,r){try{let t=`${o.replace(/\/$/,"")}.json`,n=await(await _(t,r)).json();if(!(Array.isArray(n)&&n[0]?.data?.children?.[0]?.data))throw new u("Invalid Reddit API response","reddit");let e=n[0].data.children[0].data,i=[];if(e.is_gallery&&e.media_metadata){let s=e.gallery_data?.items||[];for(let d of s){let l=d.media_id,c=e.media_metadata[l];if(c?.s){let m=c.s.u||c.s.gif;if(m)i.push({type:"image",url:m.replace(/&amp;/g,"&"),filename:`reddit-${l}.jpg`})}}}else if(e.is_video&&e.media?.reddit_video?.fallback_url)i.push({type:"video",url:e.media.reddit_video.fallback_url.replace(/&amp;/g,"&"),filename:`reddit-${e.id}.mp4`});else if(e.url_overridden_by_dest||e.url){let s=(e.url_overridden_by_dest||e.url).replace(/&amp;/g,"&"),d=s.match(ae);i.push({type:d?"video":"image",url:s,filename:`reddit-${e.id}.${d?"mp4":"jpg"}`})}if(i.length===0)throw new u("No media found in post","reddit");return{urls:i,headers:{},meta:{title:e.title,author:e.author,platform:"reddit",likes:e.score,views:e.view_count}}}catch(t){if(t instanceof p||t instanceof u)throw t;throw new u(t.message,"reddit")}}import ue from"node:path";var de="546c25a59c58ad7",le=/^[a-zA-Z0-9]+$/;async function N(o,r){try{let a=new URL(o).pathname.split("/").filter(Boolean),n=a[a.length-1];if(!n)throw new u("Invalid Imgur URL: no path","imgur");let e=n.split(".")[0];if(!e)throw new u("Invalid Imgur URL: no ID found","imgur");let i=a.includes("gallery")||a.includes("a");if(i&&e.includes("-")){let g=e.split("-"),y=g[g.length-1];if(y&&y.length>=5&&le.test(y))e=y}let d=`https://api.imgur.com/3/${i?"album":"image"}/${e}?client_id=${de}`,m=(await(await _(d,r)).json())?.data;if(!m)throw new u("Imgur API returned no data","imgur");let f=m.images||[m],h=[];for(let g of f)if(g.link){let y=ue.extname(g.link)||".jpg";h.push({type:g.type?.startsWith("video")?"video":"image",url:g.link,filename:`imgur-${g.id}${y}`})}if(h.length===0)throw new u("No media links found","imgur");return{urls:h,headers:{},meta:{title:m.title||"Imgur Media",author:m.account_url||"Unknown",platform:"imgur",views:m.views,likes:(m.ups||0)-(m.downs||0)}}}catch(t){if(t instanceof p||t instanceof u)throw t;throw new u(t.message,"imgur")}}var ce=/\/pin\/(?:[\w-]+--)?(\d+)/,X=["V_720P","V_EXP7","V_EXP6","V_EXP5","V_EXP4"];async function U(o,r){let t=o.match(ce);if(!t?.[1])throw new u("Could not extract pin ID from URL","pinterest");let a=t[1];try{let n="https://www.pinterest.com/resource/PinResource/get/?data="+encodeURIComponent(JSON.stringify({options:{field_set_key:"unauth_react_main_pin",id:a}})),e={headers:{"X-Pinterest-PWS-Handler":"www/[username].js",...r.headers}};if(r.timeout!==void 0)e.timeout=r.timeout;let d=(await(await _(n,e)).json()).resource_response?.data;if(!d)throw new u("Invalid Pinterest API response","pinterest");let l=d.title||d.grid_title||"Pinterest Pin",c=d.closeup_attribution?.full_name||d.pinner?.full_name||d.pinner?.username||"Unknown",m=d.story_pin_data?.pages;if(m)for(let v of m){let w=v.blocks;if(!w)continue;for(let M of w){let L=M.video?.video_list;if(!L)continue;for(let W of X){let C=L[W];if(C?.url&&!C.url.endsWith(".m3u8"))return{urls:[{type:"video",url:C.url,filename:`pinterest-${a}.mp4`}],headers:{},meta:{title:l,author:c,platform:"pinterest"}}}}}let f=d.videos?.video_list;if(f)for(let v of X){let w=f[v];if(w?.url&&!w.url.endsWith(".m3u8"))return{urls:[{type:"video",url:w.url,filename:`pinterest-${a}.mp4`}],headers:{},meta:{title:l,author:c,platform:"pinterest"}}}let h=d.images;if(!h)throw new u("No media found in pin","pinterest");let g=h.orig?.url;if(!g){let v=0;for(let w of Object.values(h))if(w.width>v&&w.url)v=w.width,g=w.url}if(!g)throw new u("No image URL found","pinterest");let y=g.split(".").pop()?.split("?")[0]||"jpg";return{urls:[{type:"image",url:g,filename:`pinterest-${a}.${y}`}],headers:{},meta:{title:l,author:c,platform:"pinterest"}}}catch(n){if(n instanceof p||n instanceof u)throw n;throw new u(n.message,"pinterest")}}var me=new Map([["instagram.com",T],["tiktok.com",O],["facebook.com",E],["fb.com",E],["twitter.com",x],["x.com",x],["youtube.com",P],["youtu.be",P],["reddit.com",A],["redd.it",A],["imgur.com",N],["i.imgur.com",N],["pinterest.com",U]]);function G(o){let r=new URL(o).hostname.replace(/^www\./,""),t=me.get(r);if(t)return t;if(r.endsWith(".pinterest.com"))return U;return null}async function pe(o,r={}){let t=G(o);if(!t)throw new R(o);return t(o,r)}export{pe as resolve,R as PlatformNotSupportedError,u as ParseError,p as NetworkError};
1
+ class P extends Error{constructor(r){super(`No extractor found for URL: ${r}`);this.name="PlatformNotSupportedError"}}class w extends Error{statusCode;constructor(r,o){super(r);if(this.name="NetworkError",o!==void 0)this.statusCode=o}}class c extends Error{platform;constructor(r,o){super(`[${o}] ${r}`);this.name="ParseError",this.platform=o}}var F="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36",q=1e4;async function k(r,o={}){let t=new AbortController,s=setTimeout(()=>t.abort(),o.timeout??q);try{let i=await fetch(r,{headers:{"User-Agent":F,"Accept-Language":"en-US,en;q=0.9",...o.headers},signal:t.signal});if(!i.ok)throw new w(`HTTP ${i.status}: ${i.statusText}`,i.status);return i}catch(i){if(i instanceof Error&&i.name==="AbortError")throw new w("Request timeout",408);if(i instanceof w)throw i;let e=i instanceof Error?i.message:"Unknown error";throw new w(e)}finally{clearTimeout(s)}}async function b(r,o,t={}){let s=new AbortController,i=setTimeout(()=>s.abort(),t.timeout??q);try{let e=await fetch(r,{method:"POST",headers:{"User-Agent":F,"Content-Type":o instanceof URLSearchParams?"application/x-www-form-urlencoded":"application/json",...t.headers},body:o.toString(),signal:s.signal});if(!e.ok)throw new w(`HTTP ${e.status}: ${e.statusText}`,e.status);return e}catch(e){if(e instanceof Error&&e.name==="AbortError")throw new w("Request timeout",408);if(e instanceof w)throw e;let n=e instanceof Error?e.message:"Unknown error";throw new w(n)}finally{clearTimeout(i)}}var z="936619743392459",J="8845758582119845",W="Instagram 309.0.0.15.109 Android (31/12; 480dpi; 1080x2228; samsung; SM-G996B; t2s; qcom; en_US; 544099989)",K=/(?:p|reel|tv)\/([A-Za-z0-9_-]+)/;function G(r,o,t){let s=r.is_video,i=s?r.video_url:r.display_url;if(!i)return null;let e=t>0?`-${t}`:"";return{type:s?"video":"image",url:i,filename:`instagram-${o}${e}.${s?"mp4":"jpg"}`}}function Z(r,o){let t=r.__typename;if(t==="GraphSidecar"||t==="XDTGraphSidecar")return(r.edge_sidecar_to_children?.edges||[]).map((n,a)=>n?.node&&G(n.node,o,a+1)).filter((n)=>n!==null);let i=G(r,o,0);return i?[i]:[]}async function C(r,o){let t=r.match(K);if(!t?.[1])throw new c("Could not parse post shortcode","instagram");let s=t[1],i=new URLSearchParams({doc_id:J,variables:JSON.stringify({shortcode:s})});try{let e={headers:{"User-Agent":W,"Content-Type":"application/x-www-form-urlencoded","X-IG-App-ID":z,"X-IG-WWW-Claim":"0","X-Requested-With":"XMLHttpRequest",Accept:"*/*","Accept-Language":"en-US,en;q=0.9",...o.headers}};if(o.timeout!==void 0)e.timeout=o.timeout;let u=(await(await b("https://www.instagram.com/graphql/query",i,e)).json())?.data?.xdt_shortcode_media;if(!u)throw new c("No media data found","instagram");let l=Z(u,s);if(l.length===0)throw new c("No media found","instagram");let d=u.edge_media_to_caption?.edges?.[0]?.node?.text,m=u.owner?.username,p=u.display_url??u.thumbnail_src,g=u.taken_at_timestamp,h=u.edge_media_preview_like?.count,R=u.edge_media_to_parent_comment?.count,_={platform:"instagram",title:d||"Instagram post",author:m||"Unknown"};if(d)_.description=d;if(p)_.thumbnail=p;if(g!=null&&g>0)_.timestamp=g;if(h!==void 0)_.likes=h;if(R!==void 0)_.comments=R;return{urls:l,headers:{"User-Agent":W,Referer:"https://www.instagram.com/"},meta:_}}catch(e){if(e instanceof w||e instanceof c)throw e;let n=e instanceof Error?e.message:"Unknown error";throw new c(n,"instagram")}}var Y=/<script id="__FRONTITY_CONNECT_STATE__" type="application\/json">([\s\S]*?)<\/script>/;function ee(r){if(r.includes("\\u0026"))return r.replaceAll("\\u0026","&");if(r.includes("&amp;"))return r.replaceAll("&amp;","&");return r}function te(r,o){let t=r.match(Y);if(!t?.[1])return;let i=JSON.parse(t[1]).source?.data??{},e=Object.keys(i).find((g)=>g.startsWith(`/embed/v2/${o}`)),n=e?i[e]?.videoData:void 0,a=n?.itemInfos;if(!a)return;let u=n?.authorInfos??a.authorInfos,l=a.video?.urls?.[0],d=a.musicInfos?.playUrl?.[0],m={...a.id&&{id:a.id},...a.text&&{desc:a.text},...a.covers&&{covers:a.covers},...l&&{video:{playAddr:l}},...d&&{music:{playUrl:d}},...a.createTime!==void 0&&{createTime:a.createTime}},p=a.imagePostInfo?.displayImages?.map((g)=>({imageURL:{urlList:g.urlList??[]}}));if(p?.length)m.imagePost={images:p};if(u?.nickName||u?.uniqueId)m.author={...u.nickName&&{nickname:u.nickName},...u.uniqueId&&{uniqueId:u.uniqueId}};if(a.diggCount!==void 0||a.playCount!==void 0||a.commentCount!==void 0||a.shareCount!==void 0)m.stats={...a.diggCount!==void 0&&{diggCount:a.diggCount},...a.playCount!==void 0&&{playCount:a.playCount},...a.commentCount!==void 0&&{commentCount:a.commentCount},...a.shareCount!==void 0&&{shareCount:a.shareCount}};return m}async function E(r,o){let t=r.match(/\/(?:video|photo)\/(\d+)/)?.[1];if(!t)throw new c("Could not parse TikTok post id","tiktok");try{let s=`https://www.tiktok.com/embed/v2/${t}`,i=await k(s,o).then((p)=>p.text()),e=te(i,t);if(!e)throw new c("Metadata not found","tiktok");let n=e.video?.bitrateInfo?.[0]?.PlayAddr?.UrlList?.[0]||e.video?.playAddr,a=i.match(/https:\/\/[^"\s]+tplv-photomode-image[^"\s]*/g)||[],u=[...new Set(a.map((p)=>ee(p)).filter((p)=>p.includes("x-signature=")))],l=[...new Set([...e.imagePost?.images?.map((p)=>p.imageURL?.urlList?.[0]).filter((p)=>Boolean(p))||[],...e.covers||[],...u])],d=[];if(l.length>0&&!n){if(l.forEach((p,g)=>{d.push({type:"image",url:p,filename:`tiktok-${e.id}-${g+1}.jpg`})}),e.music?.playUrl)d.push({type:"audio",url:e.music.playUrl,filename:`tiktok-${e.id}-audio.mp3`})}else if(n)d.push({type:"video",url:n,filename:`tiktok-${e.id}.mp4`});if(!d.length)throw new c("No media content found","tiktok");let m={title:e.desc||"TikTok Post",author:e.author?.nickname||e.author?.uniqueId||"Unknown",platform:"tiktok"};if(e.createTime!==void 0){let p=Number(e.createTime);if(Number.isFinite(p)&&p>0)m.timestamp=p}if(e.stats?.diggCount!==void 0)m.likes=e.stats.diggCount;if(e.stats?.playCount!==void 0)m.views=e.stats.playCount;if(e.stats?.commentCount!==void 0)m.comments=e.stats.commentCount;if(e.stats?.shareCount!==void 0)m.shares=e.stats.shareCount;return{urls:d,headers:{Referer:"https://www.tiktok.com/"},meta:m}}catch(s){if(s instanceof w||s instanceof c)throw s;let i=s instanceof Error?s.message:"Unknown error";throw new c(i,"tiktok")}}var T="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36";function A(r){try{return r.replace(/\\u([\dA-Fa-f]{4})/g,(o,t)=>String.fromCharCode(Number.parseInt(t,16))).replace(/\\\//g,"/")}catch{return r}}function v(r,o,t){let s=r.indexOf(o);if(s===-1)return"";let i=s+o.length,e=r.indexOf(t,i);if(e===-1)return"";return r.slice(i,e)}function ne(r){let o=v(r,"\\\"video_id\\\":\\\"","\\\""),t=A(v(r,'"actors":[{"__typename":"User","name":"','","')),s=v(r,'"permalink_url"',"\\/Period>\\u003C\\/MPD>"),i=v(s,"AudioChannelConfiguration","BaseURL>\\u003C"),e=A(v(i,"BaseURL>","\\u003C\\/")),n={},a=s.split("FBQualityLabel=\\\"");for(let u=1;u<a.length;u++){let l=a[u];if(!l)continue;let d=l.split('"',1)[0];if(!d)continue;let m=l.split("BaseURL>",2)[1];if(!m)continue;let p=m.split("\\u003C\\/BaseURL>",1)[0];if(!p)continue;let g=A(p);if(g)n[d]=g}return{video_urls:n,audio_url:e,video_id:o,username:t}}function re(r){let o=v(r,'"__isNode":"Photo","id":"','"'),t=A(v(r,'"owner":{"__typename":"User","name":"','"'));return{photo_url:A(v(r,',"image":{"uri":"','","')),photo_id:o,username:t}}async function I(r,o){try{let t={headers:{"User-Agent":T,Accept:"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8","Accept-Language":"en-US,en;q=0.9","Sec-Fetch-Dest":"document","Sec-Fetch-Mode":"navigate","Sec-Fetch-Site":"none",...o.headers}};if(o.timeout!==void 0)t.timeout=o.timeout;let i=await(await k(r,t)).text(),e=i.includes("\\\"video_id\\\":\\\""),n=[];if(e){let{video_urls:d,audio_url:m,video_id:p,username:g}=ne(i);if(Object.keys(d).length===0)throw new c("No video URLs found","facebook");let h=Object.keys(d).map((f)=>Number.parseInt(f.replace(/\D/g,""),10)||0).reduce((f,U)=>Math.max(f,U),0),R=Object.keys(d).find((f)=>f.includes(String(h))),_=R?d[R]:Object.values(d)[0];if(!_)throw new c("Failed to select video URL","facebook");if(n.push({type:"video",url:_,filename:`fb-${p||Date.now()}.mp4`}),m)n.push({type:"audio",url:m,filename:`fb-${p||Date.now()}.m4a`});return{urls:n,headers:{"User-Agent":T},meta:{title:"Facebook Video",author:g||"Unknown",platform:"facebook"}}}let{photo_url:a,photo_id:u,username:l}=re(i);if(!a)throw new c("No photo URL found","facebook");return{urls:[{type:"image",url:a,filename:`fb-${u||Date.now()}.jpg`}],headers:{"User-Agent":T},meta:{title:"Facebook Photo",author:l||"Unknown",platform:"facebook"}}}catch(t){if(t instanceof w||t instanceof c)throw t;let s=t instanceof Error?t.message:"Unknown error";throw new c(s,"facebook")}}import oe from"node:path";var se="https://api.vxtwitter.com";async function O(r,o){try{let t=new URL(r),s=`${se}${t.pathname}`,e=await(await k(s,o)).json();if(!e?.media_extended||e.media_extended.length===0)throw new c("No media found in tweet","twitter");let n=e.media_extended.map((u,l)=>{let d=oe.extname(new URL(u.url).pathname)||".mp4";return{type:u.type==="video"||u.type==="gif"?"video":"image",url:u.url,filename:`twitter-${e.tweetID}-${l+1}${d}`}}),a={title:e.text||"Twitter post",author:`${e.user_name} (@${e.user_screen_name})`,platform:"twitter"};if(e.likes!==void 0)a.likes=e.likes;if(e.views!==void 0)a.views=e.views;if(e.replies!==void 0)a.comments=e.replies;if(e.retweets!==void 0)a.reposts=e.retweets;if(e.date_epoch!==void 0)a.timestamp=e.date_epoch;return{urls:n,headers:{},meta:a}}catch(t){if(t instanceof w||t instanceof c)throw t;let s=t instanceof Error?t.message:"Unknown error";throw new c(s,"twitter")}}var V="AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8",ie=`https://www.youtube.com/youtubei/v1/player?key=${V}`,ae=`https://www.youtube.com/youtubei/v1/next?key=${V}`,ce={clientName:"ANDROID",clientVersion:"21.02.35",androidSdkVersion:30,hl:"en",gl:"US",userAgent:"com.google.android.youtube/21.02.35 (Linux; U; Android 11) gzip"},ue={clientName:"IOS",clientVersion:"20.10.4",deviceModel:"iPhone16,2",hl:"en",gl:"US",userAgent:"com.google.ios.youtube/20.10.4 (iPhone16,2; U; CPU iOS 18_3_2 like Mac OS X)"},X={clientName:"TVHTML5",clientVersion:"7.20240101",hl:"en",gl:"US"};function pe(r){let o=[/(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/shorts\/)([a-zA-Z0-9_-]{11})/,/youtube\.com\/embed\/([a-zA-Z0-9_-]{11})/];for(let t of o){let s=r.match(t);if(s?.[1])return s[1]}return null}function me(r){return r.replace(/[<>:"/\\|?*]/g,"").trim()}function de(r){let o=r.match(/"publishDate":"([^"]+)"/);if(!o?.[1])return;let t=Math.floor(new Date(o[1]).getTime()/1000);return Number.isFinite(t)?t:void 0}function j(r,o){if(typeof r!=="object"||r===null)return;for(let[t,s]of Object.entries(r)){if(t===o)return s;let i=j(s,o);if(i!==void 0)return i}return}function le(r){let o=r.trim().match(/^([\d.]+)\s*([KkMmBb]?)$/);if(!o?.[1])return;let t={k:1000,m:1e6,b:1e9},s=(o[2]??"").toLowerCase();return Math.round(Number.parseFloat(o[1])*(t[s]??1))}function H(r){if(!r?.streamingData)return[];return[...r.streamingData.formats||[],...r.streamingData.adaptiveFormats||[]]}function ge(r,o){let t=[],s=r.filter((n)=>n.url&&n.audioChannels&&n.audioChannels>0&&n.width&&n.width>0).sort((n,a)=>(a.width||0)-(n.width||0))[0];if(s?.url)t.push({type:"video",url:s.url,filename:`youtube-${o}.mp4`});let i=r.filter((n)=>n.url&&n.width&&n.width>0&&(!n.audioChannels||n.audioChannels===0)&&n.mimeType.includes("video/mp4")).sort((n,a)=>(a.width||0)-(n.width||0))[0],e=r.filter((n)=>n.url&&n.audioChannels&&n.audioChannels>0&&(!n.width||n.width===0)&&n.mimeType.includes("audio/mp4")).sort((n,a)=>a.bitrate-n.bitrate)[0];if(i?.url&&i.width&&i.width>(s?.width||0)){let n=i.mimeType.includes("webm")?"webm":"mp4";if(t.push({type:"video",url:i.url,filename:`youtube-${o}-video.${n}`}),e?.url)t.push({type:"audio",url:e.url,filename:`youtube-${o}-audio.m4a`})}if(t.length===0)throw new c("No downloadable formats found","youtube");return t}async function $(r,o){try{let t=pe(r);if(!t)throw new c("Could not extract video ID from URL","youtube");let s=o.timeout??15000,i={"Accept-Language":"en-US,en;q=0.9",...o.headers},e=(y,x)=>b(ie,JSON.stringify({videoId:t,context:{client:y},contentCheckOk:!0,racyCheckOk:!0}),{headers:{...i,"Content-Type":"application/json","User-Agent":y.userAgent,"X-Youtube-Client-Name":x,"X-Youtube-Client-Version":y.clientVersion},timeout:s}),[n,a,u,l]=await Promise.all([e(ce,"3").then((y)=>y.json()),e(ue,"5").then((y)=>y.json()).catch(()=>null),b(ae,JSON.stringify({videoId:t,context:{client:X}}),{headers:{...i,"Content-Type":"application/json","X-Youtube-Client-Name":"7","X-Youtube-Client-Version":X.clientVersion},timeout:s}).then((y)=>y.json()).catch(()=>null),k(`https://www.youtube.com/watch?v=${t}`,{headers:i,timeout:s}).then((y)=>y.text()).catch(()=>null)]);if(n.playabilityStatus?.status!=="OK")throw new c(n.playabilityStatus?.reason||"Video not playable","youtube");if(!n.streamingData)throw new c("No streaming data available","youtube");let d=[...H(n),...H(a)],m=ge(d,t),p=n.videoDetails?.title||"YouTube video",g=n.videoDetails?.author||"Unknown",h=n.videoDetails?.viewCount?Number.parseInt(n.videoDetails.viewCount,10):void 0,R=n.videoDetails?.thumbnail?.thumbnails,_=R?.length?R[R.length-1]?.url:void 0,f={title:me(p),author:g,platform:"youtube"};if(n.videoDetails?.shortDescription)f.description=n.videoDetails.shortDescription;if(_)f.thumbnail=_;if(h!==void 0&&Number.isFinite(h))f.views=h;let U=l?de(l):void 0;if(U!==void 0)f.timestamp=U;if(u){let y=j(u,"likeCount");if(typeof y==="number"&&Number.isFinite(y))f.likes=y;let x=j(u,"commentCount");if(typeof x==="object"&&x!==null){let M=x.simpleText,D=M?le(M):void 0;if(D!==void 0)f.comments=D}}return{urls:m,headers:{},meta:f}}catch(t){if(t instanceof w||t instanceof c)throw t;let s=t instanceof Error?t.message:"Unknown error";throw new c(s,"youtube")}}var we=/\.(mp4|mkv|webm)$/i;async function fe(r){return[...(await k("https://old.reddit.com/",r)).headers.getSetCookie().map((s)=>s.split(";")[0]).filter(Boolean),"over18=1"].join("; ")}async function S(r,o){try{let t=await fe(o),s=`${r.replace(/\/$/,"")}.json`,e=await(await k(s,{...o,headers:{...o.headers,Cookie:t}})).json();if(!(Array.isArray(e)&&e[0]?.data?.children?.[0]?.data))throw new c("Invalid Reddit API response","reddit");let n=e[0].data.children[0].data,a=[];if(n.is_gallery&&n.media_metadata){let l=n.gallery_data?.items||[];for(let d of l){let m=d.media_id,p=n.media_metadata[m];if(p?.s){let g=p.s.u||p.s.gif;if(g)a.push({type:"image",url:g.replace(/&amp;/g,"&"),filename:`reddit-${m}.jpg`})}}}else if(n.is_video&&n.media?.reddit_video?.fallback_url)a.push({type:"video",url:n.media.reddit_video.fallback_url.replace(/&amp;/g,"&"),filename:`reddit-${n.id}.mp4`});else if(n.url_overridden_by_dest||n.url){let l=(n.url_overridden_by_dest||n.url).replace(/&amp;/g,"&"),d=l.match(we);a.push({type:d?"video":"image",url:l,filename:`reddit-${n.id}.${d?"mp4":"jpg"}`})}if(a.length===0)throw new c("No media found in post","reddit");let u={title:n.title,author:n.author,platform:"reddit"};if(typeof n.score==="number"&&Number.isFinite(n.score))u.likes=n.score;if(typeof n.view_count==="number"&&Number.isFinite(n.view_count))u.views=n.view_count;return{urls:a,headers:{},meta:u}}catch(t){if(t instanceof w||t instanceof c)throw t;let s=t instanceof Error?t.message:"Unknown error";throw new c(s,"reddit")}}import he from"node:path";var _e="546c25a59c58ad7",Re=/^[a-zA-Z0-9]+$/;async function L(r,o){try{let s=new URL(r).pathname.split("/").filter(Boolean),i=s[s.length-1];if(!i)throw new c("Invalid Imgur URL: no path","imgur");let e=i.split(".")[0];if(!e)throw new c("Invalid Imgur URL: no ID found","imgur");let n=s.includes("gallery")||s.includes("a");if(n&&e.includes("-")){let h=e.split("-"),R=h[h.length-1];if(R&&R.length>=5&&Re.test(R))e=R}let u=`https://api.imgur.com/3/${n?"album":"image"}/${e}?client_id=${_e}`,m=(await(await k(u,o)).json())?.data;if(!m)throw new c("Imgur API returned no data","imgur");let p=m.images||[m],g=[];for(let h of p)if(h.link){let R=he.extname(h.link)||".jpg";g.push({type:h.type?.startsWith("video")?"video":"image",url:h.link,filename:`imgur-${h.id}${R}`})}if(g.length===0)throw new c("No media links found","imgur");return{urls:g,headers:{},meta:{title:m.title||"Imgur Media",author:m.account_url||"Unknown",platform:"imgur",views:m.views,likes:(m.ups||0)-(m.downs||0)}}}catch(t){if(t instanceof w||t instanceof c)throw t;let s=t instanceof Error?t.message:"Unknown error";throw new c(s,"imgur")}}var ye=/\/pin\/(?:[\w-]+--)?(\d+)/,B=["V_720P","V_EXP7","V_EXP6","V_EXP5","V_EXP4"];async function N(r,o){let t=r.match(ye);if(!t?.[1])throw new c("Could not extract pin ID from URL","pinterest");let s=t[1];try{let i="https://www.pinterest.com/resource/PinResource/get/?data="+encodeURIComponent(JSON.stringify({options:{field_set_key:"unauth_react_main_pin",id:s}})),e={headers:{"X-Pinterest-PWS-Handler":"www/[username].js",...o.headers}};if(o.timeout!==void 0)e.timeout=o.timeout;let u=(await(await k(i,e)).json()).resource_response?.data;if(!u)throw new c("Invalid Pinterest API response","pinterest");let l=u.title||u.grid_title||"Pinterest Pin",d=u.closeup_attribution?.full_name||u.pinner?.full_name||u.pinner?.username||"Unknown",m=u.story_pin_data?.pages;if(m)for(let _ of m){let f=_.blocks;if(!f)continue;for(let U of f){let y=U.video?.video_list;if(!y)continue;for(let x of B){let M=y[x];if(M?.url&&!M.url.endsWith(".m3u8"))return{urls:[{type:"video",url:M.url,filename:`pinterest-${s}.mp4`}],headers:{},meta:{title:l,author:d,platform:"pinterest"}}}}}let p=u.videos?.video_list;if(p)for(let _ of B){let f=p[_];if(f?.url&&!f.url.endsWith(".m3u8"))return{urls:[{type:"video",url:f.url,filename:`pinterest-${s}.mp4`}],headers:{},meta:{title:l,author:d,platform:"pinterest"}}}let g=u.images;if(!g)throw new c("No media found in pin","pinterest");let h=g.orig?.url;if(!h){let _=0;for(let f of Object.values(g))if(f.width>_&&f.url)_=f.width,h=f.url}if(!h)throw new c("No image URL found","pinterest");let R=h.split(".").pop()?.split("?")[0]||"jpg";return{urls:[{type:"image",url:h,filename:`pinterest-${s}.${R}`}],headers:{},meta:{title:l,author:d,platform:"pinterest"}}}catch(i){if(i instanceof w||i instanceof c)throw i;let e=i instanceof Error?i.message:"Unknown error";throw new c(e,"pinterest")}}var ke=new Map([["instagram.com",C],["tiktok.com",E],["facebook.com",I],["fb.com",I],["twitter.com",O],["x.com",O],["youtube.com",$],["youtu.be",$],["reddit.com",S],["redd.it",S],["imgur.com",L],["i.imgur.com",L],["pinterest.com",N]]);function Q(r){let o=new URL(r).hostname.replace(/^www\./,""),t=ke.get(o);if(t)return t;if(o.endsWith(".pinterest.com"))return N;return null}async function ve(r,o={}){let t=Q(r);if(!t)throw new P(r);return t(r,o)}export{ve as resolve,P as PlatformNotSupportedError,c as ParseError,w as NetworkError};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@totallynotdavid/downloader",
3
- "version": "1.0.2",
3
+ "version": "1.1.0",
4
4
  "author": "David Duran <dadch1404@gmail.com>",
5
5
  "repository": {
6
6
  "type": "git",
@@ -8,9 +8,9 @@
8
8
  },
9
9
  "main": "dist/index.js",
10
10
  "devDependencies": {
11
- "@types/bun": "^1.3.11",
12
- "@types/node": "^25.5.0",
13
- "bunup": "^0.16.31",
11
+ "@types/bun": "^1.3.14",
12
+ "@types/node": "^25.9.3",
13
+ "bunup": "^0.16.32",
14
14
  "shx": "^0.4.0"
15
15
  },
16
16
  "bugs": {
@@ -36,9 +36,15 @@
36
36
  "license": "MIT",
37
37
  "scripts": {
38
38
  "clean": "bun x shx rm -rf dist",
39
- "check": "biome format --write . && biome check --write . && bun x prettier \"**/*.{md,yml}\" --write --print-width=80 --prose-wrap=always",
39
+ "format": "biome format --write . && bun x prettier \"**/*.{md,yml}\" --write --print-width=80 --prose-wrap=always",
40
+ "lint": "biome check .",
41
+ "typecheck": "bun x tsc -p tsconfig.json --noEmit",
42
+ "check": "bun run lint && bun run typecheck",
40
43
  "build": "bun run check && bun x bunup",
41
- "test": "bun test",
44
+ "test": "bun test tests/replay",
45
+ "test:update": "bun test tests/replay --update-snapshots",
46
+ "eval": "bun scripts/eval.ts",
47
+ "record": "bun --env-file=.env scripts/record.ts",
42
48
  "prepublishOnly": "bun run clean && bun run build"
43
49
  },
44
50
  "type": "module",