avbridge 2.5.0 → 2.7.0
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 +85 -0
- package/dist/{chunk-TBW26OPP.cjs → chunk-6SOFJV44.cjs} +59 -2
- package/dist/chunk-6SOFJV44.cjs.map +1 -0
- package/dist/{chunk-KY2GPCT7.js → chunk-OGYHFY6K.js} +59 -2
- package/dist/chunk-OGYHFY6K.js.map +1 -0
- package/dist/element-browser.js +87 -2
- package/dist/element-browser.js.map +1 -1
- package/dist/element.cjs +32 -4
- package/dist/element.cjs.map +1 -1
- package/dist/element.d.cts +13 -1
- package/dist/element.d.ts +13 -1
- package/dist/element.js +31 -3
- package/dist/element.js.map +1 -1
- package/dist/index.cjs +8 -8
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/{player-B6WB74RD.d.cts → player-DGXeCNfD.d.cts} +41 -1
- package/dist/{player-B6WB74RD.d.ts → player-DGXeCNfD.d.ts} +41 -1
- package/dist/player.cjs +130 -2
- package/dist/player.cjs.map +1 -1
- package/dist/player.d.cts +191 -132
- package/dist/player.d.ts +191 -132
- package/dist/player.js +130 -2
- package/dist/player.js.map +1 -1
- package/package.json +7 -1
- package/src/element/avbridge-player.ts +84 -0
- package/src/element/avbridge-video.ts +86 -3
- package/src/element/player-styles.ts +12 -0
- package/src/strategies/fallback/index.ts +27 -0
- package/src/strategies/hybrid/index.ts +18 -0
- package/src/types.ts +32 -0
- package/src/util/time-ranges.ts +40 -0
- package/dist/chunk-KY2GPCT7.js.map +0 -1
- package/dist/chunk-TBW26OPP.cjs.map +0 -1
package/dist/player.cjs
CHANGED
|
@@ -2390,6 +2390,35 @@ async function loadBridge() {
|
|
|
2390
2390
|
}
|
|
2391
2391
|
}
|
|
2392
2392
|
|
|
2393
|
+
// src/util/time-ranges.ts
|
|
2394
|
+
function makeTimeRanges(ranges) {
|
|
2395
|
+
const frozen = ranges.slice();
|
|
2396
|
+
const impl = {
|
|
2397
|
+
get length() {
|
|
2398
|
+
return frozen.length;
|
|
2399
|
+
},
|
|
2400
|
+
start(index) {
|
|
2401
|
+
if (index < 0 || index >= frozen.length) {
|
|
2402
|
+
throw new DOMException(
|
|
2403
|
+
`TimeRanges.start: index ${index} out of range (length=${frozen.length})`,
|
|
2404
|
+
"IndexSizeError"
|
|
2405
|
+
);
|
|
2406
|
+
}
|
|
2407
|
+
return frozen[index][0];
|
|
2408
|
+
},
|
|
2409
|
+
end(index) {
|
|
2410
|
+
if (index < 0 || index >= frozen.length) {
|
|
2411
|
+
throw new DOMException(
|
|
2412
|
+
`TimeRanges.end: index ${index} out of range (length=${frozen.length})`,
|
|
2413
|
+
"IndexSizeError"
|
|
2414
|
+
);
|
|
2415
|
+
}
|
|
2416
|
+
return frozen[index][1];
|
|
2417
|
+
}
|
|
2418
|
+
};
|
|
2419
|
+
return impl;
|
|
2420
|
+
}
|
|
2421
|
+
|
|
2393
2422
|
// src/strategies/hybrid/index.ts
|
|
2394
2423
|
var READY_AUDIO_BUFFER_SECONDS = 0.3;
|
|
2395
2424
|
var READY_TIMEOUT_SECONDS = 10;
|
|
@@ -2447,6 +2476,18 @@ async function createHybridSession(ctx, target, transport) {
|
|
|
2447
2476
|
get: () => ctx.duration ?? NaN
|
|
2448
2477
|
});
|
|
2449
2478
|
}
|
|
2479
|
+
Object.defineProperty(target, "readyState", {
|
|
2480
|
+
configurable: true,
|
|
2481
|
+
get: () => {
|
|
2482
|
+
if (!renderer.hasFrames()) return 0;
|
|
2483
|
+
if (!audio.isPlaying() && audio.bufferAhead() <= 0 && !audio.isNoAudio()) return 1;
|
|
2484
|
+
return 2;
|
|
2485
|
+
}
|
|
2486
|
+
});
|
|
2487
|
+
Object.defineProperty(target, "seekable", {
|
|
2488
|
+
configurable: true,
|
|
2489
|
+
get: () => makeTimeRanges(ctx.duration && Number.isFinite(ctx.duration) && ctx.duration > 0 ? [[0, ctx.duration]] : [])
|
|
2490
|
+
});
|
|
2450
2491
|
async function waitForBuffer() {
|
|
2451
2492
|
const start = performance.now();
|
|
2452
2493
|
while (true) {
|
|
@@ -2528,6 +2569,8 @@ async function createHybridSession(ctx, target, transport) {
|
|
|
2528
2569
|
delete target.paused;
|
|
2529
2570
|
delete target.volume;
|
|
2530
2571
|
delete target.muted;
|
|
2572
|
+
delete target.readyState;
|
|
2573
|
+
delete target.seekable;
|
|
2531
2574
|
} catch {
|
|
2532
2575
|
}
|
|
2533
2576
|
},
|
|
@@ -3048,6 +3091,18 @@ async function createFallbackSession(ctx, target, transport) {
|
|
|
3048
3091
|
get: () => ctx.duration ?? NaN
|
|
3049
3092
|
});
|
|
3050
3093
|
}
|
|
3094
|
+
Object.defineProperty(target, "readyState", {
|
|
3095
|
+
configurable: true,
|
|
3096
|
+
get: () => {
|
|
3097
|
+
if (!renderer.hasFrames()) return 0;
|
|
3098
|
+
if (!audio.isPlaying() && audio.bufferAhead() <= 0 && !audio.isNoAudio()) return 1;
|
|
3099
|
+
return 2;
|
|
3100
|
+
}
|
|
3101
|
+
});
|
|
3102
|
+
Object.defineProperty(target, "seekable", {
|
|
3103
|
+
configurable: true,
|
|
3104
|
+
get: () => makeTimeRanges(ctx.duration && Number.isFinite(ctx.duration) && ctx.duration > 0 ? [[0, ctx.duration]] : [])
|
|
3105
|
+
});
|
|
3051
3106
|
async function waitForBuffer() {
|
|
3052
3107
|
const start = performance.now();
|
|
3053
3108
|
let firstFrameAtMs = 0;
|
|
@@ -3150,6 +3205,8 @@ async function createFallbackSession(ctx, target, transport) {
|
|
|
3150
3205
|
delete target.paused;
|
|
3151
3206
|
delete target.volume;
|
|
3152
3207
|
delete target.muted;
|
|
3208
|
+
delete target.readyState;
|
|
3209
|
+
delete target.seekable;
|
|
3153
3210
|
} catch {
|
|
3154
3211
|
}
|
|
3155
3212
|
},
|
|
@@ -3773,7 +3830,13 @@ var AvbridgeVideoElement = class extends HTMLElementCtor {
|
|
|
3773
3830
|
_strategy = null;
|
|
3774
3831
|
_strategyClass = null;
|
|
3775
3832
|
_audioTracks = [];
|
|
3833
|
+
/** Subtitle tracks reported by the active UnifiedPlayer (options.subtitles
|
|
3834
|
+
* + embedded container tracks + programmatic addSubtitle calls). */
|
|
3776
3835
|
_subtitleTracks = [];
|
|
3836
|
+
/** Subtitle tracks derived from light-DOM `<track>` children. Maintained
|
|
3837
|
+
* by _syncTextTracks on every mutation. Merged into the public
|
|
3838
|
+
* `subtitleTracks` getter so the player's settings menu sees them. */
|
|
3839
|
+
_htmlTrackInfo = [];
|
|
3777
3840
|
/**
|
|
3778
3841
|
* External subtitle list forwarded to `createPlayer()` on the next
|
|
3779
3842
|
* bootstrap. Setting this after bootstrap queues it for the next
|
|
@@ -3893,12 +3956,28 @@ var AvbridgeVideoElement = class extends HTMLElementCtor {
|
|
|
3893
3956
|
_syncTextTracks() {
|
|
3894
3957
|
const existing = this._videoEl.querySelectorAll("track");
|
|
3895
3958
|
for (const t of Array.from(existing)) t.remove();
|
|
3959
|
+
this._htmlTrackInfo = [];
|
|
3960
|
+
let htmlIdx = 0;
|
|
3896
3961
|
for (const child of Array.from(this.children)) {
|
|
3897
3962
|
if (child.tagName === "TRACK") {
|
|
3898
|
-
const
|
|
3963
|
+
const track = child;
|
|
3964
|
+
const clone = track.cloneNode(true);
|
|
3899
3965
|
this._videoEl.appendChild(clone);
|
|
3966
|
+
const src = track.getAttribute("src") ?? void 0;
|
|
3967
|
+
const format = src?.toLowerCase().endsWith(".srt") ? "srt" : "vtt";
|
|
3968
|
+
this._htmlTrackInfo.push({
|
|
3969
|
+
id: 1e4 + htmlIdx,
|
|
3970
|
+
format,
|
|
3971
|
+
language: track.srclang || track.getAttribute("label") || void 0,
|
|
3972
|
+
sidecarUrl: src
|
|
3973
|
+
});
|
|
3974
|
+
htmlIdx++;
|
|
3900
3975
|
}
|
|
3901
3976
|
}
|
|
3977
|
+
this._dispatch("trackschange", {
|
|
3978
|
+
audioTracks: this._audioTracks,
|
|
3979
|
+
subtitleTracks: this.subtitleTracks
|
|
3980
|
+
});
|
|
3902
3981
|
}
|
|
3903
3982
|
/** Internal src setter — separate from the property setter so the
|
|
3904
3983
|
* attributeChangedCallback can use it without re-entering reflection. */
|
|
@@ -4226,7 +4305,7 @@ var AvbridgeVideoElement = class extends HTMLElementCtor {
|
|
|
4226
4305
|
return this._audioTracks;
|
|
4227
4306
|
}
|
|
4228
4307
|
get subtitleTracks() {
|
|
4229
|
-
return this._subtitleTracks;
|
|
4308
|
+
return this._htmlTrackInfo.length === 0 ? this._subtitleTracks : [...this._subtitleTracks, ...this._htmlTrackInfo];
|
|
4230
4309
|
}
|
|
4231
4310
|
/**
|
|
4232
4311
|
* External subtitle files to attach when the source loads. Takes effect
|
|
@@ -4315,6 +4394,12 @@ var AvbridgeVideoElement = class extends HTMLElementCtor {
|
|
|
4315
4394
|
getDiagnostics() {
|
|
4316
4395
|
return this._player?.getDiagnostics() ?? null;
|
|
4317
4396
|
}
|
|
4397
|
+
addEventListener(type, listener, options) {
|
|
4398
|
+
super.addEventListener(type, listener, options);
|
|
4399
|
+
}
|
|
4400
|
+
removeEventListener(type, listener, options) {
|
|
4401
|
+
super.removeEventListener(type, listener, options);
|
|
4402
|
+
}
|
|
4318
4403
|
// ── Event helpers ──────────────────────────────────────────────────────
|
|
4319
4404
|
_dispatch(name, detail) {
|
|
4320
4405
|
this.dispatchEvent(new CustomEvent(name, { detail, bubbles: false }));
|
|
@@ -4363,6 +4448,18 @@ var PLAYER_STYLES = (
|
|
|
4363
4448
|
height: 100%;
|
|
4364
4449
|
}
|
|
4365
4450
|
|
|
4451
|
+
/* Drag-and-drop file target highlight. */
|
|
4452
|
+
.avp.avp-dragover::after {
|
|
4453
|
+
content: "";
|
|
4454
|
+
position: absolute;
|
|
4455
|
+
inset: 8px;
|
|
4456
|
+
border: 2px dashed rgba(255, 255, 255, 0.75);
|
|
4457
|
+
border-radius: 4px;
|
|
4458
|
+
background: rgba(0, 0, 0, 0.25);
|
|
4459
|
+
pointer-events: none;
|
|
4460
|
+
z-index: 10;
|
|
4461
|
+
}
|
|
4462
|
+
|
|
4366
4463
|
/* \u2500\u2500 Center overlay \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
4367
4464
|
|
|
4368
4465
|
.avp-overlay {
|
|
@@ -5054,6 +5151,31 @@ var AvbridgePlayerElement = class extends HTMLElement {
|
|
|
5054
5151
|
on(container, "pointerdown", (e) => this._onPointerDown(e));
|
|
5055
5152
|
on(container, "pointerup", (e) => this._onPointerUp(e));
|
|
5056
5153
|
on(container, "pointercancel", () => this._cancelHold());
|
|
5154
|
+
on(container, "dragenter", (e) => {
|
|
5155
|
+
e.preventDefault();
|
|
5156
|
+
const dt = e.dataTransfer;
|
|
5157
|
+
if (!dt || !Array.from(dt.types).includes("Files")) return;
|
|
5158
|
+
container.classList.add("avp-dragover");
|
|
5159
|
+
});
|
|
5160
|
+
on(container, "dragover", (e) => {
|
|
5161
|
+
e.preventDefault();
|
|
5162
|
+
const dt = e.dataTransfer;
|
|
5163
|
+
if (dt) dt.dropEffect = "copy";
|
|
5164
|
+
});
|
|
5165
|
+
on(container, "dragleave", (e) => {
|
|
5166
|
+
if (e.target === container) {
|
|
5167
|
+
container.classList.remove("avp-dragover");
|
|
5168
|
+
}
|
|
5169
|
+
});
|
|
5170
|
+
on(container, "drop", (e) => {
|
|
5171
|
+
e.preventDefault();
|
|
5172
|
+
container.classList.remove("avp-dragover");
|
|
5173
|
+
const file = e.dataTransfer?.files?.[0];
|
|
5174
|
+
if (!file) return;
|
|
5175
|
+
this._video.source = file;
|
|
5176
|
+
void this._video.play().catch(() => {
|
|
5177
|
+
});
|
|
5178
|
+
});
|
|
5057
5179
|
on(this, "keydown", (e) => this._onKeydown(e));
|
|
5058
5180
|
if (!this.hasAttribute("tabindex")) {
|
|
5059
5181
|
this.setAttribute("tabindex", "0");
|
|
@@ -5619,6 +5741,12 @@ var AvbridgePlayerElement = class extends HTMLElement {
|
|
|
5619
5741
|
canPlayType(mime) {
|
|
5620
5742
|
return this._video.canPlayType(mime);
|
|
5621
5743
|
}
|
|
5744
|
+
addEventListener(type, listener, options) {
|
|
5745
|
+
super.addEventListener(type, listener, options);
|
|
5746
|
+
}
|
|
5747
|
+
removeEventListener(type, listener, options) {
|
|
5748
|
+
super.removeEventListener(type, listener, options);
|
|
5749
|
+
}
|
|
5622
5750
|
};
|
|
5623
5751
|
|
|
5624
5752
|
// src/player-element.ts
|