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