mikuru 1.0.30 → 1.0.31
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.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.0.31 - 2026-05-16
|
|
4
|
+
|
|
5
|
+
- Exported `MikuruVideoPlayer` media events so parent components can listen for playback, timing, seeking, volume, and playback-rate changes with typed media state payloads.
|
|
6
|
+
- Exported matching `MikuruAudioPlayer` media events and documented the shared media player event payload.
|
|
7
|
+
|
|
3
8
|
## 1.0.30 - 2026-05-15
|
|
4
9
|
|
|
5
10
|
- Hardened `MikuruVideoPlayer` controls so stop, mute, playback speed, seeking, and modal close operations do not trigger recursive updates while browser media events are firing.
|
|
@@ -4,12 +4,15 @@
|
|
|
4
4
|
ref="mediaEl"
|
|
5
5
|
:src="src"
|
|
6
6
|
:preload="preload"
|
|
7
|
-
@loadedmetadata="
|
|
8
|
-
@timeupdate="
|
|
9
|
-
@durationchange="
|
|
7
|
+
@loadedmetadata="handleLoadedMetadata"
|
|
8
|
+
@timeupdate="handleTimeUpdate"
|
|
9
|
+
@durationchange="handleDurationChange"
|
|
10
10
|
@play="markPlaying"
|
|
11
11
|
@pause="markPaused"
|
|
12
|
-
@ended="
|
|
12
|
+
@ended="markEnded"
|
|
13
|
+
@seeked="handleSeeked"
|
|
14
|
+
@volumechange="handleVolumeChange"
|
|
15
|
+
@ratechange="handleRateChange"
|
|
13
16
|
></audio>
|
|
14
17
|
|
|
15
18
|
<div class="art">
|
|
@@ -61,6 +64,18 @@ const {
|
|
|
61
64
|
preload: String
|
|
62
65
|
});
|
|
63
66
|
|
|
67
|
+
const emit = defineEmits([
|
|
68
|
+
"loadedmetadata",
|
|
69
|
+
"timeupdate",
|
|
70
|
+
"durationchange",
|
|
71
|
+
"play",
|
|
72
|
+
"pause",
|
|
73
|
+
"ended",
|
|
74
|
+
"seeked",
|
|
75
|
+
"volumechange",
|
|
76
|
+
"ratechange"
|
|
77
|
+
]);
|
|
78
|
+
|
|
64
79
|
const mediaEl = ref(null);
|
|
65
80
|
const currentTime = ref(0);
|
|
66
81
|
const duration = ref(0);
|
|
@@ -107,6 +122,58 @@ function syncMedia() {
|
|
|
107
122
|
duration.value = Number.isFinite(media.duration) ? media.duration : 0;
|
|
108
123
|
}
|
|
109
124
|
|
|
125
|
+
function createMediaPayload(event) {
|
|
126
|
+
const media = getMedia();
|
|
127
|
+
if (!media) return null;
|
|
128
|
+
return {
|
|
129
|
+
currentTime: media.currentTime || 0,
|
|
130
|
+
duration: Number.isFinite(media.duration) ? media.duration : 0,
|
|
131
|
+
paused: media.paused,
|
|
132
|
+
ended: media.ended,
|
|
133
|
+
muted: media.muted,
|
|
134
|
+
volume: media.volume,
|
|
135
|
+
playbackRate: media.playbackRate,
|
|
136
|
+
nativeEvent: event
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function emitMediaPayload(event, dispatch) {
|
|
141
|
+
const payload = createMediaPayload(event);
|
|
142
|
+
if (payload) {
|
|
143
|
+
dispatch(payload);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function handleLoadedMetadata(event) {
|
|
148
|
+
syncMedia();
|
|
149
|
+
emitMediaPayload(event, (payload) => emit("loadedmetadata", payload));
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function handleTimeUpdate(event) {
|
|
153
|
+
syncMedia();
|
|
154
|
+
emitMediaPayload(event, (payload) => emit("timeupdate", payload));
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function handleDurationChange(event) {
|
|
158
|
+
syncMedia();
|
|
159
|
+
emitMediaPayload(event, (payload) => emit("durationchange", payload));
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function handleSeeked(event) {
|
|
163
|
+
syncMedia();
|
|
164
|
+
emitMediaPayload(event, (payload) => emit("seeked", payload));
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function handleVolumeChange(event) {
|
|
168
|
+
syncMedia();
|
|
169
|
+
emitMediaPayload(event, (payload) => emit("volumechange", payload));
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function handleRateChange(event) {
|
|
173
|
+
syncMedia();
|
|
174
|
+
emitMediaPayload(event, (payload) => emit("ratechange", payload));
|
|
175
|
+
}
|
|
176
|
+
|
|
110
177
|
function applyAudioSettings() {
|
|
111
178
|
if (isDisposed) return;
|
|
112
179
|
const media = getMedia();
|
|
@@ -115,14 +182,22 @@ function applyAudioSettings() {
|
|
|
115
182
|
media.muted = muted.value;
|
|
116
183
|
}
|
|
117
184
|
|
|
118
|
-
function markPlaying() {
|
|
185
|
+
function markPlaying(event) {
|
|
119
186
|
if (isDisposed || shouldIgnoreMediaEvent()) return;
|
|
120
187
|
isPlaying.value = true;
|
|
188
|
+
emitMediaPayload(event, (payload) => emit("play", payload));
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function markPaused(event) {
|
|
192
|
+
if (isDisposed || shouldIgnoreMediaEvent()) return;
|
|
193
|
+
isPlaying.value = false;
|
|
194
|
+
emitMediaPayload(event, (payload) => emit("pause", payload));
|
|
121
195
|
}
|
|
122
196
|
|
|
123
|
-
function
|
|
197
|
+
function markEnded(event) {
|
|
124
198
|
if (isDisposed || shouldIgnoreMediaEvent()) return;
|
|
125
199
|
isPlaying.value = false;
|
|
200
|
+
emitMediaPayload(event, (payload) => emit("ended", payload));
|
|
126
201
|
}
|
|
127
202
|
|
|
128
203
|
async function togglePlayback() {
|
|
@@ -17,12 +17,15 @@
|
|
|
17
17
|
:poster="poster"
|
|
18
18
|
:preload="preload"
|
|
19
19
|
playsinline
|
|
20
|
-
@loadedmetadata="
|
|
21
|
-
@timeupdate="
|
|
22
|
-
@durationchange="
|
|
20
|
+
@loadedmetadata="handleLoadedMetadata"
|
|
21
|
+
@timeupdate="handleTimeUpdate"
|
|
22
|
+
@durationchange="handleDurationChange"
|
|
23
23
|
@play="markPlaying"
|
|
24
24
|
@pause="markPaused"
|
|
25
|
-
@ended="
|
|
25
|
+
@ended="markEnded"
|
|
26
|
+
@seeked="handleSeeked"
|
|
27
|
+
@volumechange="handleVolumeChange"
|
|
28
|
+
@ratechange="handleRateChange"
|
|
26
29
|
@click="togglePlayback"
|
|
27
30
|
></video>
|
|
28
31
|
|
|
@@ -104,6 +107,18 @@ const {
|
|
|
104
107
|
preload: String
|
|
105
108
|
});
|
|
106
109
|
|
|
110
|
+
const emit = defineEmits([
|
|
111
|
+
"loadedmetadata",
|
|
112
|
+
"timeupdate",
|
|
113
|
+
"durationchange",
|
|
114
|
+
"play",
|
|
115
|
+
"pause",
|
|
116
|
+
"ended",
|
|
117
|
+
"seeked",
|
|
118
|
+
"volumechange",
|
|
119
|
+
"ratechange"
|
|
120
|
+
]);
|
|
121
|
+
|
|
107
122
|
const mediaEl = ref(null);
|
|
108
123
|
const screenEl = ref(null);
|
|
109
124
|
const currentTime = ref(0);
|
|
@@ -168,6 +183,58 @@ function syncMedia() {
|
|
|
168
183
|
duration.value = Number.isFinite(media.duration) ? media.duration : 0;
|
|
169
184
|
}
|
|
170
185
|
|
|
186
|
+
function createMediaPayload(event) {
|
|
187
|
+
const media = getMedia();
|
|
188
|
+
if (!media) return null;
|
|
189
|
+
return {
|
|
190
|
+
currentTime: media.currentTime || 0,
|
|
191
|
+
duration: Number.isFinite(media.duration) ? media.duration : 0,
|
|
192
|
+
paused: media.paused,
|
|
193
|
+
ended: media.ended,
|
|
194
|
+
muted: media.muted,
|
|
195
|
+
volume: media.volume,
|
|
196
|
+
playbackRate: media.playbackRate,
|
|
197
|
+
nativeEvent: event
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function emitMediaPayload(event, dispatch) {
|
|
202
|
+
const payload = createMediaPayload(event);
|
|
203
|
+
if (payload) {
|
|
204
|
+
dispatch(payload);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function handleLoadedMetadata(event) {
|
|
209
|
+
syncMedia();
|
|
210
|
+
emitMediaPayload(event, (payload) => emit("loadedmetadata", payload));
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function handleTimeUpdate(event) {
|
|
214
|
+
syncMedia();
|
|
215
|
+
emitMediaPayload(event, (payload) => emit("timeupdate", payload));
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function handleDurationChange(event) {
|
|
219
|
+
syncMedia();
|
|
220
|
+
emitMediaPayload(event, (payload) => emit("durationchange", payload));
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function handleSeeked(event) {
|
|
224
|
+
syncMedia();
|
|
225
|
+
emitMediaPayload(event, (payload) => emit("seeked", payload));
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function handleVolumeChange(event) {
|
|
229
|
+
syncMedia();
|
|
230
|
+
emitMediaPayload(event, (payload) => emit("volumechange", payload));
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function handleRateChange(event) {
|
|
234
|
+
syncMedia();
|
|
235
|
+
emitMediaPayload(event, (payload) => emit("ratechange", payload));
|
|
236
|
+
}
|
|
237
|
+
|
|
171
238
|
function applyAudioSettings() {
|
|
172
239
|
if (isDisposed) return;
|
|
173
240
|
const media = getMedia();
|
|
@@ -177,16 +244,25 @@ function applyAudioSettings() {
|
|
|
177
244
|
media.playbackRate = playbackRate;
|
|
178
245
|
}
|
|
179
246
|
|
|
180
|
-
function markPlaying() {
|
|
247
|
+
function markPlaying(event) {
|
|
181
248
|
if (isDisposed || shouldIgnoreMediaEvent()) return;
|
|
182
249
|
isPlaying.value = true;
|
|
183
250
|
controlsVisible.value = pointerInside.value;
|
|
251
|
+
emitMediaPayload(event, (payload) => emit("play", payload));
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function markPaused(event) {
|
|
255
|
+
if (isDisposed || shouldIgnoreMediaEvent()) return;
|
|
256
|
+
isPlaying.value = false;
|
|
257
|
+
controlsVisible.value = true;
|
|
258
|
+
emitMediaPayload(event, (payload) => emit("pause", payload));
|
|
184
259
|
}
|
|
185
260
|
|
|
186
|
-
function
|
|
261
|
+
function markEnded(event) {
|
|
187
262
|
if (isDisposed || shouldIgnoreMediaEvent()) return;
|
|
188
263
|
isPlaying.value = false;
|
|
189
264
|
controlsVisible.value = true;
|
|
265
|
+
emitMediaPayload(event, (payload) => emit("ended", payload));
|
|
190
266
|
}
|
|
191
267
|
|
|
192
268
|
function updateFullscreen() {
|
package/package.json
CHANGED
|
@@ -1,11 +1,34 @@
|
|
|
1
1
|
import type { MikuruComponent } from "../env";
|
|
2
2
|
|
|
3
|
+
export type MikuruAudioPlayerEventPayload = {
|
|
4
|
+
currentTime: number;
|
|
5
|
+
duration: number;
|
|
6
|
+
paused: boolean;
|
|
7
|
+
ended: boolean;
|
|
8
|
+
muted: boolean;
|
|
9
|
+
volume: number;
|
|
10
|
+
playbackRate: number;
|
|
11
|
+
nativeEvent?: Event;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export type MikuruAudioPlayerEvents = {
|
|
15
|
+
onLoadedmetadata?: (payload: MikuruAudioPlayerEventPayload) => void;
|
|
16
|
+
onTimeupdate?: (payload: MikuruAudioPlayerEventPayload) => void;
|
|
17
|
+
onDurationchange?: (payload: MikuruAudioPlayerEventPayload) => void;
|
|
18
|
+
onPlay?: (payload: MikuruAudioPlayerEventPayload) => void;
|
|
19
|
+
onPause?: (payload: MikuruAudioPlayerEventPayload) => void;
|
|
20
|
+
onEnded?: (payload: MikuruAudioPlayerEventPayload) => void;
|
|
21
|
+
onSeeked?: (payload: MikuruAudioPlayerEventPayload) => void;
|
|
22
|
+
onVolumechange?: (payload: MikuruAudioPlayerEventPayload) => void;
|
|
23
|
+
onRatechange?: (payload: MikuruAudioPlayerEventPayload) => void;
|
|
24
|
+
};
|
|
25
|
+
|
|
3
26
|
export type MikuruAudioPlayerProps = {
|
|
4
27
|
src: string;
|
|
5
28
|
title?: string;
|
|
6
29
|
artist?: string;
|
|
7
30
|
preload?: string;
|
|
8
|
-
};
|
|
31
|
+
} & MikuruAudioPlayerEvents;
|
|
9
32
|
|
|
10
33
|
declare const component: MikuruComponent<MikuruAudioPlayerProps>;
|
|
11
34
|
export default component;
|
|
@@ -1,12 +1,35 @@
|
|
|
1
1
|
import type { MikuruComponent } from "../env";
|
|
2
2
|
|
|
3
|
+
export type MikuruVideoPlayerEventPayload = {
|
|
4
|
+
currentTime: number;
|
|
5
|
+
duration: number;
|
|
6
|
+
paused: boolean;
|
|
7
|
+
ended: boolean;
|
|
8
|
+
muted: boolean;
|
|
9
|
+
volume: number;
|
|
10
|
+
playbackRate: number;
|
|
11
|
+
nativeEvent?: Event;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export type MikuruVideoPlayerEvents = {
|
|
15
|
+
onLoadedmetadata?: (payload: MikuruVideoPlayerEventPayload) => void;
|
|
16
|
+
onTimeupdate?: (payload: MikuruVideoPlayerEventPayload) => void;
|
|
17
|
+
onDurationchange?: (payload: MikuruVideoPlayerEventPayload) => void;
|
|
18
|
+
onPlay?: (payload: MikuruVideoPlayerEventPayload) => void;
|
|
19
|
+
onPause?: (payload: MikuruVideoPlayerEventPayload) => void;
|
|
20
|
+
onEnded?: (payload: MikuruVideoPlayerEventPayload) => void;
|
|
21
|
+
onSeeked?: (payload: MikuruVideoPlayerEventPayload) => void;
|
|
22
|
+
onVolumechange?: (payload: MikuruVideoPlayerEventPayload) => void;
|
|
23
|
+
onRatechange?: (payload: MikuruVideoPlayerEventPayload) => void;
|
|
24
|
+
};
|
|
25
|
+
|
|
3
26
|
export type MikuruVideoPlayerProps = {
|
|
4
27
|
src: string;
|
|
5
28
|
poster?: string;
|
|
6
29
|
title?: string;
|
|
7
30
|
subtitle?: string;
|
|
8
31
|
preload?: string;
|
|
9
|
-
};
|
|
32
|
+
} & MikuruVideoPlayerEvents;
|
|
10
33
|
|
|
11
34
|
declare const component: MikuruComponent<MikuruVideoPlayerProps>;
|
|
12
35
|
export default component;
|