tiny-essentials 1.28.4 → 1.28.6

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 (66) hide show
  1. package/README.md +18 -1
  2. package/changelog/1/28/5.md +31 -0
  3. package/changelog/1/28/6.md +11 -0
  4. package/dist/v1/TinyAdvancedRaffle.min.js +1 -1
  5. package/dist/v1/TinyBasicsEs.min.js +1 -1
  6. package/dist/v1/TinyEssentials.min.js +1 -1
  7. package/dist/v1/TinyIframeEvents.min.js +1 -1
  8. package/dist/v1/TinyLocalStorage.min.js +1 -1
  9. package/dist/v1/TinyMediaPlayer.min.js +1 -1
  10. package/dist/v1/TinyNewWinEvents.min.js +1 -1
  11. package/dist/v1/TinyRadioFm.min.js +1 -1
  12. package/dist/v1/TinySmartScroller.min.js +1 -1
  13. package/dist/v1/TinyTimeout.min.js +1 -1
  14. package/dist/v1/TinyUploadClicker.min.js +1 -1
  15. package/dist/v1/basics/index.cjs +4 -0
  16. package/dist/v1/basics/index.d.mts +3 -2
  17. package/dist/v1/basics/index.mjs +3 -2
  18. package/dist/v1/basics/mediaContent.cjs +1 -0
  19. package/dist/v1/basics/mediaContent.d.mts +4 -0
  20. package/dist/v1/basics/mediaContent.mjs +1 -0
  21. package/dist/v1/basics/objChecker.cjs +16 -0
  22. package/dist/v1/basics/objChecker.d.mts +12 -0
  23. package/dist/v1/basics/objChecker.mjs +14 -0
  24. package/dist/v1/basics/objFilter.cjs +0 -2
  25. package/dist/v1/basics/objFilter.d.mts +0 -2
  26. package/dist/v1/basics/objFilter.mjs +1 -2
  27. package/dist/v1/basics/promiseUtils.cjs +101 -0
  28. package/dist/v1/basics/promiseUtils.d.mts +31 -0
  29. package/dist/v1/basics/promiseUtils.mjs +89 -0
  30. package/dist/v1/css/TinyLoadingScreen.min.css +1 -1
  31. package/dist/v1/index.cjs +4 -0
  32. package/dist/v1/index.d.mts +3 -2
  33. package/dist/v1/index.mjs +3 -2
  34. package/dist/v1/libs/TinyMediaPlayer/HtmlAudio.cjs +318 -0
  35. package/dist/v1/libs/TinyMediaPlayer/HtmlAudio.d.mts +143 -0
  36. package/dist/v1/libs/TinyMediaPlayer/HtmlAudio.mjs +283 -0
  37. package/dist/v1/libs/TinyMediaPlayer/HtmlVideo.cjs +315 -0
  38. package/dist/v1/libs/TinyMediaPlayer/HtmlVideo.d.mts +143 -0
  39. package/dist/v1/libs/TinyMediaPlayer/HtmlVideo.mjs +280 -0
  40. package/dist/v1/libs/TinyMediaPlayer/SoundCloud.cjs +792 -0
  41. package/dist/v1/libs/TinyMediaPlayer/SoundCloud.d.mts +300 -0
  42. package/dist/v1/libs/TinyMediaPlayer/SoundCloud.mjs +697 -0
  43. package/dist/v1/libs/TinyMediaPlayer/Youtube.cjs +783 -0
  44. package/dist/v1/libs/TinyMediaPlayer/Youtube.d.mts +355 -0
  45. package/dist/v1/libs/TinyMediaPlayer/Youtube.mjs +694 -0
  46. package/dist/v1/libs/TinyMediaPlayer/docs/SoundCloudWidget.cjs +381 -0
  47. package/dist/v1/libs/TinyMediaPlayer/docs/SoundCloudWidget.d.mts +704 -0
  48. package/dist/v1/libs/TinyMediaPlayer/docs/SoundCloudWidget.mjs +343 -0
  49. package/dist/v1/libs/TinyMediaPlayer/docs/YouTubePlayer.cjs +546 -0
  50. package/dist/v1/libs/TinyMediaPlayer/docs/YouTubePlayer.d.mts +657 -0
  51. package/dist/v1/libs/TinyMediaPlayer/docs/YouTubePlayer.mjs +480 -0
  52. package/dist/v1/libs/TinyMediaPlayer/index.cjs +216 -0
  53. package/dist/v1/libs/TinyMediaPlayer/index.d.mts +162 -0
  54. package/dist/v1/libs/TinyMediaPlayer/index.mjs +190 -0
  55. package/dist/v1/libs/TinyMediaPlayer.cjs +627 -103
  56. package/dist/v1/libs/TinyMediaPlayer.d.mts +163 -65
  57. package/dist/v1/libs/TinyMediaPlayer.mjs +573 -93
  58. package/dist/v1/libs/TinyTimeout.cjs +4 -14
  59. package/dist/v1/libs/TinyTimeout.d.mts +1 -0
  60. package/dist/v1/libs/TinyTimeout.mjs +4 -13
  61. package/docs/v1/basics/mediaContent.md +1 -1
  62. package/docs/v1/basics/objChecker.md +13 -0
  63. package/docs/v1/basics/promiseUtils.md +32 -0
  64. package/docs/v1/libs/TinyMediaPlayer.md +268 -39
  65. package/docs/v1/libs/TinyTimeout.md +1 -14
  66. package/package.json +71 -10
@@ -0,0 +1,283 @@
1
+ /**
2
+ * @typedef {import('../../basics/mediaContent.mjs').PictureDataType} PictureDataType
3
+ * @typedef {import('../../basics/mediaContent.mjs').MediaContent<PictureDataType>} MediaContent
4
+ * @typedef {import('./index.mjs').ContentTimeData} ContentTimeData
5
+ * @typedef {import('./index.mjs').ContentData} ContentData
6
+ */
7
+ import { isValidObj } from '../../basics/objChecker.mjs';
8
+ import { createCheckDestroyed } from '../utils.mjs';
9
+ import { BaseMediaAdapter } from './index.mjs';
10
+ const checkDestroy = createCheckDestroyed('HtmlAudioAdapter');
11
+ /**
12
+ * Concrete implementation of the BaseMediaAdapter using HTML5 Audio.
13
+ * This class wraps the HTMLAudioElement and provides an adapter interface.
14
+ */
15
+ class HtmlAudioAdapter extends BaseMediaAdapter {
16
+ /** @type {boolean} */
17
+ #destroyed = false;
18
+ get id() {
19
+ return 'html5Audio';
20
+ }
21
+ /**
22
+ * The internal HTMLAudioElement instance.
23
+ * @type {HTMLAudioElement}
24
+ */
25
+ #audioElement = new Audio();
26
+ /** @type {string|null} */
27
+ #currentContentId = null;
28
+ get currentContentId() {
29
+ return this.#currentContentId;
30
+ }
31
+ /**
32
+ * Gets whether the adapter has been destroyed.
33
+ * @returns {boolean}
34
+ */
35
+ get destroyed() {
36
+ return this.#destroyed;
37
+ }
38
+ /**
39
+ * Exposing the element to easily bind the 'ended' event in testing.
40
+ * @returns {HTMLAudioElement}
41
+ * @throws {Error} If the adapter has been destroyed.
42
+ */
43
+ get audioElement() {
44
+ checkDestroy(this.#destroyed);
45
+ return this.#audioElement;
46
+ }
47
+ /**
48
+ * Initializes a new instance of the HtmlAudioAdapter.
49
+ */
50
+ constructor() {
51
+ super();
52
+ this.#audioElement.addEventListener('play', () => this.emit('play'));
53
+ this.#audioElement.addEventListener('pause', () => this.emit('pause'));
54
+ this.#audioElement.addEventListener('ended', () => this.emit('ended'));
55
+ this.#audioElement.addEventListener('timeupdate', () => {
56
+ this.emit('timeupdate', this.getTimeData());
57
+ });
58
+ this.#audioElement.addEventListener('error', (error) => {
59
+ this.emit('error', error);
60
+ });
61
+ this.#audioElement.addEventListener('seeked', () => {
62
+ this.emit('seek', this.getCurrentTime());
63
+ });
64
+ }
65
+ /**
66
+ * Synchronously checks whether the media adapter is fully initialized and ready.
67
+ * @returns {boolean} True if the adapter is ready, false otherwise.
68
+ */
69
+ isReady() {
70
+ checkDestroy(this.#destroyed);
71
+ return true;
72
+ }
73
+ /**
74
+ * Asynchronously waits until the content media adapter is fully initialized and ready.
75
+ * @returns {Promise<void>} A promise that resolves once the adapter is ready.
76
+ */
77
+ async waitIsReady() {
78
+ checkDestroy(this.#destroyed);
79
+ return;
80
+ }
81
+ /**
82
+ * Retrieves a consolidated object containing all time-related metrics for the current media content.
83
+ * @returns {ContentTimeData} An object containing total, current, remaining time, and playback percentage.
84
+ */
85
+ getTimeData() {
86
+ const total = this.getTotalDuration();
87
+ const current = this.getCurrentTime();
88
+ const remaining = total > 0 ? total - current : 0;
89
+ const playbackPercentage = total > 0 ? (current / total) * 100 : 0;
90
+ return { total, current, remaining, playbackPercentage };
91
+ }
92
+ /**
93
+ * Determines if the provided content is compatible with this adapter.
94
+ * @param {MediaContent} content - The media content object.
95
+ * @returns {boolean} True if the content is compatible, false otherwise.
96
+ * @throws {TypeError} If content is not a valid object.
97
+ */
98
+ static canHandle(content) {
99
+ if (!isValidObj(content)) {
100
+ throw new TypeError('Content must be a valid object.');
101
+ }
102
+ return (typeof content.url === 'string' &&
103
+ (content.url.startsWith('http') || content.url.startsWith('blob:')) &&
104
+ (content.mediaType === undefined || content.mediaType === 'audio'));
105
+ }
106
+ /**
107
+ * Determines if the provided content is compatible with this adapter.
108
+ * @param {MediaContent} content - The media content object.
109
+ * @returns {boolean} True if the content is compatible, false otherwise.
110
+ * @throws {TypeError} If content is not a valid object.
111
+ */
112
+ canHandle(content) {
113
+ checkDestroy(this.#destroyed);
114
+ return HtmlAudioAdapter.canHandle(content);
115
+ }
116
+ /**
117
+ * Plays the audio content provided via the URL.
118
+ * @param {MediaContent} content - The media content object containing the URL.
119
+ * @returns {Promise<void>} A promise that resolves when playback starts.
120
+ * @throws {TypeError} If the content URL is not a string.
121
+ */
122
+ async play(content) {
123
+ checkDestroy(this.#destroyed);
124
+ if (!isValidObj(content)) {
125
+ throw new TypeError('Content must be a valid object.');
126
+ }
127
+ if (typeof content?.url !== 'string') {
128
+ throw new TypeError('Valid media URL required.');
129
+ }
130
+ if (this.#audioElement.src !== content.url) {
131
+ this.#currentContentId = content.url;
132
+ this.#audioElement.src = content.url;
133
+ this.#audioElement.load();
134
+ }
135
+ await this.#audioElement.play();
136
+ }
137
+ /**
138
+ * Pauses the audio playback.
139
+ * @returns {Promise<void>} A promise that resolves when playback is paused.
140
+ */
141
+ async pause() {
142
+ checkDestroy(this.#destroyed);
143
+ this.#audioElement.pause();
144
+ }
145
+ /**
146
+ * Stops the audio playback and resets the current time to zero.
147
+ * @returns {Promise<void>} A promise that resolves when playback is stopped.
148
+ */
149
+ async stop() {
150
+ checkDestroy(this.#destroyed);
151
+ this.#audioElement.pause();
152
+ this.#audioElement.currentTime = 0;
153
+ }
154
+ /**
155
+ * Seeks to a specific time in the audio.
156
+ * @param {number} timeMs - The target time in milliseconds.
157
+ * @returns {Promise<void>} A promise that resolves when the seek operation is complete.
158
+ * @throws {TypeError} If timeMs is not a number.
159
+ */
160
+ async seek(timeMs) {
161
+ checkDestroy(this.#destroyed);
162
+ if (typeof timeMs !== 'number') {
163
+ throw new TypeError('Seek time must be a number.');
164
+ }
165
+ const currentTime = timeMs / 1000;
166
+ this.#audioElement.currentTime = currentTime;
167
+ }
168
+ /**
169
+ * Gets the current playback time.
170
+ * @returns {number} The current time in milliseconds.
171
+ */
172
+ getCurrentTime() {
173
+ checkDestroy(this.#destroyed);
174
+ return Math.floor(this.#audioElement.currentTime * 1000);
175
+ }
176
+ /**
177
+ * Gets the total duration of the audio.
178
+ * @returns {number} The total duration in milliseconds.
179
+ */
180
+ getTotalDuration() {
181
+ checkDestroy(this.#destroyed);
182
+ const duration = this.#audioElement.duration;
183
+ return Number.isFinite(duration) ? Math.floor(duration * 1000) : 0;
184
+ }
185
+ /**
186
+ * Gets the remaining time until the audio ends.
187
+ * @returns {number} The remaining time in milliseconds.
188
+ */
189
+ getRemainingTime() {
190
+ checkDestroy(this.#destroyed);
191
+ const total = this.getTotalDuration();
192
+ const current = this.getCurrentTime();
193
+ return total > 0 ? total - current : 0;
194
+ }
195
+ /**
196
+ * Gets the percentage of the audio that has been played.
197
+ * @returns {number} The percentage from 0 to 100.
198
+ */
199
+ getPlaybackPercentage() {
200
+ checkDestroy(this.#destroyed);
201
+ const total = this.getTotalDuration();
202
+ const current = this.getCurrentTime();
203
+ return total > 0 ? (current / total) * 100 : 0;
204
+ }
205
+ /**
206
+ * Sets the volume level of the audio.
207
+ * @param {number} volume - The volume level from 0.0 to 1.0.
208
+ * @throws {RangeError} If the volume is not between 0.0 and 1.0.
209
+ */
210
+ setVolume(volume) {
211
+ this.volume = volume;
212
+ }
213
+ /**
214
+ * Gets the volume level of the audio.
215
+ * @returns {number} The volume level from 0.0 to 1.0.
216
+ */
217
+ getVolume() {
218
+ return this.volume;
219
+ }
220
+ /**
221
+ * Gets the current volume level of the audio.
222
+ * @returns {number} The current volume level from 0.0 to 1.0.
223
+ */
224
+ get volume() {
225
+ checkDestroy(this.#destroyed);
226
+ return this.#audioElement.volume;
227
+ }
228
+ /**
229
+ * Sets the volume level.
230
+ * @param {number} value - The volume level from 0.0 to 1.0.
231
+ * @throws {RangeError} If the volume is not between 0.0 and 1.0.
232
+ */
233
+ set volume(value) {
234
+ checkDestroy(this.#destroyed);
235
+ if (typeof value !== 'number' || value < 0 || value > 1) {
236
+ throw new RangeError('Volume must be a number between 0.0 and 1.0.');
237
+ }
238
+ this.#audioElement.volume = value;
239
+ this.emit('volumeChange', value);
240
+ }
241
+ /**
242
+ * Retrieves the metadata of the currently loaded content, returning a structured
243
+ * object containing details such as the content ID, title, author, and duration.
244
+ * @returns {Promise<ContentData>}
245
+ */
246
+ async getContentData() {
247
+ return {
248
+ id: '',
249
+ createdAt: '',
250
+ artistId: '',
251
+ artistName: '',
252
+ description: '',
253
+ title: '',
254
+ duration: 0,
255
+ avatar: '',
256
+ url: '',
257
+ };
258
+ }
259
+ /**
260
+ * Cleans up the adapter resources and marks it as destroyed.
261
+ * @returns {Promise<void>}
262
+ */
263
+ async destroy() {
264
+ if (this.#destroyed)
265
+ return;
266
+ try {
267
+ this.#audioElement.pause();
268
+ this.#audioElement.src = '';
269
+ this.#currentContentId = null;
270
+ this.#audioElement.load();
271
+ }
272
+ catch (error) {
273
+ // We catch errors during cleanup to ensure the destruction process completes.
274
+ }
275
+ finally {
276
+ // @ts-ignore
277
+ this.#audioElement = null;
278
+ }
279
+ this.#destroyed = true;
280
+ super.destroy();
281
+ }
282
+ }
283
+ export { HtmlAudioAdapter };
@@ -0,0 +1,315 @@
1
+ 'use strict';
2
+
3
+ var objChecker = require('../../basics/objChecker.cjs');
4
+ var utils = require('../utils.cjs');
5
+ var index = require('./index.cjs');
6
+
7
+ /**
8
+ * @typedef {import('../../basics/mediaContent.mjs').PictureDataType} PictureDataType
9
+ * @typedef {import('../../basics/mediaContent.mjs').MediaContent<PictureDataType>} MediaContent
10
+ * @typedef {import('./index.mjs').ContentTimeData} ContentTimeData
11
+ * @typedef {import('./index.mjs').ContentData} ContentData
12
+ */
13
+
14
+
15
+ const checkDestroy = utils.createCheckDestroyed('HtmlVideoAdapter');
16
+
17
+ /**
18
+ * Concrete implementation of the BaseMediaAdapter using HTML5 Video.
19
+ * This class wraps the HTMLVideoElement and provides an adapter interface.
20
+ */
21
+ class HtmlVideoAdapter extends index.BaseMediaAdapter {
22
+ /** @type {boolean} */
23
+ #destroyed = false;
24
+
25
+ get id() {
26
+ return 'html5Video';
27
+ }
28
+
29
+ /** @type {HTMLVideoElement} */
30
+ #videoElement = document.createElement('video');
31
+
32
+ /** @type {string|null} */
33
+ #currentContentId = null;
34
+
35
+ get currentContentId() {
36
+ return this.#currentContentId;
37
+ }
38
+
39
+ /**
40
+ * Gets whether the adapter has been destroyed.
41
+ * @returns {boolean}
42
+ */
43
+ get destroyed() {
44
+ return this.#destroyed;
45
+ }
46
+
47
+ /**
48
+ * Exposing the element to easily bind the 'playing' event in testing.
49
+ * @returns {HTMLVideoElement}
50
+ * @throws {Error} If the adapter has been destroyed.
51
+ */
52
+ get videoElement() {
53
+ checkDestroy(this.#destroyed);
54
+ return this.#videoElement;
55
+ }
56
+
57
+ /**
58
+ * Initializes a new instance of the HtmlVideoAdapter.
59
+ */
60
+ constructor() {
61
+ super();
62
+
63
+ this.#videoElement.addEventListener('play', () => this.emit('play'));
64
+ this.#videoElement.addEventListener('pause', () => this.emit('pause'));
65
+ this.#videoElement.addEventListener('ended', () => this.emit('ended'));
66
+ this.#videoElement.addEventListener('timeupdate', () => {
67
+ this.emit('timeupdate', this.getTimeData());
68
+ });
69
+ this.#videoElement.addEventListener('error', (error) => {
70
+ this.emit('error', error);
71
+ });
72
+ this.#videoElement.addEventListener('seeked', () => {
73
+ this.emit('seek', this.getCurrentTime());
74
+ });
75
+ }
76
+
77
+ /**
78
+ * Synchronously checks whether the media adapter is fully initialized and ready.
79
+ * @returns {boolean} True if the adapter is ready, false otherwise.
80
+ */
81
+ isReady() {
82
+ checkDestroy(this.#destroyed);
83
+ return true;
84
+ }
85
+
86
+ /**
87
+ * Asynchronously waits until the content media adapter is fully initialized and ready.
88
+ * @returns {Promise<void>} A promise that resolves once the adapter is ready.
89
+ */
90
+ async waitIsReady() {
91
+ checkDestroy(this.#destroyed);
92
+ return;
93
+ }
94
+
95
+ /**
96
+ * Retrieves a consolidated object containing all time-related metrics for the current media content.
97
+ * @returns {ContentTimeData} An object containing total, current, remaining time, and playback percentage.
98
+ */
99
+ getTimeData() {
100
+ const total = this.getTotalDuration();
101
+ const current = this.getCurrentTime();
102
+ const remaining = total > 0 ? total - current : 0;
103
+ const playbackPercentage = total > 0 ? (current / total) * 100 : 0;
104
+ return { total, current, remaining, playbackPercentage };
105
+ }
106
+
107
+ /**
108
+ * Determines if the provided content is compatible with this adapter.
109
+ * @param {MediaContent} content - The media content object.
110
+ * @returns {boolean} True if the content is compatible, false otherwise.
111
+ * @throws {TypeError} If content is not a valid object.
112
+ */
113
+ static canHandle(content) {
114
+ if (!objChecker.isValidObj(content)) {
115
+ throw new TypeError('Content must be a valid object.');
116
+ }
117
+ return (
118
+ typeof content.url === 'string' &&
119
+ (content.url.startsWith('http') || content.url.startsWith('blob:')) &&
120
+ (content.mediaType === undefined || content.mediaType === 'video')
121
+ );
122
+ }
123
+
124
+ /**
125
+ * Determines if the provided content is compatible with this adapter.
126
+ * @param {MediaContent} content - The media content object.
127
+ * @returns {boolean} True if the content is compatible, false otherwise.
128
+ * @throws {TypeError} If content is not a valid object.
129
+ */
130
+ canHandle(content) {
131
+ checkDestroy(this.#destroyed);
132
+ return HtmlVideoAdapter.canHandle(content);
133
+ }
134
+
135
+ /**
136
+ * Plays the video content provided via the URL.
137
+ * @param {MediaContent} content - The media content object containing the URL.
138
+ * @returns {Promise<void>} A promise that resolves when playback starts.
139
+ * @throws {TypeError} If the content URL is not a string.
140
+ */
141
+ async play(content) {
142
+ checkDestroy(this.#destroyed);
143
+ if (!objChecker.isValidObj(content)) {
144
+ throw new TypeError('Content must be a valid object.');
145
+ }
146
+ if (typeof content?.url !== 'string') {
147
+ throw new TypeError('Valid media URL required.');
148
+ }
149
+
150
+ if (this.#videoElement.src !== content.url) {
151
+ this.#currentContentId = content.url;
152
+ this.#videoElement.src = content.url;
153
+ this.#videoElement.load();
154
+ }
155
+ await this.#videoElement.play();
156
+ }
157
+
158
+ /**
159
+ * Pauses the video playback.
160
+ * @returns {Promise<void>} A promise that resolves when playback is paused.
161
+ */
162
+ async pause() {
163
+ checkDestroy(this.#destroyed);
164
+ this.#videoElement.pause();
165
+ }
166
+
167
+ /**
168
+ * Stops the video playback and resets the current time to zero.
169
+ * @returns {Promise<void>} A promise that resolves when playback is stopped.
170
+ */
171
+ async stop() {
172
+ checkDestroy(this.#destroyed);
173
+ this.#videoElement.pause();
174
+ this.#videoElement.currentTime = 0;
175
+ }
176
+
177
+ /**
178
+ * Seeks to a specific time in the video.
179
+ * @param {number} timeMs - The target time in milliseconds.
180
+ * @returns {Promise<void>} A promise that resolves when the seek operation is complete.
181
+ * @throws {TypeError} If timeMs is not a number.
182
+ */
183
+ async seek(timeMs) {
184
+ checkDestroy(this.#destroyed);
185
+ if (typeof timeMs !== 'number') {
186
+ throw new TypeError('Seek time must be a number.');
187
+ }
188
+ const currentTime = timeMs / 1000;
189
+ this.#videoElement.currentTime = currentTime;
190
+ }
191
+
192
+ /**
193
+ * Gets the current playback time.
194
+ * @returns {number} The current time in milliseconds.
195
+ */
196
+ getCurrentTime() {
197
+ checkDestroy(this.#destroyed);
198
+ return Math.floor(this.#videoElement.currentTime * 1000);
199
+ }
200
+
201
+ /**
202
+ * Gets the total duration of the video.
203
+ * @returns {number} The total duration in milliseconds.
204
+ */
205
+ getTotalDuration() {
206
+ checkDestroy(this.#destroyed);
207
+ const duration = this.#videoElement.duration;
208
+ return Number.isFinite(duration) ? Math.floor(duration * 1000) : 0;
209
+ }
210
+
211
+ /**
212
+ * Gets the remaining time until the video ends.
213
+ * @returns {number} The remaining time in milliseconds.
214
+ */
215
+ getRemainingTime() {
216
+ checkDestroy(this.#destroyed);
217
+ const total = this.getTotalDuration();
218
+ const current = this.getCurrentTime();
219
+ return total > 0 ? total - current : 0;
220
+ }
221
+
222
+ /**
223
+ * Gets the percentage of the video that has been played.
224
+ * @returns {number} The percentage from 0 to 100.
225
+ */
226
+ getPlaybackPercentage() {
227
+ checkDestroy(this.#destroyed);
228
+ const total = this.getTotalDuration();
229
+ const current = this.getCurrentTime();
230
+ return total > 0 ? (current / total) * 100 : 0;
231
+ }
232
+
233
+ /**
234
+ * Sets the volume level of the video.
235
+ * @param {number} volume - The volume level from 0.0 to 1.0.
236
+ * @throws {RangeError} If the volume is not between 0.0 and 1.0.
237
+ */
238
+ setVolume(volume) {
239
+ this.volume = volume;
240
+ }
241
+
242
+ /**
243
+ * Gets the volume level of the video.
244
+ * @returns {number} The volume level from 0.0 to 1.0.
245
+ */
246
+ getVolume() {
247
+ return this.volume;
248
+ }
249
+
250
+ /**
251
+ * Gets the current volume level of the video.
252
+ * @returns {number} The volume level from 0.0 to 1.0.
253
+ */
254
+ get volume() {
255
+ checkDestroy(this.#destroyed);
256
+ return this.#videoElement.volume;
257
+ }
258
+
259
+ /**
260
+ * Sets the volume level.
261
+ * @param {number} value - The volume level from 0.0 to 1.0.
262
+ * @throws {RangeError} If the volume is not between 0.0 and 1.0.
263
+ */
264
+ set volume(value) {
265
+ checkDestroy(this.#destroyed);
266
+ if (typeof value !== 'number' || value < 0 || value > 1) {
267
+ throw new RangeError('Volume must be a number between 0.0 and 1.0.');
268
+ }
269
+ this.#videoElement.volume = value;
270
+ this.emit('volumeChange', value);
271
+ }
272
+
273
+ /**
274
+ * Retrieves the metadata of the currently loaded content, returning a structured
275
+ * object containing details such as the content ID, title, author, and duration.
276
+ * @returns {Promise<ContentData>}
277
+ */
278
+ async getContentData() {
279
+ return {
280
+ id: '',
281
+ createdAt: '',
282
+ artistId: '',
283
+ artistName: '',
284
+ description: '',
285
+ title: '',
286
+ duration: 0,
287
+ avatar: '',
288
+ url: '',
289
+ };
290
+ }
291
+
292
+ /**
293
+ * Cleans up the adapter resources and marks it as destroyed.
294
+ * @returns {Promise<void>}
295
+ */
296
+ async destroy() {
297
+ if (this.#destroyed) return;
298
+
299
+ try {
300
+ this.#videoElement.pause();
301
+ this.#videoElement.src = '';
302
+ this.#currentContentId = null;
303
+ this.#videoElement.load();
304
+ } catch (error) {
305
+ // We catch errors during cleanup to ensure the destruction process completes.
306
+ } finally {
307
+ // @ts-ignore
308
+ this.#videoElement = null;
309
+ }
310
+ this.#destroyed = true;
311
+ super.destroy();
312
+ }
313
+ }
314
+
315
+ exports.HtmlVideoAdapter = HtmlVideoAdapter;