@totallynotdavid/downloader 1.0.0 → 1.0.1

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/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2024 David Duran
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2024 David Duran
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/dist/index.d.ts CHANGED
@@ -23,11 +23,11 @@ declare class PlatformNotSupportedError extends Error {
23
23
  constructor(url: string);
24
24
  }
25
25
  declare class NetworkError extends Error {
26
- statusCode?: number | undefined;
27
- constructor(message: string, statusCode?: number | undefined);
26
+ readonly statusCode?: number;
27
+ constructor(message: string, statusCode?: number);
28
28
  }
29
29
  declare class ParseError extends Error {
30
- platform: string;
30
+ readonly platform: string;
31
31
  constructor(message: string, platform: string);
32
32
  }
33
33
  export { resolve, ResolveOptions, PlatformNotSupportedError, ParseError, NetworkError, MediaResult, MediaItem };
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,i){super(o);this.statusCode=i;this.name="NetworkError"}}class r extends Error{platform;constructor(o,i){super(`[${i}] ${o}`);this.platform=i;this.name="ParseError"}}var $="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36",L=1e4;async function y(o,i={}){let e=new AbortController,n=setTimeout(()=>e.abort(),i.timeout??L);try{let s=await fetch(o,{headers:{"User-Agent":$,"Accept-Language":"en-US,en;q=0.9",...i.headers},signal:e.signal});if(!s.ok)throw new p(`HTTP ${s.status}: ${s.statusText}`,s.status);return s}catch(s){if(s.name==="AbortError")throw new p("Request timeout",408);if(s instanceof p)throw s;throw new p(s.message)}finally{clearTimeout(n)}}async function k(o,i,e={}){let n=new AbortController,s=setTimeout(()=>n.abort(),e.timeout??L);try{let t=await fetch(o,{method:"POST",headers:{"User-Agent":$,"Content-Type":i instanceof URLSearchParams?"application/x-www-form-urlencoded":"application/json",...e.headers},body:i.toString(),signal:n.signal});if(!t.ok)throw new p(`HTTP ${t.status}: ${t.statusText}`,t.status);return t}catch(t){if(t.name==="AbortError")throw new p("Request timeout",408);if(t instanceof p)throw t;throw new p(t.message)}finally{clearTimeout(s)}}var V="936619743392459",W="8845758582119845",D="Instagram 309.0.0.15.109 Android (31/12; 480dpi; 1080x2228; samsung; SM-G996B; t2s; qcom; en_US; 544099989)",H=/(?:p|reel|tv)\/([A-Za-z0-9_-]+)/;function j(o,i,e){let n=o.is_video,s=n?o.video_url:o.display_url;if(!s)return null;let t=e>0?`-${e}`:"";return{type:n?"video":"image",url:s,filename:`instagram-${i}${t}.${n?"mp4":"jpg"}`}}function z(o,i){let e=o.__typename;if(e==="GraphSidecar"||e==="XDTGraphSidecar")return(o.edge_sidecar_to_children?.edges||[]).map((m,l)=>m?.node&&j(m.node,i,l+1)).filter((m)=>m!==null);let s=j(o,i,0);return s?[s]:[]}async function O(o,i){let e=o.match(H);if(!e?.[1])throw new r("Could not parse post shortcode","instagram");let n=e[1],s=new URLSearchParams({doc_id:W,variables:JSON.stringify({shortcode:n})});try{let l=(await(await k("https://www.instagram.com/graphql/query",s,{headers:{"User-Agent":D,"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",...i.headers},timeout:i.timeout})).json())?.data?.xdt_shortcode_media;if(!l)throw new r("No media data found","instagram");let a=z(l,n);if(a.length===0)throw new r("No media found","instagram");let d=l.edge_media_to_caption?.edges?.[0]?.node?.text,f=l.owner?.username;return{urls:a,headers:{"User-Agent":D,Referer:"https://www.instagram.com/"},meta:{platform:"instagram",title:d||"Instagram post",author:f||"Unknown"}}}catch(t){if(t instanceof p||t instanceof r)throw t;throw new r(t.message,"instagram")}}var B=/<script id="__UNIVERSAL_DATA_FOR_REHYDRATION__" type="application\/json">([^<]+)<\/script>/;async function T(o,i){let e=o.replace("/photo/","/video/");try{let t=(await(await y(e,i)).text()).match(B);if(!t?.[1])throw new r("Could not find hydration data","tiktok");let l=JSON.parse(t[1]).__DEFAULT_SCOPE__?.["webapp.video-detail"],a=l?.itemInfo?.itemStruct;if(!a)throw new r("Metadata not found","tiktok");let d=[];if(a.imagePost?.images){if(a.imagePost.images.forEach((f,c)=>{let w=f.imageURL?.urlList?.[0];if(w)d.push({type:"image",url:w,filename:`tiktok-${a.id}-${c+1}.jpg`})}),a.music?.playUrl)d.push({type:"audio",url:a.music.playUrl,filename:`tiktok-${a.id}-audio.mp3`})}else if(a.video){let f=a.video.bitrateInfo||[],c=f.length>0?f[0].PlayAddr.UrlList[0]:a.video.playAddr;if(c)d.push({type:"video",url:c,filename:`tiktok-${a.id}.mp4`})}if(d.length===0)throw new r("No media content found","tiktok");return{urls:d,headers:{Referer:"https://www.tiktok.com/"},meta:{title:l.shareMeta?.desc||a.desc||"TikTok Post",author:a.author?.nickname||a.author?.uniqueId||"Unknown",platform:"tiktok",likes:a.stats?.diggCount,views:a.stats?.playCount}}}catch(n){if(n instanceof p||n instanceof r)throw n;throw new r(n.message,"tiktok")}}var C="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36";function b(o){try{return o.replace(/\\u([\dA-Fa-f]{4})/g,(i,e)=>String.fromCharCode(Number.parseInt(e,16))).replace(/\\\//g,"/")}catch{return o}}function v(o,i,e){let n=o.indexOf(i);if(n===-1)return"";let s=n+i.length,t=o.indexOf(e,s);if(t===-1)return"";return o.slice(s,t)}function Y(o){let i=v(o,"\\\"video_id\\\":\\\"","\\\""),e=b(v(o,'"actors":[{"__typename":"User","name":"','","')),n=v(o,'"permalink_url"',"\\/Period>\\u003C\\/MPD>"),s=v(n,"AudioChannelConfiguration","BaseURL>\\u003C"),t=b(v(s,"BaseURL>","\\u003C\\/")),m={},l=n.split("FBQualityLabel=\\\"");for(let a=1;a<l.length;a++){let d=l[a];if(!d)continue;let f=d.split('"',1)[0];if(!f)continue;let c=d.split("BaseURL>",2)[1];if(!c)continue;let w=c.split("\\u003C\\/BaseURL>",1)[0];if(!w)continue;let u=b(w);if(u)m[f]=u}return{video_urls:m,audio_url:t,video_id:i,username:e}}function K(o){let i=v(o,'"__isNode":"Photo","id":"','"'),e=b(v(o,'"owner":{"__typename":"User","name":"','"'));return{photo_url:b(v(o,',"image":{"uri":"','","')),photo_id:i,username:e}}async function E(o,i){try{let n=await(await y(o,{headers:{"User-Agent":C,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",...i.headers},timeout:i.timeout})).text(),s=n.includes("\\\"video_id\\\":\\\""),t=[];if(s){let{video_urls:d,audio_url:f,video_id:c,username:w}=Y(n);if(Object.keys(d).length===0)throw new r("No video URLs found","facebook");let u=Object.keys(d).map((h)=>Number.parseInt(h.replace(/\D/g,""),10)||0).reduce((h,U)=>Math.max(h,U),0),g=Object.keys(d).find((h)=>h.includes(String(u))),_=g?d[g]:Object.values(d)[0];if(!_)throw new r("Failed to select video URL","facebook");if(t.push({type:"video",url:_,filename:`fb-${c||Date.now()}.mp4`}),f)t.push({type:"audio",url:f,filename:`fb-${c||Date.now()}.m4a`});return{urls:t,headers:{"User-Agent":C},meta:{title:"Facebook Video",author:w||"Unknown",platform:"facebook"}}}let{photo_url:m,photo_id:l,username:a}=K(n);if(!m)throw new r("No photo URL found","facebook");return{urls:[{type:"image",url:m,filename:`fb-${l||Date.now()}.jpg`}],headers:{"User-Agent":C},meta:{title:"Facebook Photo",author:a||"Unknown",platform:"facebook"}}}catch(e){if(e instanceof p||e instanceof r)throw e;throw new r(e.message,"facebook")}}import Z from"node:path";var J="https://api.vxtwitter.com";async function x(o,i){try{let e=new URL(o),n=`${J}${e.pathname}`,t=await(await y(n,i)).json();if(!t?.media_extended||t.media_extended.length===0)throw new r("No media found in tweet","twitter");return{urls:t.media_extended.map((l,a)=>{let d=Z.extname(new URL(l.url).pathname)||".mp4";return{type:l.type==="video"||l.type==="gif"?"video":"image",url:l.url,filename:`twitter-${t.tweetID}-${a}${d}`}}),headers:{},meta:{title:t.text||"Twitter post",author:`${t.user_name} (@${t.user_screen_name})`,platform:"twitter",likes:t.likes,views:t.views}}}catch(e){if(e instanceof p||e instanceof r)throw e;throw new r(e.message,"twitter")}}var Q="https://www.youtube.com/youtubei/v1/player?key=AIzaSyA8eiZmM1FaDVjRy-df2KTyQ_vz_yYM39w",F={clientName:"ANDROID",clientVersion:"19.09.37",androidSdkVersion:30,hl:"en",gl:"US",timeZone:"UTC",utcOffsetMinutes:0},tt="com.google.android.youtube/19.09.37 (Linux; U; Android 11) gzip";function et(o){let i=[/(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/shorts\/)([a-zA-Z0-9_-]{11})/,/youtube\.com\/embed\/([a-zA-Z0-9_-]{11})/];for(let e of i){let n=o.match(e);if(n?.[1])return n[1]}return null}function ot(o){return o.replace(/[<>:"/\\|?*]/g,"").trim()}async function A(o,i){try{let e=et(o);if(!e)throw new r("Could not extract video ID from URL","youtube");let t=await(await k(Q,JSON.stringify({videoId:e,context:{client:F},contentCheckOk:!0,racyCheckOk:!0}),{headers:{"Content-Type":"application/json","User-Agent":tt,"X-Youtube-Client-Name":"3","X-Youtube-Client-Version":F.clientVersion,...i.headers},timeout:i.timeout??15000})).json();if(t.playabilityStatus?.status!=="OK")throw new r(t.playabilityStatus?.reason||"Video not playable","youtube");if(!t.streamingData)throw new r("No streaming data available","youtube");let m=[...t.streamingData.formats||[],...t.streamingData.adaptiveFormats||[]],l=[],a=m.filter((u)=>u.url&&u.audioChannels&&u.audioChannels>0&&u.width&&u.width>0).sort((u,g)=>(g.width||0)-(u.width||0))[0];if(a?.url)l.push({type:"video",url:a.url,filename:`youtube-${e}.mp4`});let d=m.filter((u)=>u.url&&u.width&&u.width>0&&(!u.audioChannels||u.audioChannels===0)&&u.mimeType.includes("video/mp4")).sort((u,g)=>(g.width||0)-(u.width||0))[0],f=m.filter((u)=>u.url&&u.audioChannels&&u.audioChannels>0&&(!u.width||u.width===0)&&u.mimeType.includes("audio/mp4")).sort((u,g)=>g.bitrate-u.bitrate)[0];if(d?.url&&d.width&&d.width>(a?.width||0)){let u=d.mimeType.includes("webm")?"webm":"mp4";if(l.push({type:"video",url:d.url,filename:`youtube-${e}-video.${u}`}),f?.url)l.push({type:"audio",url:f.url,filename:`youtube-${e}-audio.m4a`})}if(l.length===0)throw new r("No downloadable formats found","youtube");let c=t.videoDetails?.title||"YouTube video",w=t.videoDetails?.author||"Unknown";return{urls:l,headers:{},meta:{title:ot(c),author:w,platform:"youtube",views:t.videoDetails?.viewCount?Number.parseInt(t.videoDetails.viewCount,10):void 0}}}catch(e){if(e instanceof p||e instanceof r)throw e;throw new r(e.message,"youtube")}}var rt=/\.(mp4|mkv|webm)$/i;async function I(o,i){try{let e=`${o.replace(/\/$/,"")}.json`,s=await(await y(e,i)).json();if(!(Array.isArray(s)&&s[0]?.data?.children?.[0]?.data))throw new r("Invalid Reddit API response","reddit");let t=s[0].data.children[0].data,m=[];if(t.is_gallery&&t.media_metadata){let l=t.gallery_data?.items||[];for(let a of l){let d=a.media_id,f=t.media_metadata[d];if(f?.s){let c=f.s.u||f.s.gif;if(c)m.push({type:"image",url:c.replace(/&amp;/g,"&"),filename:`reddit-${d}.jpg`})}}}else if(t.is_video&&t.media?.reddit_video?.fallback_url)m.push({type:"video",url:t.media.reddit_video.fallback_url.replace(/&amp;/g,"&"),filename:`reddit-${t.id}.mp4`});else if(t.url_overridden_by_dest||t.url){let l=(t.url_overridden_by_dest||t.url).replace(/&amp;/g,"&"),a=l.match(rt);m.push({type:a?"video":"image",url:l,filename:`reddit-${t.id}.${a?"mp4":"jpg"}`})}if(m.length===0)throw new r("No media found in post","reddit");return{urls:m,headers:{},meta:{title:t.title,author:t.author,platform:"reddit",likes:t.score,views:t.view_count}}}catch(e){if(e instanceof p||e instanceof r)throw e;throw new r(e.message,"reddit")}}import it from"node:path";var nt="546c25a59c58ad7",st=/^[a-zA-Z0-9]+$/;async function P(o,i){try{let n=new URL(o).pathname.split("/").filter(Boolean),s=n[n.length-1];if(!s)throw new r("Invalid Imgur URL: no path","imgur");let t=s.split(".")[0];if(!t)throw new r("Invalid Imgur URL: no ID found","imgur");let m=n.includes("gallery")||n.includes("a");if(m&&t.includes("-")){let g=t.split("-"),_=g[g.length-1];if(_&&_.length>=5&&st.test(_))t=_}let a=`https://api.imgur.com/3/${m?"album":"image"}/${t}?client_id=${nt}`,c=(await(await y(a,i)).json())?.data;if(!c)throw new r("Imgur API returned no data","imgur");let w=c.images||[c],u=[];for(let g of w)if(g.link){let _=it.extname(g.link)||".jpg";u.push({type:g.type?.startsWith("video")?"video":"image",url:g.link,filename:`imgur-${g.id}${_}`})}if(u.length===0)throw new r("No media links found","imgur");return{urls:u,headers:{},meta:{title:c.title||"Imgur Media",author:c.account_url||"Unknown",platform:"imgur",views:c.views,likes:(c.ups||0)-(c.downs||0)}}}catch(e){if(e instanceof p||e instanceof r)throw e;throw new r(e.message,"imgur")}}var at=/\/pin\/(?:[\w-]+--)?(\d+)/,X=["V_720P","V_EXP7","V_EXP6","V_EXP5","V_EXP4"];async function N(o,i){let e=o.match(at);if(!e?.[1])throw new r("Could not extract pin ID from URL","pinterest");let n=e[1];try{let s="https://www.pinterest.com/resource/PinResource/get/?data="+encodeURIComponent(JSON.stringify({options:{field_set_key:"unauth_react_main_pin",id:n}})),l=(await(await y(s,{headers:{"X-Pinterest-PWS-Handler":"www/[username].js",...i.headers},timeout:i.timeout})).json()).resource_response?.data;if(!l)throw new r("Invalid Pinterest API response","pinterest");let a=l.title||l.grid_title||"Pinterest Pin",d=l.closeup_attribution?.full_name||l.pinner?.full_name||l.pinner?.username||"Unknown",f=l.story_pin_data?.pages;if(f)for(let _ of f){let h=_.blocks;if(!h)continue;for(let U of h){let S=U.video?.video_list;if(!S)continue;for(let q of X){let M=S[q];if(M?.url&&!M.url.endsWith(".m3u8"))return{urls:[{type:"video",url:M.url,filename:`pinterest-${n}.mp4`}],headers:{},meta:{title:a,author:d,platform:"pinterest"}}}}}let c=l.videos?.video_list;if(c)for(let _ of X){let h=c[_];if(h?.url&&!h.url.endsWith(".m3u8"))return{urls:[{type:"video",url:h.url,filename:`pinterest-${n}.mp4`}],headers:{},meta:{title:a,author:d,platform:"pinterest"}}}let w=l.images;if(!w)throw new r("No media found in pin","pinterest");let u=w.orig?.url;if(!u){let _=0;for(let h of Object.values(w))if(h.width>_&&h.url)_=h.width,u=h.url}if(!u)throw new r("No image URL found","pinterest");let g=u.split(".").pop()?.split("?")[0]||"jpg";return{urls:[{type:"image",url:u,filename:`pinterest-${n}.${g}`}],headers:{},meta:{title:a,author:d,platform:"pinterest"}}}catch(s){if(s instanceof p||s instanceof r)throw s;throw new r(s.message,"pinterest")}}var ut=new Map([["instagram.com",O],["tiktok.com",T],["facebook.com",E],["fb.com",E],["twitter.com",x],["x.com",x],["youtube.com",A],["youtu.be",A],["reddit.com",I],["redd.it",I],["imgur.com",P],["i.imgur.com",P],["pinterest.com",N]]);function G(o){let i=new URL(o).hostname.replace(/^www\./,""),e=ut.get(i);if(e)return e;if(i.endsWith(".pinterest.com"))return N;return null}async function lt(o,i={}){let e=G(o);if(!e)throw new R(o);return e(o,i)}export{lt as resolve,R as PlatformNotSupportedError,r as ParseError,p as NetworkError};
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};
package/package.json CHANGED
@@ -1,47 +1,46 @@
1
1
  {
2
2
  "name": "@totallynotdavid/downloader",
3
- "version": "1.0.0",
4
- "description": "Get direct download URLs from YouTube, Instagram, TikTok, X, and more.",
5
- "keywords": [
6
- "imgur",
7
- "reddit",
8
- "instagram",
9
- "facebook",
10
- "twitter",
11
- "pinterest",
12
- "tiktok",
13
- "downloader"
14
- ],
15
- "license": "MIT",
3
+ "version": "1.0.1",
16
4
  "author": "David Duran <dadch1404@gmail.com>",
17
5
  "repository": {
18
6
  "type": "git",
19
7
  "url": "git+https://github.com/totallynotdavid/downloader.git"
20
8
  },
9
+ "main": "dist/index.js",
10
+ "devDependencies": {
11
+ "@types/bun": "^1.3.11",
12
+ "@types/node": "^25.5.0",
13
+ "bunup": "^0.16.31",
14
+ "shx": "^0.4.0"
15
+ },
21
16
  "bugs": {
22
17
  "url": "https://github.com/totallynotdavid/downloader/issues"
23
18
  },
24
- "homepage": "https://github.com/totallynotdavid/downloader#readme",
25
- "type": "module",
26
- "main": "dist/index.js",
27
- "types": "dist/index.d.ts",
19
+ "description": "Get direct download URLs from YouTube, Instagram, TikTok, X, and more.",
28
20
  "files": [
29
21
  "dist",
30
22
  "README.md",
31
23
  "LICENSE"
32
24
  ],
25
+ "homepage": "https://github.com/totallynotdavid/downloader#readme",
26
+ "keywords": [
27
+ "imgur",
28
+ "reddit",
29
+ "instagram",
30
+ "facebook",
31
+ "twitter",
32
+ "pinterest",
33
+ "tiktok",
34
+ "downloader"
35
+ ],
36
+ "license": "MIT",
33
37
  "scripts": {
34
- "clean": "shx rm -rf dist",
35
- "format": "biome format --write . && biome check --write . && bun x prettier \"**/*.{md,yml}\" --write --print-width=80 --prose-wrap=always",
36
- "build": "bun run format && bun x bunup",
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",
40
+ "build": "bun run check && bun x bunup",
37
41
  "test": "bun test",
38
42
  "prepublishOnly": "bun run clean && bun run build"
39
43
  },
40
- "dependencies": {
41
- "cheerio": "1.1.2"
42
- },
43
- "devDependencies": {
44
- "@types/bun": "^1.3.3",
45
- "shx": "^0.4.0"
46
- }
44
+ "type": "module",
45
+ "types": "dist/index.d.ts"
47
46
  }
package/readme.md CHANGED
@@ -1,8 +1,7 @@
1
1
  # [pkg]: @totallynotdavid/downloader
2
2
 
3
- [![npm version](https://img.shields.io/npm/v/@totallynotdavid/downloader.svg)](https://www.npmjs.com/package/@totallynotdavid/downloader)
4
- [![CodeQL](https://github.com/totallynotdavid/downloader/actions/workflows/codeql.yml/badge.svg)](https://github.com/totallynotdavid/downloader/actions/workflows/codeql.yml)
5
- [![Biome CI](https://github.com/totallynotdavid/downloader/actions/workflows/quality.yml/badge.svg)](https://github.com/totallynotdavid/downloader/actions/workflows/quality.yml)
3
+ [![NPM Version](https://img.shields.io/npm/v/@totallynotdavid/downloader?logo=npm&logoColor=212121&label=version&labelColor=ffc44e&color=212121)](https://www.npmjs.com/package/@totallynotdavid/downloader)
4
+ [![codecov](https://codecov.io/gh/totallynotdavid/downloader/graph/badge.svg?token=8OBBAZG8MN)](https://codecov.io/gh/totallynotdavid/downloader)
6
5
 
7
6
  Direct URLs from social posts. Skip reverse-engineering and heavy tools.
8
7
  Supports Instagram, TikTok, Twitter/X, YouTube, Reddit, Facebook, Imgur, and