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