tiny-essentials 1.29.1 → 1.29.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.
- package/changelog/1/29/2.md +82 -0
- package/dist/v1/TinyBasicsEs.min.js +1 -1
- package/dist/v1/TinyEssentials.min.js +1 -1
- package/dist/v1/TinyFetchLoader.min.js +1 -0
- package/dist/v1/TinyImageLoader.min.js +1 -0
- package/dist/v1/TinyMediaCache.min.js +1 -0
- package/dist/v1/TinyMediaLoader.min.js +1 -0
- package/dist/v1/basics/index.cjs +5 -0
- package/dist/v1/basics/index.d.mts +2 -1
- package/dist/v1/basics/index.d.mts.map +1 -1
- package/dist/v1/basics/index.mjs +2 -1
- package/dist/v1/basics/media/youtube.cjs +275 -0
- package/dist/v1/basics/media/youtube.d.mts +107 -0
- package/dist/v1/basics/media/youtube.d.mts.map +1 -0
- package/dist/v1/basics/media/youtube.mjs +214 -0
- package/dist/v1/index.cjs +13 -0
- package/dist/v1/index.d.mts +6 -1
- package/dist/v1/index.d.mts.map +1 -1
- package/dist/v1/index.mjs +7 -2
- package/dist/v1/libs/fs/plugins/OPFS/StorageBucket.cjs +0 -1
- package/dist/v1/libs/fs/plugins/OPFS/StorageBucket.d.mts +0 -1
- package/dist/v1/libs/fs/plugins/OPFS/StorageBucket.d.mts.map +1 -1
- package/dist/v1/libs/fs/plugins/OPFS/StorageBucket.mjs +0 -1
- package/dist/v1/libs/fs/plugins/OPFS/index.cjs +0 -8
- package/dist/v1/libs/fs/plugins/OPFS/index.d.mts +0 -8
- package/dist/v1/libs/fs/plugins/OPFS/index.d.mts.map +1 -1
- package/dist/v1/libs/fs/plugins/OPFS/index.mjs +0 -8
- package/dist/v1/libs/html/TinyHtml.cjs +1 -1
- package/dist/v1/libs/html/TinyHtml.d.mts +2 -2
- package/dist/v1/libs/html/TinyHtml.d.mts.map +1 -1
- package/dist/v1/libs/html/TinyHtml.mjs +1 -1
- package/dist/v1/libs/html/loader/TinyMediaLoader.cjs +1999 -0
- package/dist/v1/libs/html/loader/TinyMediaLoader.d.mts +999 -0
- package/dist/v1/libs/html/loader/TinyMediaLoader.d.mts.map +1 -0
- package/dist/v1/libs/html/loader/TinyMediaLoader.mjs +1852 -0
- package/dist/v1/libs/html/loader/beta/formats/TinyAudioLoader.cjs +77 -0
- package/dist/v1/libs/html/loader/beta/formats/TinyAudioLoader.d.mts +103 -0
- package/dist/v1/libs/html/loader/beta/formats/TinyAudioLoader.d.mts.map +1 -0
- package/dist/v1/libs/html/loader/beta/formats/TinyAudioLoader.mjs +67 -0
- package/dist/v1/libs/html/loader/beta/formats/TinyContentLoader.cjs +630 -0
- package/dist/v1/libs/html/loader/beta/formats/TinyContentLoader.d.mts +138 -0
- package/dist/v1/libs/html/loader/beta/formats/TinyContentLoader.d.mts.map +1 -0
- package/dist/v1/libs/html/loader/beta/formats/TinyContentLoader.mjs +576 -0
- package/dist/v1/libs/html/loader/beta/formats/TinyVideoLoader.cjs +70 -0
- package/dist/v1/libs/html/loader/beta/formats/TinyVideoLoader.d.mts +100 -0
- package/dist/v1/libs/html/loader/beta/formats/TinyVideoLoader.d.mts.map +1 -0
- package/dist/v1/libs/html/loader/beta/formats/TinyVideoLoader.mjs +60 -0
- package/dist/v1/libs/html/loader/beta/probe/TinyMediaProbe.cjs +946 -0
- package/dist/v1/libs/html/loader/beta/probe/TinyMediaProbe.d.mts +67 -0
- package/dist/v1/libs/html/loader/beta/probe/TinyMediaProbe.d.mts.map +1 -0
- package/dist/v1/libs/html/loader/beta/probe/TinyMediaProbe.mjs +896 -0
- package/dist/v1/libs/html/loader/formats/TinyFetchLoader.cjs +225 -0
- package/dist/v1/libs/html/loader/formats/TinyFetchLoader.d.mts +149 -0
- package/dist/v1/libs/html/loader/formats/TinyFetchLoader.d.mts.map +1 -0
- package/dist/v1/libs/html/loader/formats/TinyFetchLoader.mjs +197 -0
- package/dist/v1/libs/html/loader/formats/TinyImageLoader.cjs +1454 -0
- package/dist/v1/libs/html/loader/formats/TinyImageLoader.d.mts +200 -0
- package/dist/v1/libs/html/loader/formats/TinyImageLoader.d.mts.map +1 -0
- package/dist/v1/libs/html/loader/formats/TinyImageLoader.mjs +1317 -0
- package/dist/v1/libs/html/loader/probe/TinyImageProbe.cjs +668 -0
- package/dist/v1/libs/html/loader/probe/TinyImageProbe.d.mts +83 -0
- package/dist/v1/libs/html/loader/probe/TinyImageProbe.d.mts.map +1 -0
- package/dist/v1/libs/html/loader/probe/TinyImageProbe.mjs +626 -0
- package/dist/v1/libs/html/loader/utils/TinyBufferReader.cjs +277 -0
- package/dist/v1/libs/html/loader/utils/TinyBufferReader.d.mts +118 -0
- package/dist/v1/libs/html/loader/utils/TinyBufferReader.d.mts.map +1 -0
- package/dist/v1/libs/html/loader/utils/TinyBufferReader.mjs +247 -0
- package/dist/v1/libs/html/loader/utils/TinyIsoBox.cjs +128 -0
- package/dist/v1/libs/html/loader/utils/TinyIsoBox.d.mts +49 -0
- package/dist/v1/libs/html/loader/utils/TinyIsoBox.d.mts.map +1 -0
- package/dist/v1/libs/html/loader/utils/TinyIsoBox.mjs +116 -0
- package/dist/v1/libs/html/loader/utils/TinyMediaCache.cjs +796 -0
- package/dist/v1/libs/html/loader/utils/TinyMediaCache.d.mts +354 -0
- package/dist/v1/libs/html/loader/utils/TinyMediaCache.d.mts.map +1 -0
- package/dist/v1/libs/html/loader/utils/TinyMediaCache.mjs +734 -0
- package/dist/v1/libs/html/loader/utils/TinyMediaMirror.cjs +302 -0
- package/dist/v1/libs/html/loader/utils/TinyMediaMirror.d.mts +256 -0
- package/dist/v1/libs/html/loader/utils/TinyMediaMirror.d.mts.map +1 -0
- package/dist/v1/libs/html/loader/utils/TinyMediaMirror.mjs +280 -0
- package/dist/v1/libs/html/loader/utils/TinyMediaProgress.cjs +194 -0
- package/dist/v1/libs/html/loader/utils/TinyMediaProgress.d.mts +138 -0
- package/dist/v1/libs/html/loader/utils/TinyMediaProgress.d.mts.map +1 -0
- package/dist/v1/libs/html/loader/utils/TinyMediaProgress.mjs +175 -0
- package/dist/v1/libs/tools/TinyPkgExportValidator.cjs +359 -55
- package/dist/v1/libs/tools/TinyPkgExportValidator.d.mts +64 -6
- package/dist/v1/libs/tools/TinyPkgExportValidator.d.mts.map +1 -1
- package/dist/v1/libs/tools/TinyPkgExportValidator.mjs +320 -57
- package/dist/v1/libs/worker/engine/TinyWebWorkerEngine.cjs +0 -1
- package/dist/v1/libs/worker/engine/TinyWebWorkerEngine.d.mts +0 -1
- package/dist/v1/libs/worker/engine/TinyWebWorkerEngine.d.mts.map +1 -1
- package/dist/v1/libs/worker/engine/TinyWebWorkerEngine.mjs +0 -1
- package/dist/v1/webTemplates/bootstrap/5.3/scss/background/_background-grid.scss +15 -0
- package/dist/v1/webTemplates/bootstrap/5.3/scss/components/buttons/_buttons-contrast.scss +208 -0
- package/dist/v1/webTemplates/bootstrap/5.3/scss/components/buttons/_buttons-ghost.scss +89 -0
- package/dist/v1/webTemplates/bootstrap/5.3/scss/components/icon/_cube-icon.scss +65 -0
- package/dist/v1/webTemplates/bootstrap/5.3/scss/components/icon/_sphere-pulse.scss +39 -0
- package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/_badges.scss +90 -0
- package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/_bootstrap-overrides.scss +495 -0
- package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/_buttons.scss +390 -0
- package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/_colors.scss +213 -0
- package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/_effects.scss +153 -0
- package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/_forms-tones.scss +69 -0
- package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/_forms.scss +323 -0
- package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/_global.scss +31 -0
- package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/_links.scss +47 -0
- package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/_tables.scss +25 -0
- package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/_theme.scss +21 -0
- package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/_tokens.scss +161 -0
- package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/_utilities.scss +81 -0
- package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/_variables.scss +63 -0
- package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/plugins/_form-tone-mixins.scss +236 -0
- package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/plugins/_sizing.scss +68 -0
- package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/plugins/_tone-mixins.scss +392 -0
- package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/plugins/_typography.scss +112 -0
- package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/plugins/background/_tiny_gradient_1.scss +131 -0
- package/dist/v1/webTemplates/bootstrap/5.3/scss/themes/deepstrap/plugins/colors/_pink.scss +47 -0
- package/docs/v1/Exports.md +33 -2
- package/docs/v1/README.md +9 -0
- package/docs/v1/basics/youtube.md +204 -0
- package/docs/v1/libs/html/loader/TinyFetchLoader.md +114 -0
- package/docs/v1/libs/html/loader/TinyImageLoader.md +141 -0
- package/docs/v1/libs/html/loader/TinyMediaCache.md +166 -0
- package/docs/v1/libs/html/loader/TinyMediaLoader.md +300 -0
- package/docs/v1/libs/tools/TinyPkgExportValidator.md +55 -0
- package/docs/v1/webTemplates/bootstrap/5.3/DeepStrap.md +177 -0
- package/package.json +244 -8
- /package/dist/v1/webTemplates/bootstrap/5.3/scss/input/{Placeholder-Colors-Fix.scss → _placeholder-colors-fix.scss} +0 -0
- /package/dist/v1/webTemplates/bootstrap/5.3/scss/spacing/{Spacing-Plus.scss → _spacing-plus.scss} +0 -0
package/docs/v1/Exports.md
CHANGED
|
@@ -21,6 +21,8 @@ These are pre-built stylesheets that can be directly imported:
|
|
|
21
21
|
import "tiny-essentials/css/TinyNotify.min.css";
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
+
> 💡 The whole `css/` folder is exposed through the `./css/*.css` wildcard, so any new stylesheet shipped in `dist/v1/css` is importable without a `package.json` change.
|
|
25
|
+
|
|
24
26
|
---
|
|
25
27
|
|
|
26
28
|
## 🏠 Root Entrypoints
|
|
@@ -62,6 +64,7 @@ Direct access to smaller utilities:
|
|
|
62
64
|
* `basics/promiseUtils`
|
|
63
65
|
* `basics/browserDetector`
|
|
64
66
|
* `basics/jsonFilter`
|
|
67
|
+
* `basics/media/youtube`
|
|
65
68
|
|
|
66
69
|
Direct access to file utilities:
|
|
67
70
|
|
|
@@ -228,6 +231,22 @@ Each library can be imported separately:
|
|
|
228
231
|
* `libs/media/TinyMediaPlayer/docs/SoundCloudWidget`
|
|
229
232
|
* `libs/media/TinyRadioFm`
|
|
230
233
|
|
|
234
|
+
* **Media Loaders 🎞️**
|
|
235
|
+
|
|
236
|
+
* `libs/html/loader/TinyMediaLoader`
|
|
237
|
+
* `libs/html/loader/formats/TinyImageLoader`
|
|
238
|
+
* `libs/html/loader/formats/TinyFetchLoader`
|
|
239
|
+
* `libs/html/loader/utils/TinyMediaCache`
|
|
240
|
+
* `libs/html/loader/utils/TinyMediaProgress`
|
|
241
|
+
* `libs/html/loader/utils/TinyMediaMirror`
|
|
242
|
+
* `libs/html/loader/utils/TinyBufferReader`
|
|
243
|
+
* `libs/html/loader/utils/TinyIsoBox`
|
|
244
|
+
* `libs/html/loader/probe/TinyImageProbe`
|
|
245
|
+
* `libs/html/loader/beta/formats/TinyContentLoader` (BETA)
|
|
246
|
+
* `libs/html/loader/beta/formats/TinyAudioLoader` (BETA)
|
|
247
|
+
* `libs/html/loader/beta/formats/TinyVideoLoader` (BETA)
|
|
248
|
+
* `libs/html/loader/beta/probe/TinyMediaProbe` (BETA)
|
|
249
|
+
|
|
231
250
|
* **HTML Helpers 🧩**
|
|
232
251
|
|
|
233
252
|
* `libs/html/TinyHtml`
|
|
@@ -310,8 +329,20 @@ Each library can be imported separately:
|
|
|
310
329
|
### Bootstrap 5.3 🅱️
|
|
311
330
|
|
|
312
331
|
* `webTemplates/bootstrap/5.3/html/BootstrapDialogs`
|
|
313
|
-
* `webTemplates/bootstrap/5.3/scss
|
|
314
|
-
|
|
332
|
+
* `webTemplates/bootstrap/5.3/scss/*` (wildcard)
|
|
333
|
+
|
|
334
|
+
> 💡 The whole `scss/` folder is exposed through the `./webTemplates/bootstrap/5.3/scss/*` wildcard, so every partial is importable without a `package.json` change.
|
|
335
|
+
|
|
336
|
+
**Theme (DeepStrap)**
|
|
337
|
+
|
|
338
|
+
* `webTemplates/bootstrap/5.3/scss/themes/deepstrap/theme`
|
|
339
|
+
* `webTemplates/bootstrap/5.3/scss/background/background-grid`
|
|
340
|
+
* `webTemplates/bootstrap/5.3/scss/components/buttons/buttons-ghost`
|
|
341
|
+
* `webTemplates/bootstrap/5.3/scss/components/buttons/buttons-contrast`
|
|
342
|
+
* `webTemplates/bootstrap/5.3/scss/components/icon/cube-icon`
|
|
343
|
+
* `webTemplates/bootstrap/5.3/scss/components/icon/sphere-pulse`
|
|
344
|
+
* `webTemplates/bootstrap/5.3/scss/input/placeholder-colors-fix`
|
|
345
|
+
* `webTemplates/bootstrap/5.3/scss/spacing/spacing-plus`
|
|
315
346
|
|
|
316
347
|
### Vite 7.3 ⚡
|
|
317
348
|
|
package/docs/v1/README.md
CHANGED
|
@@ -22,6 +22,7 @@ Here, you can navigate through different sections of the project. Below are the
|
|
|
22
22
|
- 📺 **[FullScreen](./basics/fullScreen.md)** — A complete fullscreen API manager with detection, event handling, and cross-browser compatibility.
|
|
23
23
|
- 🧱 **[Collision](./basics/collision.md)** — Full-featured rectangle collision detection system with directional analysis, depth calculation, and center offset metrics.
|
|
24
24
|
- 🎵 **[MediaContent](./basics/mediaContent.md)** — A comprehensive media processing module for downloading audio, extracting rich ID3 metadata, and managing image blobs for album art.
|
|
25
|
+
- 🎬 **[YouTube](./basics/youtube.md)** — Extract video ids from any YouTube URL, build thumbnail URLs for every quality, and fetch/validate the official oEmbed metadata.
|
|
25
26
|
- 📦 **[BrowserDetector](./basics/browserDetector.md)** — A multi-layered JavaScript utility for identifying the user's web browser and rendering engine.
|
|
26
27
|
- 🔐 **[Crypto](./basics/crypto.md)** — A lightweight utility for generating cryptographic hashes (e.g., SHA-256) using the Web Crypto API.
|
|
27
28
|
- 🔁 **[PromiseUtils](./basics/promiseUtils.md)** — Promise helpers such as `waitForTrue` and `createSingletonTask` for polling and single-flight async tasks.
|
|
@@ -119,6 +120,13 @@ Here, you can navigate through different sections of the project. Below are the
|
|
|
119
120
|
- 📻 **[TinyRadioFm](./libs/media/TinyRadioFm.md)** — It is designed to handle complex playback sequences, including music and voice playlists, scheduled timeline mutations, and custom content injections, all while maintaining absolute reproducibility through a mathematical seed.
|
|
120
121
|
- ⏯️ **[TinyMediaPlayer](./libs/media/TinyMediaPlayer.md)** — A universal media player architecture utilizing an adapter pattern to orchestrate multiple platform APIs, featuring robust playlist management, playback controls, and weighted random selection.
|
|
121
122
|
|
|
123
|
+
#### 2.9.1. **`libs/html/loader`**
|
|
124
|
+
|
|
125
|
+
- 📦 **[TinyMediaLoader](./libs/html/loader/TinyMediaLoader.md)** — The abstract base class behind every media loader, with a full state machine, streaming progress, shared cache, download mirroring and a typed event surface.
|
|
126
|
+
- 🖼️ **[TinyImageLoader](./libs/html/loader/TinyImageLoader.md)** — Image loader with intrinsic size helpers, streaming and a complete animation engine for `GIF`, `APNG`, `WebP` and `AVIF`.
|
|
127
|
+
- 📥 **[TinyFetchLoader](./libs/html/loader/TinyFetchLoader.md)** — Generic file loader that reuses the media pipeline to download `blob`, `arraybuffer`, `text` or `json` payloads.
|
|
128
|
+
- 🗄️ **[TinyMediaCache](./libs/html/loader/TinyMediaCache.md)** — Shared, reference-counted blob cache with TTL, byte budgets and parent/child links.
|
|
129
|
+
|
|
122
130
|
### 2.10. **`libs/tools`**
|
|
123
131
|
|
|
124
132
|
- 🛠️ **[TinyDebugger](./libs/tools/TinyDebugger.md)** — A lightweight, event-driven debugging utility designed to wrap standard `console` methods. It provides enhanced features such as custom prefixes, color support, and the ability to emit events whenever a log occurs, making it perfect for complex applications where you might want to redirect logs to an external service or a UI component.
|
|
@@ -208,6 +216,7 @@ Here, you can navigate through different sections of the project. Below are the
|
|
|
208
216
|
#### 4.1. **`webTemplates/bootstrap/`**
|
|
209
217
|
|
|
210
218
|
- 🖼️ **[BootstrapDialogs](./webTemplates/bootstrap/5.3/html/BootstrapDialogs.md)** — An asynchronous JavaScript utility designed to replace native browser dialogs with customizable Bootstrap 5 Modals.
|
|
219
|
+
- 🎨 **[DeepStrap](./webTemplates/bootstrap/5.3/DeepStrap.md)** — A token-driven Bootstrap 5.3 theme with light/dark palettes, CSS custom properties and optional plugins (ghost buttons, contrast buttons, cube icon, sphere pulse and background grid).
|
|
211
220
|
|
|
212
221
|
#### 4.2. **`webTemplates/vite/`**
|
|
213
222
|
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
# 🎬 YouTube Utils Documentation
|
|
2
|
+
|
|
3
|
+
A tiny, dependency-free toolkit for everything you need to do with a YouTube link: **extract the video id**, **build thumbnail URLs** and **fetch the official oEmbed metadata** — with strict validation at every step. 🚀
|
|
4
|
+
|
|
5
|
+
```javascript
|
|
6
|
+
import {
|
|
7
|
+
extractYouTubeVideoId,
|
|
8
|
+
getYouTubeThumbnailUrl,
|
|
9
|
+
fetchYouTubeOEmbed,
|
|
10
|
+
assertYouTubeOEmbedResponse,
|
|
11
|
+
} from 'tiny-essentials/basics/media/youtube';
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## 📋 Table of Contents
|
|
17
|
+
|
|
18
|
+
1. [Data Types](#-data-types)
|
|
19
|
+
2. [extractYouTubeVideoId](#-extractyoutubevideoid)
|
|
20
|
+
3. [getYouTubeThumbnailUrl](#-getyoutubethumbnailurl)
|
|
21
|
+
4. [fetchYouTubeOEmbed](#-fetchyoutuboeembed)
|
|
22
|
+
5. [assertYouTubeOEmbedResponse](#-assertyoutuboeembedresponse)
|
|
23
|
+
6. [Error Reference](#-error-reference)
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## 📋 Data Types
|
|
28
|
+
|
|
29
|
+
### `YouTubeThumbnailQuality`
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
type YouTubeThumbnailQuality =
|
|
33
|
+
| 'default' // 120x90
|
|
34
|
+
| 'mqdefault' // 320x180
|
|
35
|
+
| 'hqdefault' // 480x360 (default)
|
|
36
|
+
| 'sddefault' // 640x480
|
|
37
|
+
| 'maxresdefault'; // 1280x720
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### `YouTubeOEmbedResponse`
|
|
41
|
+
|
|
42
|
+
The validated payload returned by [`fetchYouTubeOEmbed`](#-fetchyoutuboeembed).
|
|
43
|
+
|
|
44
|
+
| Property | Type | Description |
|
|
45
|
+
| :----------------- | :-------- | :-------------------------------------- |
|
|
46
|
+
| `title` | `string` | Video title. |
|
|
47
|
+
| `author_name` | `string` | Channel display name. |
|
|
48
|
+
| `author_url` | `string` | Channel URL. |
|
|
49
|
+
| `type` | `'video'` | Resource type returned by the provider. |
|
|
50
|
+
| `version` | `string` | oEmbed version. |
|
|
51
|
+
| `provider_name` | `string` | Always `"YouTube"`. |
|
|
52
|
+
| `provider_url` | `string` | Provider root URL. |
|
|
53
|
+
| `html` | `string` | Ready to use iframe markup. |
|
|
54
|
+
| `width` | `number` | Suggested iframe width in pixels. |
|
|
55
|
+
| `height` | `number` | Suggested iframe height in pixels. |
|
|
56
|
+
| `thumbnail_url` | `string` | Thumbnail URL provided by the provider. |
|
|
57
|
+
| `thumbnail_width` | `number` | Thumbnail width in pixels. |
|
|
58
|
+
| `thumbnail_height` | `number` | Thumbnail height in pixels. |
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## 🆔 `extractYouTubeVideoId`
|
|
63
|
+
|
|
64
|
+
Extracts the canonical 11 character video id from a raw id or a full YouTube URL.
|
|
65
|
+
|
|
66
|
+
```javascript
|
|
67
|
+
import { extractYouTubeVideoId } from 'tiny-essentials/basics/media/youtube';
|
|
68
|
+
|
|
69
|
+
extractYouTubeVideoId('dQw4w9WgXcQ');
|
|
70
|
+
// → 'dQw4w9WgXcQ'
|
|
71
|
+
|
|
72
|
+
extractYouTubeVideoId('https://www.youtube.com/watch?v=dQw4w9WgXcQ&list=PL123');
|
|
73
|
+
// → 'dQw4w9WgXcQ'
|
|
74
|
+
|
|
75
|
+
extractYouTubeVideoId('https://youtu.be/dQw4w9WgXcQ?t=42');
|
|
76
|
+
// → 'dQw4w9WgXcQ'
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Supported inputs
|
|
80
|
+
|
|
81
|
+
| Input | Example |
|
|
82
|
+
| :--------- | :-------------------------------------------- |
|
|
83
|
+
| Raw id | `dQw4w9WgXcQ` |
|
|
84
|
+
| `watch` | `https://www.youtube.com/watch?v=dQw4w9WgXcQ` |
|
|
85
|
+
| Short link | `https://youtu.be/dQw4w9WgXcQ` |
|
|
86
|
+
| `embed` | `https://www.youtube.com/embed/dQw4w9WgXcQ` |
|
|
87
|
+
| `shorts` | `https://www.youtube.com/shorts/dQw4w9WgXcQ` |
|
|
88
|
+
| `live` | `https://www.youtube.com/live/dQw4w9WgXcQ` |
|
|
89
|
+
| `v` | `https://www.youtube.com/v/dQw4w9WgXcQ` |
|
|
90
|
+
|
|
91
|
+
**Allowed hosts:** `youtube.com`, `m.youtube.com`, `music.youtube.com`, `youtube-nocookie.com` and `youtu.be` (the `www.` prefix is optional). Only `http:` and `https:` are accepted.
|
|
92
|
+
|
|
93
|
+
**Throws:**
|
|
94
|
+
|
|
95
|
+
- `TypeError` — when the input is not a string.
|
|
96
|
+
- `RangeError` — when the input is empty, the protocol is unsupported or the host is not a YouTube host.
|
|
97
|
+
- `SyntaxError` — when the input is not a video id, is not a valid URL, or does not contain a video id.
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## 🖼️ `getYouTubeThumbnailUrl`
|
|
102
|
+
|
|
103
|
+
Builds the thumbnail URL of a video **without any network request**.
|
|
104
|
+
|
|
105
|
+
```javascript
|
|
106
|
+
import { getYouTubeThumbnailUrl } from 'tiny-essentials/basics/media/youtube';
|
|
107
|
+
|
|
108
|
+
getYouTubeThumbnailUrl('dQw4w9WgXcQ');
|
|
109
|
+
// → 'https://i.ytimg.com/vi/dQw4w9WgXcQ/hqdefault.jpg'
|
|
110
|
+
|
|
111
|
+
getYouTubeThumbnailUrl('https://youtu.be/dQw4w9WgXcQ', 'maxresdefault');
|
|
112
|
+
// → 'https://i.ytimg.com/vi/dQw4w9WgXcQ/maxresdefault.jpg'
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
| Parameter | Type | Default | Description |
|
|
116
|
+
| :------------- | :------------------------ | :------------ | :-------------------------------------------------------------------------------- |
|
|
117
|
+
| `videoIdOrUrl` | `string` | — | Raw id or any URL accepted by [`extractYouTubeVideoId`](#-extractyoutubevideoid). |
|
|
118
|
+
| `quality` | `YouTubeThumbnailQuality` | `'hqdefault'` | Thumbnail resolution. |
|
|
119
|
+
|
|
120
|
+
**Returns:** `string` — the thumbnail URL.
|
|
121
|
+
|
|
122
|
+
**Throws:** `TypeError` when `quality` is not a string, `RangeError` when it is not a supported quality, plus anything thrown by `extractYouTubeVideoId`.
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## 📡 `fetchYouTubeOEmbed`
|
|
127
|
+
|
|
128
|
+
Fetches the [official oEmbed](https://oembed.com/) metadata of a video and returns a fully validated [`YouTubeOEmbedResponse`](#-data-types).
|
|
129
|
+
|
|
130
|
+
```javascript
|
|
131
|
+
import { fetchYouTubeOEmbed } from 'tiny-essentials/basics/media/youtube';
|
|
132
|
+
|
|
133
|
+
const video = await fetchYouTubeOEmbed('https://youtu.be/dQw4w9WgXcQ');
|
|
134
|
+
|
|
135
|
+
console.log(video.title); // 'Never Gonna Give You Up'
|
|
136
|
+
console.log(video.author_name); // 'Rick Astley'
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Options
|
|
140
|
+
|
|
141
|
+
| Option | Type | Default | Description |
|
|
142
|
+
| :---------- | :------------- | :----------------- | :-------------------------------------------------- |
|
|
143
|
+
| `signal` | `AbortSignal` | `undefined` | Forwarded to `fetch` so the request can be aborted. |
|
|
144
|
+
| `fetchImpl` | `typeof fetch` | `globalThis.fetch` | Injectable fetch implementation (handy for tests). |
|
|
145
|
+
|
|
146
|
+
```javascript
|
|
147
|
+
const controller = new AbortController();
|
|
148
|
+
|
|
149
|
+
const video = await fetchYouTubeOEmbed('dQw4w9WgXcQ', {
|
|
150
|
+
signal: controller.signal,
|
|
151
|
+
fetchImpl: myFetch,
|
|
152
|
+
});
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
**Returns:** `Promise<YouTubeOEmbedResponse>`
|
|
156
|
+
|
|
157
|
+
**Throws:**
|
|
158
|
+
|
|
159
|
+
- `TypeError` — invalid `options`, `options.signal` or `options.fetchImpl`, or a response that is not a `Response`.
|
|
160
|
+
- `Error` — the request failed or returned a non 2xx status.
|
|
161
|
+
- `RangeError` — the payload contains an out of range numeric value.
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## ✅ `assertYouTubeOEmbedResponse`
|
|
166
|
+
|
|
167
|
+
Deeply validates an oEmbed payload and returns a **brand new** object containing only the known properties.
|
|
168
|
+
|
|
169
|
+
```javascript
|
|
170
|
+
import { assertYouTubeOEmbedResponse } from 'tiny-essentials/basics/media/youtube';
|
|
171
|
+
|
|
172
|
+
const payload = await fetch('/oembed.json').then((res) => res.json());
|
|
173
|
+
const video = assertYouTubeOEmbedResponse(payload);
|
|
174
|
+
|
|
175
|
+
console.log(video.title);
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
| Parameter | Type | Default | Description |
|
|
179
|
+
| :-------- | :-------- | :------------------------------ | :--------------------------------------- |
|
|
180
|
+
| `payload` | `unknown` | — | Raw value returned by `response.json()`. |
|
|
181
|
+
| `context` | `string` | `'assertYouTubeOEmbedResponse'` | Caller name used in the error messages. |
|
|
182
|
+
|
|
183
|
+
**Throws:**
|
|
184
|
+
|
|
185
|
+
- `TypeError` — the payload (or one of its properties) has the wrong type.
|
|
186
|
+
- `RangeError` — a numeric property is not a positive integer, or `type` is not `'video'`.
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## ❌ Error Reference
|
|
191
|
+
|
|
192
|
+
| Error | When |
|
|
193
|
+
| :------------ | :--------------------------------------------------------------------------------------------------------------------------------- |
|
|
194
|
+
| `TypeError` | A value has the wrong type (non-string id, non-function `fetchImpl`, non-`AbortSignal` signal, non-object payload…). |
|
|
195
|
+
| `RangeError` | A value is syntactically valid but out of range (empty string, unsupported host/protocol, unknown quality, non-positive integer…). |
|
|
196
|
+
| `SyntaxError` | The input is neither a video id nor a URL containing one. |
|
|
197
|
+
| `Error` | The HTTP request failed or returned a non 2xx status. |
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## 🧪 Testing
|
|
202
|
+
|
|
203
|
+
- **Browser playground:** `test/html/basics/youtube` (run `npm run test:mjs:web` and open `/basics/youtube/`).
|
|
204
|
+
- **Node.js suite:** `npm run test:mjs:youtube`.
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# 📥 TinyFetchLoader
|
|
2
|
+
|
|
3
|
+
`TinyFetchLoader` is a generic file loader built on top of [`TinyMediaLoader`](./TinyMediaLoader.md). It does not create a DOM element — it downloads any resource and exposes the payload as a `Blob`, `ArrayBuffer`, `string` or parsed `JSON`.
|
|
4
|
+
|
|
5
|
+
Because it reuses the whole media pipeline, it inherits progress tracking, caching, mirroring, timeouts and the typed event surface.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## ✨ Highlights
|
|
10
|
+
|
|
11
|
+
* **Any payload type** — `blob`, `arraybuffer`, `text` or `json`.
|
|
12
|
+
* **`fetch` compatible** — `exportFetch()` returns a drop-in replacement for `globalThis.fetch`.
|
|
13
|
+
* **Cloneable** — `clone(overrides)` creates an independent loader that reuses the shared cache.
|
|
14
|
+
* **No DOM required** — works in workers and in Node.js (with a `fetch` implementation).
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## 🚀 Quick start
|
|
19
|
+
|
|
20
|
+
```js
|
|
21
|
+
import TinyFetchLoader from 'tiny-essentials/libs/html/loader/formats/TinyFetchLoader';
|
|
22
|
+
|
|
23
|
+
const loader = new TinyFetchLoader({
|
|
24
|
+
src: 'https://api.example.com/data.json',
|
|
25
|
+
responseType: 'json',
|
|
26
|
+
init: { headers: { Accept: 'application/json' } },
|
|
27
|
+
timeout: 15_000,
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
loader.on('progress', (p) => console.log(`${p.percent.toFixed(0)}%`));
|
|
31
|
+
|
|
32
|
+
await loader.load();
|
|
33
|
+
console.log(loader.data);
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## ⚙️ Options
|
|
39
|
+
|
|
40
|
+
Inherits every [`TinyMediaLoader`](./TinyMediaLoader.md) option, plus:
|
|
41
|
+
|
|
42
|
+
| Option | Type | Default | Description |
|
|
43
|
+
| :--- | :--- | :--- | :--- |
|
|
44
|
+
| `src` | `string` | `''` | The file URL. |
|
|
45
|
+
| `init` | `RequestInit` | `{}` | Options forwarded to `fetch` (method, headers, body, credentials, mode, cache, redirect, referrer, referrerPolicy, integrity, keepalive). |
|
|
46
|
+
| `fetch` | `typeof fetch` | `globalThis.fetch` | A custom fetch implementation. |
|
|
47
|
+
| `responseType` | `'blob' \| 'arraybuffer' \| 'text' \| 'json'` | `'blob'` | How the payload is parsed. |
|
|
48
|
+
| `body` | `BodyInit \| undefined` | `undefined` | The request body forwarded to the transport. Ignored by `GET`/`HEAD`; `init.body` wins when both are set. |
|
|
49
|
+
|
|
50
|
+
> **Note:** `body` is inherited from [`TinyMediaLoader`](./TinyMediaLoader.md#-request-body) and is only sent when the request method is not `GET`/`HEAD`.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## 🧩 Properties
|
|
55
|
+
|
|
56
|
+
| Getter | Type | Description |
|
|
57
|
+
| :--- | :--- | :--- |
|
|
58
|
+
| `blob` | `Blob \| null` | The raw payload, or `null` before the load finishes. |
|
|
59
|
+
| `data` | `Blob \| ArrayBuffer \| string \| unknown \| null` | The parsed payload. |
|
|
60
|
+
| `responseType` | `string` | The requested response type. |
|
|
61
|
+
| `init` | `RequestInit` | A defensive copy of the fetch options. |
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## 🛠️ Methods
|
|
66
|
+
|
|
67
|
+
### `clone(overrides = {})`
|
|
68
|
+
|
|
69
|
+
Creates a new, fully independent loader. The shared `cache` and the custom `fetch` implementation are reused by reference, which makes cloning cheap.
|
|
70
|
+
|
|
71
|
+
```js
|
|
72
|
+
const json = loader.clone({ responseType: 'json' });
|
|
73
|
+
const text = loader.clone({ responseType: 'text' });
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### `exportFetch()`
|
|
77
|
+
|
|
78
|
+
Returns a function with the same signature as `fetch` that is backed by this loader.
|
|
79
|
+
|
|
80
|
+
```js
|
|
81
|
+
const tinyFetch = loader.exportFetch();
|
|
82
|
+
const response = await tinyFetch('https://api.example.com/ping');
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## 📦 Response types
|
|
88
|
+
|
|
89
|
+
| `responseType` | `loader.data` |
|
|
90
|
+
| :--- | :--- |
|
|
91
|
+
| `'blob'` *(default)* | `Blob` |
|
|
92
|
+
| `'arraybuffer'` | `ArrayBuffer` |
|
|
93
|
+
| `'text'` | `string` |
|
|
94
|
+
| `'json'` | `unknown` |
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## 🧪 Example: downloading a JSON file with progress
|
|
99
|
+
|
|
100
|
+
```js
|
|
101
|
+
import TinyFetchLoader from 'tiny-essentials/libs/html/loader/formats/TinyFetchLoader';
|
|
102
|
+
|
|
103
|
+
const loader = new TinyFetchLoader({
|
|
104
|
+
src: './data/large-dataset.json',
|
|
105
|
+
responseType: 'json',
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
loader.on('progress', ({ percent, rate, eta }) => {
|
|
109
|
+
console.log(`${percent.toFixed(1)}% — ${(rate / 1024).toFixed(0)} KB/s — ETA ${(eta / 1000).toFixed(1)}s`);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
await loader.load();
|
|
113
|
+
console.log(loader.data);
|
|
114
|
+
```
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# 🖼️ TinyImageLoader
|
|
2
|
+
|
|
3
|
+
`TinyImageLoader` is the image implementation of [`TinyMediaLoader`](./TinyMediaLoader.md). On top of the shared lifecycle it adds intrinsic size helpers, streaming, and a complete animation engine capable of decoding and replaying `GIF`, `APNG`, `WebP` and `AVIF` frames by hand.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## ✨ Highlights
|
|
8
|
+
|
|
9
|
+
* **Everything from `TinyMediaLoader`** — cache, progress, mirror, timeout, events.
|
|
10
|
+
* **Intrinsic metrics** — `naturalWidth`, `naturalHeight` and `aspectRatio` without touching the DOM.
|
|
11
|
+
* **Four animation modes** — from fully native to a manual frame clock.
|
|
12
|
+
* **Frame extraction** — every frame becomes an independent blob stored in a dedicated `TinyMediaCache`.
|
|
13
|
+
* **Playback API** — `play`, `pause`, `freeze`, `unfreeze` and `playbackRate`.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## 🚀 Quick start
|
|
18
|
+
|
|
19
|
+
```js
|
|
20
|
+
import TinyImageLoader from 'tiny-essentials/libs/html/loader/formats/TinyImageLoader';
|
|
21
|
+
|
|
22
|
+
const loader = new TinyImageLoader({
|
|
23
|
+
src: './assets/nyan-cat.gif',
|
|
24
|
+
animationControl: 'native',
|
|
25
|
+
decoding: 'async',
|
|
26
|
+
crossOrigin: 'anonymous',
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
loader.on('loaded', () => {
|
|
30
|
+
console.log(loader.naturalWidth, loader.naturalHeight, loader.aspectRatio);
|
|
31
|
+
loader.mount(document.querySelector('#stage'));
|
|
32
|
+
loader.play();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
await loader.load();
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## ⚙️ Options
|
|
41
|
+
|
|
42
|
+
Inherits every [`TinyMediaLoader`](./TinyMediaLoader.md) option, plus:
|
|
43
|
+
|
|
44
|
+
| Option | Type | Default | Description |
|
|
45
|
+
| :--- | :--- | :--- | :--- |
|
|
46
|
+
| `crossOrigin` | `'anonymous' \| 'use-credentials' \| null` | `null` | The `crossorigin` attribute applied to the image. |
|
|
47
|
+
| `decoding` | `'sync' \| 'async' \| 'auto'` | `'async'` | The `decoding` hint passed to the browser. |
|
|
48
|
+
| `stream` | `boolean` | `false` | Downloads the image through `fetch` to expose byte level progress. |
|
|
49
|
+
| `animationControl` | `'none' \| 'native' \| 'script-only' \| 'controlled'` | `'none'` | How animated images are handled. |
|
|
50
|
+
|
|
51
|
+
### Animation modes
|
|
52
|
+
|
|
53
|
+
| Mode | Description |
|
|
54
|
+
| :--- | :--- |
|
|
55
|
+
| `none` | The browser renders the image as-is. No frame extraction. |
|
|
56
|
+
| `native` | Frames are extracted, but the browser still plays the animation. `freeze()` falls back to the first frame. |
|
|
57
|
+
| `script-only` | Frames are extracted and available, but the loader never touches playback. |
|
|
58
|
+
| `controlled` | The loader owns a frame clock. Use `play`, `pause`, `freeze`, `unfreeze` and `playbackRate`. **Stable on `WebP`, 95% on `PNG` (APNG), unstable on other formats — see the compatibility status below.** |
|
|
59
|
+
|
|
60
|
+
### ⚠️ Compatibility status (`controlled` mode)
|
|
61
|
+
|
|
62
|
+
Frame extraction and the manual frame clock are **not equally stable across every container**. Check the table below before relying on `controlled` in production:
|
|
63
|
+
|
|
64
|
+
| Format | Status | Notes |
|
|
65
|
+
| :--- | :--- | :--- |
|
|
66
|
+
| `WebP` | ✅ Stable | Fully working. This is the recommended format. |
|
|
67
|
+
| `PNG` (APNG) | 🟡 95% | Works in almost every case. Rare edge cases may show a wrong frame delay or drop a frame. |
|
|
68
|
+
| `GIF` / `AVIF` | ⚠️ Unstable | Frame extraction may fail, desynchronize or return incomplete data depending on the encoder. |
|
|
69
|
+
|
|
70
|
+
**Recommendation:** if you need `controlled` mode, convert the asset to `WebP` first. If you cannot convert it, use `animationControl: 'native'` instead — it is 100% tested and stable on every animated format listed above.
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## 🧩 Properties
|
|
75
|
+
|
|
76
|
+
| Getter | Type | Description |
|
|
77
|
+
| :--- | :--- | :--- |
|
|
78
|
+
| `image` | `HTMLImageElement \| null` | The managed `<img>` element. |
|
|
79
|
+
| `stream` | `boolean` | Whether the image is downloaded through `fetch`. |
|
|
80
|
+
| `naturalWidth` | `number` | The intrinsic width in pixels. |
|
|
81
|
+
| `naturalHeight` | `number` | The intrinsic height in pixels. |
|
|
82
|
+
| `aspectRatio` | `number` | `naturalWidth / naturalHeight`, or `0` when unknown. |
|
|
83
|
+
| `playbackRate` | `number` | Playback speed modifier (controlled mode). |
|
|
84
|
+
| `animationScript` | `AnimationScriptStep[]` | A deep copy of the per-frame delays. |
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## 🛠️ Methods
|
|
89
|
+
|
|
90
|
+
### `freeze(frameIndex = null)`
|
|
91
|
+
|
|
92
|
+
Freezes the animation.
|
|
93
|
+
|
|
94
|
+
* In `native` mode the first frame is displayed.
|
|
95
|
+
* In `controlled` mode the clock stops at `frameIndex` (or at the current frame when omitted).
|
|
96
|
+
|
|
97
|
+
```js
|
|
98
|
+
loader.freeze(0); // back to the first frame
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### `unfreeze()`
|
|
102
|
+
|
|
103
|
+
Resumes the animation from the frozen state.
|
|
104
|
+
|
|
105
|
+
### `play()`
|
|
106
|
+
|
|
107
|
+
Starts (or resumes) the frame clock. Only available in `controlled` mode.
|
|
108
|
+
|
|
109
|
+
### `pause()`
|
|
110
|
+
|
|
111
|
+
Stops the frame clock without changing the current frame. Only available in `controlled` mode.
|
|
112
|
+
|
|
113
|
+
### `playbackRate` (setter)
|
|
114
|
+
|
|
115
|
+
Changes the playback speed. Values are clamped to a minimum of `0.1`.
|
|
116
|
+
|
|
117
|
+
```js
|
|
118
|
+
loader.playbackRate = 0.5; // half speed
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### `animationScript` (setter)
|
|
122
|
+
|
|
123
|
+
Replaces the per-frame delays. Only available in `controlled` mode.
|
|
124
|
+
|
|
125
|
+
```js
|
|
126
|
+
loader.animationScript = loader.animationScript.map(() => ({ delay: 50 }));
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## 📡 Events
|
|
132
|
+
|
|
133
|
+
Every event from [`TinyMediaLoader`](./TinyMediaLoader.md#-events) is available. The `metadata` event is emitted with `type: 'PROBE_ANIMATION'` once the animation data has been resolved.
|
|
134
|
+
|
|
135
|
+
```js
|
|
136
|
+
loader.on('metadata', (metadata, type) => {
|
|
137
|
+
if (type === 'PROBE_ANIMATION') {
|
|
138
|
+
console.log(metadata.animation); // { frames, loopCount, duration }
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
```
|