umedia 0.4.1 → 0.4.2

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.
Files changed (2) hide show
  1. package/lib/registry.js +52 -1
  2. package/package.json +1 -1
package/lib/registry.js CHANGED
@@ -31,6 +31,49 @@ export function detectPlatform(url) {
31
31
 
32
32
  const DIRECT_MEDIA = /\.(mp4|webm|m4a|mp3|aac|wav|jpg|jpeg|png|gif|webp|mov|mkv)(\?|#|$)/i;
33
33
 
34
+ /** Signed CDN blob URLs have no file extension — probe the content-type honestly. */
35
+ async function probeDirectMedia(url) {
36
+ try {
37
+ let res = await fetch(url, {
38
+ method: "HEAD",
39
+ headers: { "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0 Safari/537.36" },
40
+ redirect: "follow",
41
+ signal: AbortSignal.timeout(12_000),
42
+ });
43
+ if (!res.ok && res.status !== 405) {
44
+ res = await fetch(url, {
45
+ headers: { "user-agent": "Mozilla/5.0", range: "bytes=0-0" },
46
+ redirect: "follow",
47
+ signal: AbortSignal.timeout(12_000),
48
+ });
49
+ }
50
+ const ct = (res.headers.get("content-type") || "").split(";")[0].trim();
51
+ const kind = /^video\//.test(ct) ? "video" : /^audio\//.test(ct) ? "audio" : /^image\//.test(ct) ? "image" : null;
52
+ return kind ? { kind, mimeType: ct } : null;
53
+ } catch {
54
+ return null;
55
+ }
56
+ }
57
+
58
+ function directResult(url, kind, mimeType, note) {
59
+ return {
60
+ data: {
61
+ sourceUrl: url, platform: "direct",
62
+ title: String(url).split("/").pop()?.split("?")[0] || "Direct media",
63
+ author: null, thumbnail: kind === "video" ? null : url,
64
+ itemCount: 1,
65
+ counts: { images: kind === "image" ? 1 : 0, videos: kind === "video" ? 1 : 0, audios: kind === "audio" ? 1 : 0, other: 0 },
66
+ truncated: false,
67
+ media: [{
68
+ type: kind, index: 0, url: String(url), thumbnail: null, mimeType: mimeType ?? null,
69
+ width: null, height: null, duration: null, size: null, quality: "original",
70
+ hasAudio: kind !== "image", hasVideo: kind !== "audio", source: "direct", variants: [],
71
+ }],
72
+ },
73
+ engine: { attempts: [{ provider: "direct", status: "success", note }], finalProvider: "direct", totalLatencyMs: 0 },
74
+ };
75
+ }
76
+
34
77
  /** Resolve a URL: native adapter first, optional yt-dlp fallback. attempts[] is the honest trail. */
35
78
  export async function resolve(url) {
36
79
  // SSRF hygiene FIRST — never dispatch or fetch private/internal targets (bots pass user URLs!)
@@ -88,6 +131,14 @@ export async function resolve(url) {
88
131
  }
89
132
  }
90
133
  const last = ctx.attempts[ctx.attempts.length - 1];
134
+ // extension-less signed CDN URLs: honest content-type probe before refusing
135
+ if (detectPlatform(url) === "unknown") {
136
+ const probe = await probeDirectMedia(url);
137
+ if (probe) return directResult(url, probe.kind, probe.mimeType, "direct media URL (content-type probe)");
138
+ throw new UMediaError(Codes.INVALID_URL,
139
+ `No provider could resolve this URL${last?.error ? " Last error: " + last.error : ""}. Tip: install yt-dlp to unlock 1800+ sites and hard networks.`,
140
+ { attempts: ctx.attempts });
141
+ }
91
142
  // surface the real provider error when every matching provider failed —
92
143
  // never mask a typed outcome behind a generic message
93
144
  if (lastTyped) {
@@ -95,7 +146,7 @@ export async function resolve(url) {
95
146
  throw lastTyped;
96
147
  }
97
148
  throw new UMediaError(
98
- detectPlatform(url) === "unknown" ? Codes.INVALID_URL : Codes.PROVIDER_UNAVAILABLE,
149
+ Codes.PROVIDER_UNAVAILABLE,
99
150
  `No provider could resolve this URL.${last?.error ? " Last error: " + last.error : ""}` +
100
151
  (ytdlp.binary() ? "" : " Tip: install yt-dlp to unlock 1800+ sites and hard networks."),
101
152
  { attempts: ctx.attempts },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "umedia",
3
- "version": "0.4.1",
3
+ "version": "0.4.2",
4
4
  "description": "Standalone media engine — search, resolve and download media from YouTube, TikTok, Instagram, Reddit, X, iTunes (and 1800+ sites via the yt-dlp tier) entirely on your machine. No hosted API, no keys, no server bills. Honest quality labels, complete galleries, typed errors.",
5
5
  "type": "module",
6
6
  "main": "./index.js",