@scarlett-player/embed 0.5.1 → 0.5.3
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/dist/embed.audio.js +140 -27
- package/dist/embed.audio.js.map +1 -1
- package/dist/embed.audio.umd.cjs +1 -1
- package/dist/embed.audio.umd.cjs.map +1 -1
- package/dist/embed.js +201 -32
- package/dist/embed.js.map +1 -1
- package/dist/embed.umd.cjs +1 -1
- package/dist/embed.umd.cjs.map +1 -1
- package/dist/embed.video.js +150 -12
- package/dist/embed.video.js.map +1 -1
- package/dist/embed.video.umd.cjs +1 -1
- package/dist/embed.video.umd.cjs.map +1 -1
- package/package.json +9 -9
package/dist/embed.audio.js
CHANGED
|
@@ -123,12 +123,44 @@ function setupHlsEventHandlers(hls, api, callbacks) {
|
|
|
123
123
|
addHandler("hlsLevelLoaded", (_event, data) => {
|
|
124
124
|
if (data.details?.live !== void 0) {
|
|
125
125
|
api.setState("live", data.details.live);
|
|
126
|
+
if (data.details.live) {
|
|
127
|
+
const video = hls.media;
|
|
128
|
+
if (video && video.seekable && video.seekable.length > 0) {
|
|
129
|
+
const start = video.seekable.start(0);
|
|
130
|
+
const end = video.seekable.end(video.seekable.length - 1);
|
|
131
|
+
api.setState("seekableRange", { start, end });
|
|
132
|
+
const threshold = (data.details.targetduration ?? 3) * 3;
|
|
133
|
+
const isAtLiveEdge = end - video.currentTime < threshold;
|
|
134
|
+
api.setState("liveEdge", isAtLiveEdge);
|
|
135
|
+
const latency = end - video.currentTime;
|
|
136
|
+
api.setState("liveLatency", Math.max(0, latency));
|
|
137
|
+
}
|
|
138
|
+
}
|
|
126
139
|
callbacks.onLiveUpdate?.();
|
|
127
140
|
}
|
|
128
141
|
});
|
|
129
142
|
addHandler("hlsError", (_event, data) => {
|
|
130
143
|
const error = parseHlsError(data);
|
|
131
|
-
|
|
144
|
+
const isBufferHoleSeek = !error.fatal && (error.details?.includes("bufferStalledError") || data.reason?.includes("buffer holes"));
|
|
145
|
+
if (isBufferHoleSeek) {
|
|
146
|
+
api.logger.debug(`HLS buffer recovery: ${error.reason || error.details}`, {
|
|
147
|
+
details: error.details,
|
|
148
|
+
reason: error.reason
|
|
149
|
+
});
|
|
150
|
+
} else if (error.fatal) {
|
|
151
|
+
api.logger.error(`HLS fatal error: ${error.details} (type=${error.type})`, {
|
|
152
|
+
type: error.type,
|
|
153
|
+
details: error.details,
|
|
154
|
+
url: error.url
|
|
155
|
+
});
|
|
156
|
+
} else {
|
|
157
|
+
api.logger.warn(`HLS error: ${error.details} (type=${error.type}, fatal=${error.fatal})`, {
|
|
158
|
+
type: error.type,
|
|
159
|
+
details: error.details,
|
|
160
|
+
fatal: error.fatal,
|
|
161
|
+
url: error.url
|
|
162
|
+
});
|
|
163
|
+
}
|
|
132
164
|
callbacks.onError?.(error);
|
|
133
165
|
});
|
|
134
166
|
return () => {
|
|
@@ -150,6 +182,8 @@ function setupVideoEventHandlers(video, api) {
|
|
|
150
182
|
addHandler("playing", () => {
|
|
151
183
|
api.setState("playing", true);
|
|
152
184
|
api.setState("paused", false);
|
|
185
|
+
api.setState("waiting", false);
|
|
186
|
+
api.setState("buffering", false);
|
|
153
187
|
api.setState("playbackState", "playing");
|
|
154
188
|
});
|
|
155
189
|
addHandler("pause", () => {
|
|
@@ -166,6 +200,15 @@ function setupVideoEventHandlers(video, api) {
|
|
|
166
200
|
addHandler("timeupdate", () => {
|
|
167
201
|
api.setState("currentTime", video.currentTime);
|
|
168
202
|
api.emit("playback:timeupdate", { currentTime: video.currentTime });
|
|
203
|
+
const isLive = api.getState("live");
|
|
204
|
+
if (isLive && video.seekable && video.seekable.length > 0) {
|
|
205
|
+
const start = video.seekable.start(0);
|
|
206
|
+
const end = video.seekable.end(video.seekable.length - 1);
|
|
207
|
+
api.setState("seekableRange", { start, end });
|
|
208
|
+
const isAtLiveEdge = end - video.currentTime < 10;
|
|
209
|
+
api.setState("liveEdge", isAtLiveEdge);
|
|
210
|
+
api.setState("liveLatency", Math.max(0, end - video.currentTime));
|
|
211
|
+
}
|
|
169
212
|
});
|
|
170
213
|
addHandler("durationchange", () => {
|
|
171
214
|
api.setState("duration", video.duration || 0);
|
|
@@ -1006,6 +1049,8 @@ function createStyles(prefix, theme) {
|
|
|
1006
1049
|
align-items: center;
|
|
1007
1050
|
justify-content: center;
|
|
1008
1051
|
transition: background 0.2s, transform 0.1s;
|
|
1052
|
+
min-width: 44px;
|
|
1053
|
+
min-height: 44px;
|
|
1009
1054
|
}
|
|
1010
1055
|
|
|
1011
1056
|
.${prefix}__btn:hover {
|
|
@@ -1155,6 +1200,8 @@ function createAudioUIPlugin(config) {
|
|
|
1155
1200
|
document.head.appendChild(styleElement);
|
|
1156
1201
|
container = document.createElement("div");
|
|
1157
1202
|
container.className = `${prefix} ${prefix}--${layout}`;
|
|
1203
|
+
container.setAttribute("role", "region");
|
|
1204
|
+
container.setAttribute("aria-label", "Audio player");
|
|
1158
1205
|
if (layout === "full") {
|
|
1159
1206
|
container.innerHTML = buildFullLayout();
|
|
1160
1207
|
} else if (layout === "compact") {
|
|
@@ -1189,24 +1236,24 @@ function createAudioUIPlugin(config) {
|
|
|
1189
1236
|
${mergedConfig.showArtist ? `<div class="${prefix}__artist">-</div>` : ""}
|
|
1190
1237
|
</div>
|
|
1191
1238
|
<div class="${prefix}__progress">
|
|
1192
|
-
${mergedConfig.showTime ? `<span class="${prefix}__time ${prefix}__time--current">0:00</span>` : ""}
|
|
1193
|
-
<div class="${prefix}__progress-bar">
|
|
1239
|
+
${mergedConfig.showTime ? `<span class="${prefix}__time ${prefix}__time--current" aria-label="Current time">0:00</span>` : ""}
|
|
1240
|
+
<div class="${prefix}__progress-bar" role="slider" aria-label="Seek" aria-valuemin="0" aria-valuemax="0" aria-valuenow="0" aria-valuetext="0:00" tabindex="0">
|
|
1194
1241
|
<div class="${prefix}__progress-fill" style="transform: scaleX(0)"></div>
|
|
1195
1242
|
</div>
|
|
1196
|
-
${mergedConfig.showTime ? `<span class="${prefix}__time ${prefix}__time--duration">0:00</span>` : ""}
|
|
1243
|
+
${mergedConfig.showTime ? `<span class="${prefix}__time ${prefix}__time--duration" aria-label="Duration">0:00</span>` : ""}
|
|
1197
1244
|
</div>
|
|
1198
|
-
<div class="${prefix}__controls">
|
|
1199
|
-
${mergedConfig.showShuffle ? `<button class="${prefix}__btn ${prefix}__btn--shuffle" title="Shuffle">${ICONS.shuffle}</button>` : ""}
|
|
1200
|
-
${mergedConfig.showNavigation ? `<button class="${prefix}__btn ${prefix}__btn--prev" title="Previous">${ICONS.previous}</button>` : ""}
|
|
1201
|
-
<button class="${prefix}__btn ${prefix}__btn--primary ${prefix}__btn--play" title="Play">${ICONS.play}</button>
|
|
1202
|
-
${mergedConfig.showNavigation ? `<button class="${prefix}__btn ${prefix}__btn--next" title="Next">${ICONS.next}</button>` : ""}
|
|
1203
|
-
${mergedConfig.showRepeat ? `<button class="${prefix}__btn ${prefix}__btn--repeat" title="Repeat">${ICONS.repeatOff}</button>` : ""}
|
|
1245
|
+
<div class="${prefix}__controls" role="group" aria-label="Playback controls">
|
|
1246
|
+
${mergedConfig.showShuffle ? `<button class="${prefix}__btn ${prefix}__btn--shuffle" title="Shuffle" aria-label="Shuffle" aria-pressed="false">${ICONS.shuffle}</button>` : ""}
|
|
1247
|
+
${mergedConfig.showNavigation ? `<button class="${prefix}__btn ${prefix}__btn--prev" title="Previous" aria-label="Previous track">${ICONS.previous}</button>` : ""}
|
|
1248
|
+
<button class="${prefix}__btn ${prefix}__btn--primary ${prefix}__btn--play" title="Play" aria-label="Play">${ICONS.play}</button>
|
|
1249
|
+
${mergedConfig.showNavigation ? `<button class="${prefix}__btn ${prefix}__btn--next" title="Next" aria-label="Next track">${ICONS.next}</button>` : ""}
|
|
1250
|
+
${mergedConfig.showRepeat ? `<button class="${prefix}__btn ${prefix}__btn--repeat" title="Repeat" aria-label="Repeat" aria-pressed="false">${ICONS.repeatOff}</button>` : ""}
|
|
1204
1251
|
</div>
|
|
1205
1252
|
${mergedConfig.showVolume ? `
|
|
1206
1253
|
<div class="${prefix}__secondary-controls">
|
|
1207
|
-
<div class="${prefix}__volume">
|
|
1208
|
-
<button class="${prefix}__btn ${prefix}__btn--volume" title="Volume">${ICONS.volumeHigh}</button>
|
|
1209
|
-
<div class="${prefix}__volume-slider">
|
|
1254
|
+
<div class="${prefix}__volume" role="group" aria-label="Volume controls">
|
|
1255
|
+
<button class="${prefix}__btn ${prefix}__btn--volume" title="Volume" aria-label="Mute">${ICONS.volumeHigh}</button>
|
|
1256
|
+
<div class="${prefix}__volume-slider" role="slider" aria-label="Volume" aria-valuemin="0" aria-valuemax="100" aria-valuenow="100" aria-valuetext="100%" tabindex="0">
|
|
1210
1257
|
<div class="${prefix}__volume-fill" style="width: 100%"></div>
|
|
1211
1258
|
</div>
|
|
1212
1259
|
</div>
|
|
@@ -1225,21 +1272,21 @@ function createAudioUIPlugin(config) {
|
|
|
1225
1272
|
${mergedConfig.showTitle ? `<div class="${prefix}__title">-</div>` : ""}
|
|
1226
1273
|
${mergedConfig.showArtist ? `<div class="${prefix}__artist">-</div>` : ""}
|
|
1227
1274
|
<div class="${prefix}__progress">
|
|
1228
|
-
<div class="${prefix}__progress-bar">
|
|
1275
|
+
<div class="${prefix}__progress-bar" role="slider" aria-label="Seek" aria-valuemin="0" aria-valuemax="0" aria-valuenow="0" aria-valuetext="0:00" tabindex="0">
|
|
1229
1276
|
<div class="${prefix}__progress-fill" style="transform: scaleX(0)"></div>
|
|
1230
1277
|
</div>
|
|
1231
1278
|
</div>
|
|
1232
1279
|
</div>
|
|
1233
|
-
<div class="${prefix}__controls">
|
|
1234
|
-
${mergedConfig.showNavigation ? `<button class="${prefix}__btn ${prefix}__btn--prev" title="Previous">${ICONS.previous}</button>` : ""}
|
|
1235
|
-
<button class="${prefix}__btn ${prefix}__btn--primary ${prefix}__btn--play" title="Play">${ICONS.play}</button>
|
|
1236
|
-
${mergedConfig.showNavigation ? `<button class="${prefix}__btn ${prefix}__btn--next" title="Next">${ICONS.next}</button>` : ""}
|
|
1280
|
+
<div class="${prefix}__controls" role="group" aria-label="Playback controls">
|
|
1281
|
+
${mergedConfig.showNavigation ? `<button class="${prefix}__btn ${prefix}__btn--prev" title="Previous" aria-label="Previous track">${ICONS.previous}</button>` : ""}
|
|
1282
|
+
<button class="${prefix}__btn ${prefix}__btn--primary ${prefix}__btn--play" title="Play" aria-label="Play">${ICONS.play}</button>
|
|
1283
|
+
${mergedConfig.showNavigation ? `<button class="${prefix}__btn ${prefix}__btn--next" title="Next" aria-label="Next track">${ICONS.next}</button>` : ""}
|
|
1237
1284
|
</div>
|
|
1238
1285
|
`;
|
|
1239
1286
|
};
|
|
1240
1287
|
const buildMiniLayout = () => {
|
|
1241
1288
|
return `
|
|
1242
|
-
<button class="${prefix}__btn ${prefix}__btn--primary ${prefix}__btn--play" title="Play">${ICONS.play}</button>
|
|
1289
|
+
<button class="${prefix}__btn ${prefix}__btn--primary ${prefix}__btn--play" title="Play" aria-label="Play">${ICONS.play}</button>
|
|
1243
1290
|
${mergedConfig.showArtwork ? `
|
|
1244
1291
|
<div class="${prefix}__artwork">
|
|
1245
1292
|
<img src="${mergedConfig.defaultArtwork || ""}" alt="Album art" />
|
|
@@ -1248,7 +1295,7 @@ function createAudioUIPlugin(config) {
|
|
|
1248
1295
|
<div class="${prefix}__info">
|
|
1249
1296
|
${mergedConfig.showTitle ? `<div class="${prefix}__title-wrapper"><div class="${prefix}__title">-</div></div>` : ""}
|
|
1250
1297
|
<div class="${prefix}__progress">
|
|
1251
|
-
<div class="${prefix}__progress-bar">
|
|
1298
|
+
<div class="${prefix}__progress-bar" role="slider" aria-label="Seek" aria-valuemin="0" aria-valuemax="0" aria-valuenow="0" aria-valuetext="0:00" tabindex="0">
|
|
1252
1299
|
<div class="${prefix}__progress-fill" style="transform: scaleX(0)"></div>
|
|
1253
1300
|
</div>
|
|
1254
1301
|
</div>
|
|
@@ -1314,6 +1361,7 @@ function createAudioUIPlugin(config) {
|
|
|
1314
1361
|
if (playPauseBtn) {
|
|
1315
1362
|
playPauseBtn.innerHTML = playing ? ICONS.pause : ICONS.play;
|
|
1316
1363
|
playPauseBtn.title = playing ? "Pause" : "Play";
|
|
1364
|
+
playPauseBtn.setAttribute("aria-label", playing ? "Pause" : "Play");
|
|
1317
1365
|
}
|
|
1318
1366
|
const currentTime = api.getState("currentTime") || 0;
|
|
1319
1367
|
const duration = api.getState("duration") || 0;
|
|
@@ -1336,6 +1384,12 @@ function createAudioUIPlugin(config) {
|
|
|
1336
1384
|
if (durationEl) {
|
|
1337
1385
|
durationEl.textContent = formatTime(duration);
|
|
1338
1386
|
}
|
|
1387
|
+
const progressBar = container?.querySelector(`.${prefix}__progress-bar`);
|
|
1388
|
+
if (progressBar) {
|
|
1389
|
+
progressBar.setAttribute("aria-valuemax", String(Math.floor(duration)));
|
|
1390
|
+
progressBar.setAttribute("aria-valuenow", String(Math.floor(currentTime)));
|
|
1391
|
+
progressBar.setAttribute("aria-valuetext", formatTime(currentTime));
|
|
1392
|
+
}
|
|
1339
1393
|
const title = api.getState("title");
|
|
1340
1394
|
const poster = api.getState("poster");
|
|
1341
1395
|
if (titleEl && title) {
|
|
@@ -1360,21 +1414,34 @@ function createAudioUIPlugin(config) {
|
|
|
1360
1414
|
}
|
|
1361
1415
|
if (volumeBtn) {
|
|
1362
1416
|
volumeBtn.innerHTML = muted || volume === 0 ? ICONS.volumeMuted : ICONS.volumeHigh;
|
|
1417
|
+
volumeBtn.setAttribute("aria-label", muted || volume === 0 ? "Unmute" : "Mute");
|
|
1418
|
+
}
|
|
1419
|
+
const volumeSlider = container?.querySelector(`.${prefix}__volume-slider`);
|
|
1420
|
+
if (volumeSlider) {
|
|
1421
|
+
const displayVolume = Math.round((muted ? 0 : volume) * 100);
|
|
1422
|
+
volumeSlider.setAttribute("aria-valuenow", String(displayVolume));
|
|
1423
|
+
volumeSlider.setAttribute("aria-valuetext", `${displayVolume}%`);
|
|
1363
1424
|
}
|
|
1364
1425
|
const playlist = api.getPlugin("playlist");
|
|
1365
1426
|
if (playlist) {
|
|
1366
1427
|
const state = playlist.getState();
|
|
1367
1428
|
if (shuffleBtn) {
|
|
1368
1429
|
shuffleBtn.classList.toggle(`${prefix}__btn--active`, state.shuffle);
|
|
1430
|
+
shuffleBtn.setAttribute("aria-pressed", String(state.shuffle));
|
|
1431
|
+
shuffleBtn.setAttribute("aria-label", state.shuffle ? "Shuffle on" : "Shuffle off");
|
|
1369
1432
|
}
|
|
1370
1433
|
if (repeatBtn) {
|
|
1371
1434
|
repeatBtn.classList.toggle(`${prefix}__btn--active`, state.repeat !== "none");
|
|
1435
|
+
repeatBtn.setAttribute("aria-pressed", String(state.repeat !== "none"));
|
|
1372
1436
|
if (state.repeat === "one") {
|
|
1373
1437
|
repeatBtn.innerHTML = ICONS.repeatOne;
|
|
1438
|
+
repeatBtn.setAttribute("aria-label", "Repeat one");
|
|
1374
1439
|
} else if (state.repeat === "all") {
|
|
1375
1440
|
repeatBtn.innerHTML = ICONS.repeatAll;
|
|
1441
|
+
repeatBtn.setAttribute("aria-label", "Repeat all");
|
|
1376
1442
|
} else {
|
|
1377
1443
|
repeatBtn.innerHTML = ICONS.repeatOff;
|
|
1444
|
+
repeatBtn.setAttribute("aria-label", "Repeat off");
|
|
1378
1445
|
}
|
|
1379
1446
|
}
|
|
1380
1447
|
}
|
|
@@ -3595,6 +3662,7 @@ class ScarlettPlayer {
|
|
|
3595
3662
|
this.destroyed = false;
|
|
3596
3663
|
this.seekingWhilePlaying = false;
|
|
3597
3664
|
this.seekResumeTimeout = null;
|
|
3665
|
+
this.loadGeneration = 0;
|
|
3598
3666
|
if (typeof options.container === "string") {
|
|
3599
3667
|
const el = document.querySelector(options.container);
|
|
3600
3668
|
if (!el || !(el instanceof HTMLElement)) {
|
|
@@ -3668,6 +3736,7 @@ class ScarlettPlayer {
|
|
|
3668
3736
|
*/
|
|
3669
3737
|
async load(source) {
|
|
3670
3738
|
this.checkDestroyed();
|
|
3739
|
+
const generation = ++this.loadGeneration;
|
|
3671
3740
|
try {
|
|
3672
3741
|
this.logger.info("Loading source", { source });
|
|
3673
3742
|
this.stateManager.update({
|
|
@@ -3686,6 +3755,10 @@ class ScarlettPlayer {
|
|
|
3686
3755
|
await this.pluginManager.destroyPlugin(previousProviderId);
|
|
3687
3756
|
this._currentProvider = null;
|
|
3688
3757
|
}
|
|
3758
|
+
if (generation !== this.loadGeneration) {
|
|
3759
|
+
this.logger.info("Load superseded by newer load call", { source });
|
|
3760
|
+
return;
|
|
3761
|
+
}
|
|
3689
3762
|
const provider = this.pluginManager.selectProvider(source);
|
|
3690
3763
|
if (!provider) {
|
|
3691
3764
|
this.errorHandler.throw(
|
|
@@ -3701,18 +3774,28 @@ class ScarlettPlayer {
|
|
|
3701
3774
|
this._currentProvider = provider;
|
|
3702
3775
|
this.logger.info("Provider selected", { provider: provider.id });
|
|
3703
3776
|
await this.pluginManager.initPlugin(provider.id);
|
|
3777
|
+
if (generation !== this.loadGeneration) {
|
|
3778
|
+
this.logger.info("Load superseded by newer load call", { source });
|
|
3779
|
+
return;
|
|
3780
|
+
}
|
|
3704
3781
|
this.stateManager.set("source", { src: source, type: this.detectMimeType(source) });
|
|
3705
3782
|
if (typeof provider.loadSource === "function") {
|
|
3706
3783
|
await provider.loadSource(source);
|
|
3707
3784
|
}
|
|
3785
|
+
if (generation !== this.loadGeneration) {
|
|
3786
|
+
this.logger.info("Load superseded by newer load call", { source });
|
|
3787
|
+
return;
|
|
3788
|
+
}
|
|
3708
3789
|
if (this.stateManager.getValue("autoplay")) {
|
|
3709
3790
|
await this.play();
|
|
3710
3791
|
}
|
|
3711
3792
|
} catch (error) {
|
|
3712
|
-
this.
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
|
|
3793
|
+
if (generation === this.loadGeneration) {
|
|
3794
|
+
this.errorHandler.handle(error, {
|
|
3795
|
+
operation: "load",
|
|
3796
|
+
source
|
|
3797
|
+
});
|
|
3798
|
+
}
|
|
3716
3799
|
}
|
|
3717
3800
|
}
|
|
3718
3801
|
/**
|
|
@@ -3840,8 +3923,9 @@ class ScarlettPlayer {
|
|
|
3840
3923
|
*/
|
|
3841
3924
|
setPlaybackRate(rate) {
|
|
3842
3925
|
this.checkDestroyed();
|
|
3843
|
-
|
|
3844
|
-
this.
|
|
3926
|
+
const clampedRate = Math.max(0.0625, Math.min(16, rate));
|
|
3927
|
+
this.stateManager.set("playbackRate", clampedRate);
|
|
3928
|
+
this.eventBus.emit("playback:ratechange", { rate: clampedRate });
|
|
3845
3929
|
}
|
|
3846
3930
|
/**
|
|
3847
3931
|
* Set autoplay state.
|
|
@@ -3970,6 +4054,13 @@ class ScarlettPlayer {
|
|
|
3970
4054
|
}
|
|
3971
4055
|
const provider = this._currentProvider;
|
|
3972
4056
|
if (typeof provider.setLevel === "function") {
|
|
4057
|
+
if (index !== -1) {
|
|
4058
|
+
const levels = this.getQualities();
|
|
4059
|
+
if (levels.length > 0 && (index < 0 || index >= levels.length)) {
|
|
4060
|
+
this.logger.warn(`Invalid quality index: ${index} (available: ${levels.length})`);
|
|
4061
|
+
return;
|
|
4062
|
+
}
|
|
4063
|
+
}
|
|
3973
4064
|
provider.setLevel(index);
|
|
3974
4065
|
this.eventBus.emit("quality:change", {
|
|
3975
4066
|
quality: index === -1 ? "auto" : `level-${index}`,
|
|
@@ -4210,18 +4301,40 @@ class ScarlettPlayer {
|
|
|
4210
4301
|
* @private
|
|
4211
4302
|
*/
|
|
4212
4303
|
detectMimeType(source) {
|
|
4213
|
-
|
|
4304
|
+
let path = source;
|
|
4305
|
+
try {
|
|
4306
|
+
path = new URL(source).pathname;
|
|
4307
|
+
} catch {
|
|
4308
|
+
const noQuery = source.split("?")[0] ?? source;
|
|
4309
|
+
path = noQuery.split("#")[0] ?? noQuery;
|
|
4310
|
+
}
|
|
4311
|
+
const ext = path.split(".").pop()?.toLowerCase() ?? "";
|
|
4214
4312
|
switch (ext) {
|
|
4215
4313
|
case "m3u8":
|
|
4216
4314
|
return "application/x-mpegURL";
|
|
4217
4315
|
case "mpd":
|
|
4218
4316
|
return "application/dash+xml";
|
|
4219
4317
|
case "mp4":
|
|
4318
|
+
case "m4v":
|
|
4220
4319
|
return "video/mp4";
|
|
4221
4320
|
case "webm":
|
|
4222
4321
|
return "video/webm";
|
|
4223
4322
|
case "ogg":
|
|
4323
|
+
case "ogv":
|
|
4224
4324
|
return "video/ogg";
|
|
4325
|
+
case "mov":
|
|
4326
|
+
return "video/quicktime";
|
|
4327
|
+
case "mkv":
|
|
4328
|
+
return "video/x-matroska";
|
|
4329
|
+
case "mp3":
|
|
4330
|
+
return "audio/mpeg";
|
|
4331
|
+
case "wav":
|
|
4332
|
+
return "audio/wav";
|
|
4333
|
+
case "flac":
|
|
4334
|
+
return "audio/flac";
|
|
4335
|
+
case "aac":
|
|
4336
|
+
case "m4a":
|
|
4337
|
+
return "audio/mp4";
|
|
4225
4338
|
default:
|
|
4226
4339
|
return "video/mp4";
|
|
4227
4340
|
}
|