@herowcode/utils 1.1.2 → 1.1.3

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/README.md CHANGED
@@ -337,11 +337,24 @@ function VideoComponent() {
337
337
  #### `validateYoutubeLink(videoUrl: string): Promise<boolean>`
338
338
  Checks whether a YouTube video exists by probing thumbnails and falling back to the oEmbed endpoint. Returns `true` for found/public videos and `false` otherwise.
339
339
 
340
- ```typescript
340
+ ```ts
341
341
  const ok = await validateYoutubeLink('https://youtu.be/dQw4w9WgXcQ');
342
342
  // true | false
343
343
  ```
344
344
 
345
+ #### `getYoutubeThumbnail(videoUrl: string): Promise<string | null>`
346
+ Attempts to load YouTube thumbnail images in priority order (maxresdefault, hqdefault, mqdefault, default). It creates an Image in the browser and returns the first URL that successfully loads, or `null` if none are available or the video ID cannot be extracted.
347
+
348
+ Example:
349
+ ```ts
350
+ const thumb = await getYoutubeThumbnail('https://youtu.be/abc123');
351
+ // e.g. "https://img.youtube.com/vi/abc123/hqdefault.jpg" or null
352
+ ```
353
+
354
+ Notes:
355
+ - Uses the browser Image load/error events to avoid CORS issues.
356
+ - Returns `null` when the video ID cannot be extracted or no thumbnails load.
357
+
345
358
  ## Browser Support
346
359
 
347
360
  This library supports all modern browsers and Node.js environments. It uses ES2018 features and requires:
@@ -0,0 +1,2 @@
1
+ export declare function getYoutubeThumbnail(videoUrl: string): Promise<string | null>;
2
+ //# sourceMappingURL=get-youtube-thumbnail.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-youtube-thumbnail.d.ts","sourceRoot":"","sources":["../../src/youtube/get-youtube-thumbnail.ts"],"names":[],"mappings":"AAAA,wBAAsB,mBAAmB,CACvC,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAgCxB"}
@@ -0,0 +1,31 @@
1
+ export async function getYoutubeThumbnail(videoUrl) {
2
+ const { extractYouTubeId } = await import("./extract-youtube-video-id");
3
+ const videoId = extractYouTubeId(videoUrl);
4
+ if (!videoId)
5
+ return null;
6
+ // Try loading YouTube thumbnail images — avoids CORS problems because
7
+ // creating an Image and listening for load/error is not blocked by CORS.
8
+ const thumbs = [
9
+ `https://img.youtube.com/vi/${videoId}/maxresdefault.jpg`,
10
+ `https://img.youtube.com/vi/${videoId}/hqdefault.jpg`,
11
+ `https://img.youtube.com/vi/${videoId}/mqdefault.jpg`,
12
+ `https://img.youtube.com/vi/${videoId}/default.jpg`,
13
+ ];
14
+ const loadImage = (src) => new Promise((resolve) => {
15
+ const img = new Image();
16
+ img.onload = () => resolve(true);
17
+ img.onerror = () => resolve(false);
18
+ img.src = src;
19
+ });
20
+ for (const url of thumbs) {
21
+ try {
22
+ const ok = await loadImage(url);
23
+ if (ok)
24
+ return url;
25
+ }
26
+ catch (_a) {
27
+ // ignore and try next thumbnail
28
+ }
29
+ }
30
+ return null;
31
+ }
@@ -0,0 +1,31 @@
1
+ export async function getYoutubeThumbnail(videoUrl) {
2
+ const { extractYouTubeId } = await import("./extract-youtube-video-id");
3
+ const videoId = extractYouTubeId(videoUrl);
4
+ if (!videoId)
5
+ return null;
6
+ // Try loading YouTube thumbnail images — avoids CORS problems because
7
+ // creating an Image and listening for load/error is not blocked by CORS.
8
+ const thumbs = [
9
+ `https://img.youtube.com/vi/${videoId}/maxresdefault.jpg`,
10
+ `https://img.youtube.com/vi/${videoId}/hqdefault.jpg`,
11
+ `https://img.youtube.com/vi/${videoId}/mqdefault.jpg`,
12
+ `https://img.youtube.com/vi/${videoId}/default.jpg`,
13
+ ];
14
+ const loadImage = (src) => new Promise((resolve) => {
15
+ const img = new Image();
16
+ img.onload = () => resolve(true);
17
+ img.onerror = () => resolve(false);
18
+ img.src = src;
19
+ });
20
+ for (const url of thumbs) {
21
+ try {
22
+ const ok = await loadImage(url);
23
+ if (ok)
24
+ return url;
25
+ }
26
+ catch (_a) {
27
+ // ignore and try next thumbnail
28
+ }
29
+ }
30
+ return null;
31
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-youtube-thumbnail.js","sourceRoot":"","sources":["../../src/youtube/get-youtube-thumbnail.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,kDAkCC;AAlCM,KAAK,UAAU,mBAAmB,CACvC,QAAgB;IAEhB,MAAM,EAAE,gBAAgB,EAAE,GAAG,wDAAa,4BAA4B,GAAC,CAAA;IACvE,MAAM,OAAO,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAA;IAC1C,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAA;IAEzB,sEAAsE;IACtE,yEAAyE;IACzE,MAAM,MAAM,GAAG;QACb,8BAA8B,OAAO,oBAAoB;QACzD,8BAA8B,OAAO,gBAAgB;QACrD,8BAA8B,OAAO,gBAAgB;QACrD,8BAA8B,OAAO,cAAc;KACpD,CAAA;IAED,MAAM,SAAS,GAAG,CAAC,GAAW,EAAE,EAAE,CAChC,IAAI,OAAO,CAAU,CAAC,OAAO,EAAE,EAAE;QAC/B,MAAM,GAAG,GAAG,IAAI,KAAK,EAAE,CAAA;QACvB,GAAG,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;QAChC,GAAG,CAAC,OAAO,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;QAClC,GAAG,CAAC,GAAG,GAAG,GAAG,CAAA;IACf,CAAC,CAAC,CAAA;IAEJ,KAAK,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;QACzB,IAAI,CAAC;YACH,MAAM,EAAE,GAAG,MAAM,SAAS,CAAC,GAAG,CAAC,CAAA;YAC/B,IAAI,EAAE;gBAAE,OAAO,GAAG,CAAA;QACpB,CAAC;QAAC,WAAM,CAAC;YACP,gCAAgC;QAClC,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAA;AACb,CAAC"}
@@ -1,5 +1,6 @@
1
1
  export * from "./extract-youtube-video-id";
2
2
  export * from "./generate-youtube-url";
3
+ export * from "./get-youtube-thumbnail";
3
4
  export * from "./use-get-video-duration";
4
5
  export * from "./validate-youtube-link";
5
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/youtube/index.ts"],"names":[],"mappings":"AAAA,cAAc,4BAA4B,CAAA;AAC1C,cAAc,wBAAwB,CAAA;AACtC,cAAc,0BAA0B,CAAA;AACxC,cAAc,yBAAyB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/youtube/index.ts"],"names":[],"mappings":"AAAA,cAAc,4BAA4B,CAAA;AAC1C,cAAc,wBAAwB,CAAA;AACtC,cAAc,yBAAyB,CAAA;AACvC,cAAc,0BAA0B,CAAA;AACxC,cAAc,yBAAyB,CAAA"}
@@ -1,4 +1,5 @@
1
1
  export * from "./extract-youtube-video-id";
2
2
  export * from "./generate-youtube-url";
3
+ export * from "./get-youtube-thumbnail";
3
4
  export * from "./use-get-video-duration";
4
5
  export * from "./validate-youtube-link";
@@ -1,4 +1,5 @@
1
1
  export * from "./extract-youtube-video-id";
2
2
  export * from "./generate-youtube-url";
3
+ export * from "./get-youtube-thumbnail";
3
4
  export * from "./use-get-video-duration";
4
5
  export * from "./validate-youtube-link";
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/youtube/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,6DAA0C;AAC1C,yDAAsC;AACtC,2DAAwC;AACxC,0DAAuC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/youtube/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,6DAA0C;AAC1C,yDAAsC;AACtC,0DAAuC;AACvC,2DAAwC;AACxC,0DAAuC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@herowcode/utils",
3
- "version": "1.1.2",
3
+ "version": "1.1.3",
4
4
  "description": "A lightweight collection of utility functions for everyday JavaScript/TypeScript development",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.esm.js",