tiny-essentials 1.28.4 → 1.28.5

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 (51) hide show
  1. package/changelog/1/28/5.md +31 -0
  2. package/dist/v1/TinyBasicsEs.min.js +1 -1
  3. package/dist/v1/TinyEssentials.min.js +1 -1
  4. package/dist/v1/TinyMediaPlayer.min.js +1 -1
  5. package/dist/v1/TinyTimeout.min.js +1 -1
  6. package/dist/v1/TinyUploadClicker.min.js +1 -1
  7. package/dist/v1/basics/index.cjs +4 -0
  8. package/dist/v1/basics/index.d.mts +3 -2
  9. package/dist/v1/basics/index.mjs +3 -2
  10. package/dist/v1/basics/mediaContent.cjs +1 -0
  11. package/dist/v1/basics/mediaContent.d.mts +4 -0
  12. package/dist/v1/basics/mediaContent.mjs +1 -0
  13. package/dist/v1/basics/objChecker.cjs +16 -0
  14. package/dist/v1/basics/objChecker.d.mts +12 -0
  15. package/dist/v1/basics/objChecker.mjs +14 -0
  16. package/dist/v1/basics/objFilter.cjs +0 -2
  17. package/dist/v1/basics/objFilter.d.mts +0 -2
  18. package/dist/v1/basics/objFilter.mjs +1 -2
  19. package/dist/v1/basics/promiseUtils.cjs +101 -0
  20. package/dist/v1/basics/promiseUtils.d.mts +31 -0
  21. package/dist/v1/basics/promiseUtils.mjs +89 -0
  22. package/dist/v1/index.cjs +4 -0
  23. package/dist/v1/index.d.mts +3 -2
  24. package/dist/v1/index.mjs +3 -2
  25. package/dist/v1/libs/TinyMediaPlayer/HtmlAudio.cjs +268 -0
  26. package/dist/v1/libs/TinyMediaPlayer/HtmlAudio.d.mts +118 -0
  27. package/dist/v1/libs/TinyMediaPlayer/HtmlAudio.mjs +237 -0
  28. package/dist/v1/libs/TinyMediaPlayer/HtmlVideo.cjs +265 -0
  29. package/dist/v1/libs/TinyMediaPlayer/HtmlVideo.d.mts +118 -0
  30. package/dist/v1/libs/TinyMediaPlayer/HtmlVideo.mjs +234 -0
  31. package/dist/v1/libs/TinyMediaPlayer/Youtube.cjs +671 -0
  32. package/dist/v1/libs/TinyMediaPlayer/Youtube.d.mts +259 -0
  33. package/dist/v1/libs/TinyMediaPlayer/Youtube.mjs +597 -0
  34. package/dist/v1/libs/TinyMediaPlayer/docs/YouTubePlayer.cjs +546 -0
  35. package/dist/v1/libs/TinyMediaPlayer/docs/YouTubePlayer.d.mts +657 -0
  36. package/dist/v1/libs/TinyMediaPlayer/docs/YouTubePlayer.mjs +480 -0
  37. package/dist/v1/libs/TinyMediaPlayer/index.cjs +165 -0
  38. package/dist/v1/libs/TinyMediaPlayer/index.d.mts +102 -0
  39. package/dist/v1/libs/TinyMediaPlayer/index.mjs +144 -0
  40. package/dist/v1/libs/TinyMediaPlayer.cjs +589 -103
  41. package/dist/v1/libs/TinyMediaPlayer.d.mts +150 -65
  42. package/dist/v1/libs/TinyMediaPlayer.mjs +535 -93
  43. package/dist/v1/libs/TinyTimeout.cjs +4 -14
  44. package/dist/v1/libs/TinyTimeout.d.mts +1 -0
  45. package/dist/v1/libs/TinyTimeout.mjs +4 -13
  46. package/docs/v1/basics/mediaContent.md +1 -1
  47. package/docs/v1/basics/objChecker.md +13 -0
  48. package/docs/v1/basics/promiseUtils.md +32 -0
  49. package/docs/v1/libs/TinyMediaPlayer.md +239 -32
  50. package/docs/v1/libs/TinyTimeout.md +1 -14
  51. package/package.json +21 -1
@@ -1,5 +1,6 @@
1
1
  'use strict';
2
2
 
3
+ var promiseUtils = require('../basics/promiseUtils.cjs');
3
4
  var utils = require('./utils.cjs');
4
5
 
5
6
  const checkDestroy = utils.createCheckDestroyed('TinyTimeout');
@@ -152,21 +153,10 @@ class TinyTimeout {
152
153
  * @param {number} [checkInterval=100] - How often (in ms) to check the condition.
153
154
  * @returns {Promise<void>} Resolves when the condition is met.
154
155
  * @throws {TypeError} If arguments are invalid.
156
+ * @deprecated Script migrated to `src/v1/basics/promiseUtils.mjs`.
155
157
  */
156
158
  static waitForTrue(getValue, checkInterval = 100) {
157
- if (typeof getValue !== 'function')
158
- throw new TypeError(`Expected 'getValue' to be a function.`);
159
- if (!Number.isFinite(checkInterval) || checkInterval <= 0)
160
- throw new TypeError(`Expected 'checkInterval' to be a positive number.`);
161
-
162
- return new Promise((resolve) => {
163
- const interval = setInterval(() => {
164
- if (getValue()) {
165
- clearInterval(interval);
166
- resolve();
167
- }
168
- }, checkInterval);
169
- });
159
+ return promiseUtils.waitForTrue(getValue, checkInterval);
170
160
  }
171
161
 
172
162
  /**
@@ -185,7 +175,7 @@ class TinyTimeout {
185
175
  throw new TypeError(`Expected 'getValue' to be a function.`);
186
176
  if (checkInterval !== null && (!Number.isFinite(checkInterval) || checkInterval <= 0))
187
177
  throw new TypeError(`Expected 'checkInterval' to be null or a positive number.`);
188
- return TinyTimeout.waitForTrue(getValue, checkInterval ?? this.#cooldownWatcherTime);
178
+ return promiseUtils.waitForTrue(getValue, checkInterval ?? this.#cooldownWatcherTime);
189
179
  }
190
180
 
191
181
  /**
@@ -67,6 +67,7 @@ declare class TinyTimeout {
67
67
  * @param {number} [checkInterval=100] - How often (in ms) to check the condition.
68
68
  * @returns {Promise<void>} Resolves when the condition is met.
69
69
  * @throws {TypeError} If arguments are invalid.
70
+ * @deprecated Script migrated to `src/v1/basics/promiseUtils.mjs`.
70
71
  */
71
72
  static waitForTrue(getValue: () => boolean, checkInterval?: number): Promise<void>;
72
73
  /**
@@ -1,3 +1,4 @@
1
+ import { waitForTrue } from '../basics/promiseUtils.mjs';
1
2
  import { createCheckDestroyed } from './utils.mjs';
2
3
  const checkDestroy = createCheckDestroyed('TinyTimeout');
3
4
  /**
@@ -136,20 +137,10 @@ class TinyTimeout {
136
137
  * @param {number} [checkInterval=100] - How often (in ms) to check the condition.
137
138
  * @returns {Promise<void>} Resolves when the condition is met.
138
139
  * @throws {TypeError} If arguments are invalid.
140
+ * @deprecated Script migrated to `src/v1/basics/promiseUtils.mjs`.
139
141
  */
140
142
  static waitForTrue(getValue, checkInterval = 100) {
141
- if (typeof getValue !== 'function')
142
- throw new TypeError(`Expected 'getValue' to be a function.`);
143
- if (!Number.isFinite(checkInterval) || checkInterval <= 0)
144
- throw new TypeError(`Expected 'checkInterval' to be a positive number.`);
145
- return new Promise((resolve) => {
146
- const interval = setInterval(() => {
147
- if (getValue()) {
148
- clearInterval(interval);
149
- resolve();
150
- }
151
- }, checkInterval);
152
- });
143
+ return waitForTrue(getValue, checkInterval);
153
144
  }
154
145
  /**
155
146
  * Instance version of `waitForTrue`, which defaults to using the instance's
@@ -168,7 +159,7 @@ class TinyTimeout {
168
159
  throw new TypeError(`Expected 'getValue' to be a function.`);
169
160
  if (checkInterval !== null && (!Number.isFinite(checkInterval) || checkInterval <= 0))
170
161
  throw new TypeError(`Expected 'checkInterval' to be null or a positive number.`);
171
- return TinyTimeout.waitForTrue(getValue, checkInterval ?? this.#cooldownWatcherTime);
162
+ return waitForTrue(getValue, checkInterval ?? this.#cooldownWatcherTime);
172
163
  }
173
164
  /**
174
165
  * Cleans up all internal references and stops the cooldown watcher.
@@ -13,7 +13,7 @@ These definitions ensure type safety and provide a clear blueprint for how media
13
13
  | Type | Description |
14
14
  | :--- | :--- |
15
15
  | `MediaContent` | **The Final Object.** A combination of `MediaContentBase` and `MediaContentMetadata`. This is what the system uses for playback. |
16
- | `MediaContentBase` | The essential properties: `id`, `title`, `artist`, `duration`, `url`, and `weight`. |
16
+ | `MediaContentBase` | The essential properties: `id`, `title`, `artist`, `duration`, `url`, `weight`, and an optional `mediaType` (e.g., 'audio', 'video') to facilitate content filtering when using both `HtmlAudioAdapter` and `HtmlVideoAdapter` within a `TinyMediaPlayer`. |
17
17
  | `MediaContentMetadata` | Rich metadata modeled after `music-metadata`. Includes `album`, `genre`, `year`, `track` info, etc. |
18
18
  | `MediaNumber` | A structure for indexing: `{ no: number \| null, of: number \| null }`. |
19
19
  | `UnknownArtistGetter` | A type that can be a `string` or a function returning a `string`. |
@@ -20,6 +20,19 @@ countObj('hi'); // Throws TypeError
20
20
 
21
21
  ---
22
22
 
23
+ ### 🧼 `isValidObj(value)`
24
+
25
+ Check if a value is a **JSON-compatible object** — meaning it's **not** a Array.
26
+
27
+ 🔒 This function ensures the object:
28
+
29
+ * is not `null`
30
+ * has `typeof === 'object'`
31
+
32
+ Use this when you need to strictly validate a JSON object.
33
+
34
+ ---
35
+
23
36
  ### 🧼 `isJsonObject(value)`
24
37
 
25
38
  Check if a value is a **plain JSON-compatible object** — meaning it's created via `{}` or `new Object()`, with a prototype of `Object.prototype`, and **not** a special object like `Date`, `Map`, `Array`, etc.
@@ -0,0 +1,32 @@
1
+ ### 🔁 `waitForTrue(getValue, checkInterval = 100)`
2
+
3
+ Waits until a provided function returns `true`, polling periodically.
4
+
5
+ ```ts
6
+ static waitForTrue(getValue: () => boolean, checkInterval?: number): Promise<void>
7
+ ```
8
+
9
+ * `getValue`: Function that returns `true` when the wait should end.
10
+ * `checkInterval`: Polling interval in milliseconds.
11
+
12
+ ---
13
+
14
+ ### 🔒 `createSingletonTask(baseFunction)`
15
+
16
+ Creates a controller object that ensures an asynchronous function is only executed once at a time. If a task is already in progress, subsequent calls to `callback` will wait for the existing promise to resolve.
17
+
18
+ ```ts
19
+ createSingletonTask<T, A extends any[]>(
20
+ baseFunction: (...args: A) => Promise<T>
21
+ ): {
22
+ callback: (...args: A) => Promise<T>;
23
+ getActivePromise: () => Promise<T> | null;
24
+ resetActivePromise: () => void;
25
+ }
26
+ ```
27
+
28
+ * `callback`: The wrapped function that manages execution state.
29
+ * `getActivePromise`: Returns the current active promise, or `null` if no task is running.
30
+ * `resetActivePromise`: Forces the state to reset, allowing the next call to start a new execution.
31
+
32
+ > **Note:** This utility is designed for **request collapsing** (all callers receive the same result). If you need to execute multiple independent tasks sequentially where each call returns its own unique result, please refer to the documentation for `TinyPromiseQueue.mjs`.
@@ -23,34 +23,167 @@ To support different media platforms, `TinyMediaPlayer` relies on **Adapters**.
23
23
 
24
24
  ---
25
25
 
26
+ ### 🚀 Implementation Guide
27
+
28
+ You can integrate `TinyMediaPlayer` into your project using two methods: using the ready-to-use adapters provided in the library, or creating your own custom adapters for unique services.
29
+
30
+ #### 📦 Option 1: Using Pre-built Adapters
31
+ The library provides several optimized adapters for common media sources. You can import them and register them directly with your player instance.
32
+
33
+ **Available Adapters:**
34
+ * `YoutubeMediaAdapter` from `tiny-essentials/libs/TinyMediaPlayer/Youtube`
35
+ * `HtmlAudioAdapter` from `tiny-essentials/libs/TinyMediaPlayer/HtmlAudio`
36
+ * `HtmlVideoAdapter` from `tiny-essentials/libs/TinyMediaPlayer/HtmlVideo`
37
+
38
+ **Example Usage:**
39
+ ```javascript
40
+ import TinyMediaPlayer from 'tiny-essentials/libs/TinyMediaPlayer';
41
+ import { YoutubeMediaAdapter } from 'tiny-essentials/libs/TinyMediaPlayer/Youtube';
42
+ import { HtmlAudioAdapter } from 'tiny-essentials/libs/TinyMediaPlayer/HtmlAudio';
43
+
44
+ const player = new TinyMediaPlayer({
45
+ persistVolume: true
46
+ });
47
+
48
+ YoutubeMediaAdapter.defaultContainer = YoutubeMediaAdapter.createIframeContainer({
49
+ videoId: 'fzKvGbQ9SgY',
50
+ hidden: true,
51
+ autoplay: false,
52
+ });
53
+
54
+ // Register the ready-made adapters
55
+ player.registerAdapter(new YoutubeMediaAdapter());
56
+ player.registerAdapter(new HtmlAudioAdapter());
57
+
58
+ // The player will now automatically select the correct adapter
59
+ // based on the content provided.
60
+ ```
61
+
62
+ #### 🛠️ Option 2: Creating Custom Adapters
63
+ If you need to support a service not covered by the pre-built adapters, you can create a custom class by extending the `BaseMediaAdapter`.
64
+
65
+ **Example Implementation:**
66
+ ```javascript
67
+ import { BaseMediaAdapter } from 'tiny-essentials/libs/TinyMediaPlayer/Base';
68
+
69
+ /**
70
+ * A custom adapter for a hypothetical streaming service.
71
+ * @extends BaseMediaAdapter
72
+ */
73
+ class MyCustomServiceAdapter extends BaseMediaAdapter {
74
+ /**
75
+ * Determines if this adapter can play the given content.
76
+ * @param {MediaContent} content - The media content to check.
77
+ * @returns {boolean} - True if the URL matches the service.
78
+ */
79
+ canHandle(content) {
80
+ return content.url.startsWith('https://example.com/');
81
+ }
82
+
83
+ /**
84
+ * Starts playback for the provided content.
85
+ * @param {MediaContent} content - The content to play.
86
+ * @returns {Promise<void>}
87
+ */
88
+ async play(content) {
89
+ console.log(`Starting playback for: ${content.title}`);
90
+ // Logic to interface with the custom service API goes here
91
+ }
92
+
93
+ async pause() {
94
+ // Logic to pause custom service playback
95
+ }
96
+
97
+ async stop() {
98
+ // Logic to stop custom service playback
99
+ }
100
+
101
+ async seek(timeMs) {
102
+ // Logic to seek in custom service playback
103
+ }
104
+
105
+ getCurrentTime() {
106
+ // Logic to return current time from custom service
107
+ return 0;
108
+ }
109
+
110
+ setVolume(volume) {
111
+ // Logic to set volume on custom service
112
+ console.log(`Volume set to: ${volume}`);
113
+ }
114
+ }
115
+
116
+ // Registering the custom adapter
117
+ const player = new TinyMediaPlayer();
118
+ player.registerAdapter(new MyCustomServiceAdapter());
119
+ ```
120
+
121
+ ---
122
+
123
+ ## 📊 Data Types
124
+
125
+ ### `ContentTimeData`
126
+ This object is emitted during the `timeupdate` event and provides real-time playback information.
127
+
128
+ | Property | Type | Description |
129
+ | :--- | :--- | :--- |
130
+ | `total` | `number` | The total duration of the media in milliseconds. |
131
+ | `current` | `number` | The current playback position in milliseconds. |
132
+ | `remaining` | `number` | The remaining time until the media ends in milliseconds. |
133
+ | `playbackPercentage` | `number` | The percentage of the media that has been played (0 to 100). |
134
+
135
+ ### `SearchResult`
136
+ Returned by the `searchTrack` method.
137
+
138
+ | Property | Type | Description |
139
+ | :--- | :--- | :--- |
140
+ | `track` | `MediaContent` | The matched media content object. |
141
+ | `index` | `number` | The current index of the track in the playlist. |
142
+
143
+ ---
144
+
26
145
  ## 🚀 `TinyMediaPlayer` Class
27
146
 
28
147
  The main controller for managing playlists, playback state, and adapters.
29
148
 
30
149
  ### ⚙️ Configuration (Constructor Options)
31
- When initializing `new TinyMediaPlayer(options)`, you can pass the following:
150
+ When initializing `new TinyMediaPlayer(options)`, you can pass the following `TinyMediaPlayerOptions`:
32
151
 
33
- * **`persistVolume`** (`boolean`): If `true`, the volume level is automatically saved to and loaded from `localStorage`. (Default: `false`).
34
- * **`volumeStorageKey`** (`string`): The key name used in `localStorage` to store the volume. (Default: `'tiny_media_player_volume'`).
152
+ * **`persistVolume`** (`boolean`): If `true`, the volume level is automatically saved to and loaded from `localStorage`. (Default: `false`).
153
+ * **`volumeStorageKey`** (`string`): The key name used in `localStorage` to store the volume. (Default: `'tiny_media_player_volume'`).
154
+ * **`repeatCurrentOnPrev`** (`boolean`): If `true`, clicking 'previous' repeats the current track on the first click. (Default: `false`).
155
+ * **`smoothPlayPauseVolume`** (`boolean`): If `true`, volume fades smoothly during play/pause transitions. (Default: `false`).
156
+ * **`smoothStopVolume`** (`boolean`): If `true`, volume fades smoothly to zero when stopping. (Default: `false`).
35
157
 
36
158
  ---
37
159
 
38
160
  ### 🏗️ Static Members
39
161
 
40
162
  #### Properties
41
- * **`unknownArtist`**: A global setting for when an artist's name is unavailable.
42
- * **Type**: `string` | `function` (returns a string).
43
- * **Getter/Setter**: Allows you to define a custom string or a logic-based function.
163
+ * **`BaseMediaAdapter`**: A reference to the `BaseMediaAdapter` class (useful for `instanceof` checks).
164
+ * **`unknownArtist`**: A global setting for when an artist's name is unavailable.
165
+ * **Type**: `string` | `function` (returns a string).
166
+ * **Getter/Setter**: Allows you to define a custom string or a logic-based function.
167
+ * **`defaultAdapterEventNames`**: An array of strings representing the events the player listens to by default on all registered adapters.
168
+ * **Type**: `string[]` (e.g., `['timeupdate', 'ended']`).
169
+ * **Getter/Setter**: Allows customization of the default event subscription list.
44
170
 
45
171
  #### Methods
46
- * **`parseContent(source, defaultMetadata, metadata, parseFile, callbacks)`**:
47
- * A static factory method to prepare a `MediaContent` object by extracting metadata from a URL or HTMLMediaElement.
48
- * **Returns**: `Promise<MediaContent>`.
172
+ * **`parseContent(source, defaultMetadata, metadata, parseFile, callbacks)`**:
173
+ * A static factory method to prepare a `MediaContent` object by extracting metadata from a URL or HTMLMediaElement.
174
+ * **Parameters**:
175
+ * `source`: `string | HTMLMediaElement`
176
+ * `defaultMetadata`: `Partial<MediaContentBase & MediaContentMetadata>`
177
+ * `metadata`: `Partial<MediaContentBase & MediaContentMetadata>`
178
+ * `parseFile`: `ParseMediaContentMetadata`
179
+ * `callbacks`: `Object` (containing `onProgress` and `onError`)
180
+ * **Returns**: `Promise<MediaContent>`.
49
181
 
50
182
  ---
51
183
 
52
184
  ### 📊 Instance Properties (Getters & Setters)
53
185
 
186
+ #### 🔄 State & Playback
54
187
  | Property | Type | Description |
55
188
  | :--- | :--- | :--- |
56
189
  | `playlist` | `MediaContent[]` | Returns a shallow copy of the current playlist. |
@@ -58,43 +191,117 @@ When initializing `new TinyMediaPlayer(options)`, you can pass the following:
58
191
  | `loopMode` | `'NONE' \| 'TRACK' \| 'PLAYLIST'` | Defines how the player behaves at the end of a track. |
59
192
  | `isRandom` | `boolean` | Enables or disables shuffle mode. |
60
193
  | `isPlaying` | `boolean` | Indicates if media is currently playing. |
61
- | `volume` | `number` | The current volume (constrained between `0.0` and `1.0`). |
194
+ | `volume` | `number` | The current volume level (strictly between `0.0` and `1.0`). |
195
+
196
+ #### 🎨 UX & Customization
197
+ | Property | Type | Description |
198
+ | :--- | :--- | :--- |
199
+ | `repeatCurrentOnPrev` | `boolean` | Whether clicking 'previous' repeats the current track on first click. |
200
+ | `smoothPlayPauseVolume`| `boolean` | Enables smooth volume fading during play/pause. |
201
+ | `smoothStopVolume` | `boolean` | Enables smooth volume fading when stopping. |
202
+ | `fadeVolumeSpeed` | `number` | The duration (ms) of the volume fade transition (must be non-negative). |
203
+ | `prevClickTimeoutDuration`| `number` | The duration (ms) before the "repeat on prev" state resets (must be non-negative). |
62
204
  | `persistVolume` | `boolean` | Enables/disables volume saving to `localStorage`. |
63
- | `volumeStorageKey`| `string` | The key used for volume persistence. |
205
+ | `volumeStorageKey` | `string` | The key used for volume persistence (cannot be empty). |
206
+
207
+ #### 🛠️ Utility & Lifecycle
208
+ | Property | Type | Description |
209
+ | :--- | :--- | :--- |
210
+ | `destroyed` | `boolean` | Indicates if the instance has been destroyed. |
211
+ | `adapters` | `BaseMediaAdapter[]` | Returns an array of all registered adapters. |
212
+ | `adaptersSize` | `number` | The number of currently registered adapters. |
64
213
 
65
214
  ---
66
215
 
67
216
  ### 🕹️ Methods
68
217
 
69
218
  #### 🔌 Adapter Management
70
- * **`registerAdapter(id, adapter)`**: Registers a new provider.
71
- * `id`: A unique string (e.g., `'youtube'`).
72
- * `adapter`: An instance of a class extending `BaseMediaAdapter`.
219
+ * **`registerAdapter(adapter)`**: Registers a new provider.
220
+ * `adapter`: An instance of a class extending `BaseMediaAdapter`.
221
+ * **`removeAdapter(adapter)`**: Removes a specific registered adapter.
222
+ * **`destroyAdapter(adapter)`**: Destroys and removes a specific registered adapter.
223
+ * **`hasAdapter(adapter)`**: Checks if a specific adapter is registered.
224
+ * **`clearAdapters()`**: Clears the list of adapters (does not call `destroy` on them).
225
+ * **`destroyAllAdapters()`**: Destroys and removes all registered media adapters.
226
+ * **`getMediaAdapter(content)`**: Finds the compatible adapter for the provided content.
227
+
228
+ #### 📡 Event Management (Controlled)
229
+ *Use these methods to manage which events from the underlying adapters are proxied to the `TinyMediaPlayer` instance.*
230
+
231
+ * **`addAdapterEvent(eventName)`**: Subscribes the player to a new event type from all registered adapters.
232
+ * `eventName`: `string`.
233
+ * **`removeAdapterEvent(eventName)`**: Unsubscribes the player from a specific event type from all registered adapters.
234
+ * `eventName`: `string`.
235
+ * **`resetAdapterEvents()`**: Restores the event listener list to the default state defined by `TinyMediaPlayer.defaultAdapterEventNames`.
73
236
 
74
237
  #### 📋 Playlist Management
75
- * **`addTrack(content)`**: Adds a new `MediaContent` object to the end of the playlist.
76
- * **`existsTrack(index)`**: Returns `true` if a track exists at the given index.
77
- * **`getTrack(index)`**: Retrieves the track at the specified index.
78
- * **`removeTrack(index)`**: Removes a track. If the current track is removed, playback stops or adjusts.
79
- * **`searchTrack(query)`**: Searches the playlist.
80
- * `query`: A `string` (searches title, artist, or album) or a `function` (custom logic).
81
- * **Returns**: `SearchResult[]` (array of objects containing the track and its index).
82
- * **`clearPlaylist()`**: Stops playback and empties the playlist.
238
+ * **`addTrack(content)`**: Adds a new `MediaContent` object to the end of the playlist.
239
+ * **Returns**: `number` (the new length of the playlist).
240
+ * **`existsTrack(index)`**: Returns `true` if the track exists at the given index.
241
+ * **`getTrack(index)`**: Retrieves the track at the specified index.
242
+ * **`removeTrack(index)`**: **(Async)** Removes a track and adjusts the current index/playback accordingly.
243
+ * **`searchTrack(query)`**: Searches the playlist.
244
+ * `query`: A `string` (searches title, artist, or album) or a `function` (custom logic).
245
+ * **Returns**: `SearchResult[]` (array of objects containing the track and their corresponding indices).
246
+ * **`clearPlaylist()`**: **(Async)** Stops playback and empties the entire playlist.
83
247
 
84
248
  #### ⏯️ Playback Controls
85
- * **`play()`**: Starts playback of the current track.
86
- * **`pause()`**: Pauses the current track.
87
- * **`stop()`**: Stops the current track completely.
88
- * **`next()`**: Advances to the next track (respects `loopMode` and `isRandom`).
89
- * **`prev()`**: Returns to the previous track (respects `loopMode` and `isRandom`).
90
- * **`seek(timeMs)`**: Jumps to a specific millisecond in the current track.
91
- * **`step(stepMs)`**: Moves the timeline forward (positive) or backward (negative) by a specific amount.
249
+ * **`play()`**: **(Async)** Starts playback of the current track.
250
+ * **`pause()`**: **(Async)** Pauses the current track.
251
+ * **`stop()`**: **(Async)** Stops the current track completely.
252
+ * **`next()`**: **(Async)** Advances to the next track (respects `loopMode` and `isRandom`).
253
+ * **`prev()`**: **(Async)** Returns to the previous track (respects `loopMode` and `isRandom`).
254
+ * **`seek(timeMs)`**: **(Async)** Jumps to a specific millisecond in the current track.
255
+ * **`step(stepMs)`**: **(Async)** Moves the timeline forward (positive) or backward (negative) by a specified amount.
256
+
257
+ #### ⏳ Time Utilities
258
+ * **`getCurrentTime()`**: Gets the current playback time in milliseconds.
259
+ * **`getTotalDuration()`**: Gets the total duration of the current track in milliseconds.
260
+ * **`getRemainingTime()`**: Gets the remaining time until the current track ends in milliseconds.
261
+ * **`getPlaybackPercentage()`**: Gets the percentage of the current track that has been played (0-100).
262
+
263
+ #### ♻️ Lifecycle
264
+ * **`destroy()`**: **(Async)** Safely stops playback, clears state, removes adapters, and detaches all listeners to prevent memory leaks.
265
+
266
+ ---
267
+
268
+ ## 📡 Emitted Events
269
+
270
+ `TinyMediaPlayer` extends `EventEmitter`. You can listen to the following events.
271
+
272
+ ### 🎶 Playback Events
273
+ | Event | Data Type | Description |
274
+ | :--- | :--- | :--- |
275
+ | `play` | `number` | Emitted when playback starts. Returns the `currentIndex`. |
276
+ | `pause` | `number` | Emitted when playback is paused. Returns the `currentIndex`. |
277
+ | `stop` | `number` | Emitted when playback is stopped. Returns the `currentIndex`. |
278
+ | `seek` | `number` | Emitted when the timeline is jumped to a new time (in ms). |
279
+ | `timeupdate` | `ContentTimeData` | Emitted by the adapter every time the playback position changes (in ms). |
280
+
281
+ ### 🔄 State & Configuration Events
282
+ | Event | Data Type | Description |
283
+ | :--- | :--- | :--- |
284
+ | `playlistUpdate` | `MediaContent[]` | Emitted when the playlist is modified. |
285
+ | `trackChange` | `number` | Emitted when the `currentIndex` changes. |
286
+ | `volumeChange` | `number` | Emitted when the volume is updated (0.0 to 1.0). |
287
+ | `isRandomChange` | `boolean` | Emitted when the random mode is toggled. |
288
+ | `loopModeChange` | `LoopModeType` | Emitted when the loop mode is changed. |
289
+ | `repeatCurrentOnPrevChange` | `boolean` | Emitted when the "repeat on prev" setting is toggled. |
290
+ | `smoothPlayPauseVolumeChange` | `boolean` | Emitted when the smooth play/pause volume setting is toggled. |
291
+ | `smoothStopVolumeChange` | `boolean` | Emitted when the smooth stop volume setting is toggled. |
292
+ | `fadeVolumeSpeedChange` | `number` | Emitted when the volume fade speed is updated. |
293
+ | `prevClickTimeoutDurationChange` | `number` | Emitted when the "repeat on prev" timeout duration is updated. |
294
+
295
+ ### 💀 Lifecycle Events
296
+ | Event | Data Type | Description |
297
+ | :--- | :--- | :--- |
298
+ | `destroyed` | `void` | Emitted when the `destroy()` method has finished executing. |
92
299
 
93
300
  ---
94
301
 
95
302
  ## ⚠️ Error Handling
96
303
 
97
304
  The class uses strict validation. The following errors may be thrown:
98
- * **`TypeError`**: Thrown when an argument is of the wrong type (e.g., passing a string to `volume`).
99
- * **`RangeError`**: Thrown when a number is out of allowed bounds (e.g., an index that doesn't exist or volume > 1.0).
100
- * **`Error`**: Thrown if no compatible adapter is found for the current content.
305
+ * **`TypeError`**: Thrown when an argument is of the wrong type (e.g., invalid index type, non-boolean options, invalid adapter instance, or empty storage key).
306
+ * **`RangeError`**: Thrown when a number is out of allowed bounds (e.g., negative time, volume outside `0.0`-`1.0`, negative durations, or index out of playlist bounds).
307
+ * **`Error`**: Thrown if no compatible adapter is found for the current content.
@@ -78,20 +78,7 @@ A numeric ID returned by `setTimeout`, which can be manually canceled later usin
78
78
 
79
79
  ---
80
80
 
81
- ### 🔁 `waitForTrue(getValue, checkInterval = 100)`
82
-
83
- Waits until a provided function returns `true`, polling periodically.
84
-
85
- ```ts
86
- static waitForTrue(getValue: () => boolean, checkInterval?: number): Promise<void>
87
- ```
88
-
89
- * `getValue`: Function that returns `true` when the wait should end.
90
- * `checkInterval`: Polling interval in milliseconds.
91
-
92
- ---
93
-
94
- ### 🧩 `waitForTrue(getValue, checkInterval?)` *(instance version)*
81
+ ### 🧩 `waitForTrue(getValue, checkInterval?)`
95
82
 
96
83
  Same as `waitForTrue`, but uses the instance's `cooldownWatcherTime` as the default if `checkInterval` is `null`.
97
84
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tiny-essentials",
3
- "version": "1.28.4",
3
+ "version": "1.28.5",
4
4
  "description": "Collection of small, essential scripts designed to be used across various projects. These simple utilities are crafted for speed, ease of use, and versatility.",
5
5
  "bin": {
6
6
  "tiny-essentials-fork": "./TinyFork.mjs"
@@ -132,6 +132,26 @@
132
132
  "require": "./dist/v1/libs/TinyMediaPlayer.cjs",
133
133
  "import": "./dist/v1/libs/TinyMediaPlayer.mjs"
134
134
  },
135
+ "./libs/TinyMediaPlayer/Base": {
136
+ "require": "./dist/v1/libs/TinyMediaPlayer/index.cjs",
137
+ "import": "./dist/v1/libs/TinyMediaPlayer/index.mjs"
138
+ },
139
+ "./libs/TinyMediaPlayer/Youtube": {
140
+ "require": "./dist/v1/libs/TinyMediaPlayer/Youtube.cjs",
141
+ "import": "./dist/v1/libs/TinyMediaPlayer/Youtube.mjs"
142
+ },
143
+ "./libs/TinyMediaPlayer/HtmlAudio": {
144
+ "require": "./dist/v1/libs/TinyMediaPlayer/HtmlAudio.cjs",
145
+ "import": "./dist/v1/libs/TinyMediaPlayer/HtmlAudio.mjs"
146
+ },
147
+ "./libs/TinyMediaPlayer/HtmlVideo": {
148
+ "require": "./dist/v1/libs/TinyMediaPlayer/HtmlVideo.cjs",
149
+ "import": "./dist/v1/libs/TinyMediaPlayer/HtmlVideo.mjs"
150
+ },
151
+ "./libs/TinyMediaPlayer/docs/YouTubePlayer": {
152
+ "require": "./dist/v1/libs/TinyMediaPlayer/docs/YouTubePlayer.cjs",
153
+ "import": "./dist/v1/libs/TinyMediaPlayer/docs/YouTubePlayer.mjs"
154
+ },
135
155
  "./libs/TinyRadioFm": {
136
156
  "require": "./dist/v1/libs/TinyRadioFm.cjs",
137
157
  "import": "./dist/v1/libs/TinyRadioFm.mjs"