@norskvideo/moq-watch 0.2.1 → 0.2.2

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/element.d.ts CHANGED
@@ -17,6 +17,7 @@ import type * as Text from "./text";
17
17
  import type * as Video from "./video";
18
18
  declare const OBSERVED: readonly ["url", "name", "paused", "volume", "muted", "visible", "announced", "delay", "buffer", "reload", "latency", "latency-min", "jitter", "catalog-format", "captions"];
19
19
  type Observed = (typeof OBSERVED)[number];
20
+ export declare function parseVisible(value: string | null): Video.Visible;
20
21
  /** Parse the element's catalog-format attribute. */
21
22
  export declare function parseCatalogFormat(value: string | null): CatalogFormat | undefined;
22
23
  export default class MoqWatch extends HTMLElement {
package/element.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"element.d.ts","sourceRoot":"","sources":["../src/element.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"element.d.ts","sourceRoot":"","sources":["../src/element.ts"],"names":[],"mappings":"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"}
package/element.js CHANGED
@@ -1,4 +1,4 @@
1
- import { h as e, t } from "./player-BFRzc5zw.js";
1
+ import { h as e, t } from "./player-C7Ih0ExN.js";
2
2
  import * as n from "@norskvideo/moq-net";
3
3
  import { Time as r } from "@norskvideo/moq-net";
4
4
  import { Computed as i, Effect as a, Signal as o } from "@norskvideo/moq-signals";
@@ -33,7 +33,7 @@ var l = [
33
33
  ];
34
34
  function u(e) {
35
35
  let t = e?.trim();
36
- return t ? t === "never" || t === "always" || /^-?\d+(\.\d+)?(px|%)$/.test(t) ? t : /^-?\d+(\.\d+)?$/.test(t) ? `${t}px` : (console.warn(`moq-watch: invalid visible="${e}", expected "never", "always", or a CSS length like "200px"`), "20%") : "20%";
36
+ return t ? t === "never" || t === "always" || t === "tab" || /^-?\d+(\.\d+)?(px|%)$/.test(t) ? t : /^-?\d+(\.\d+)?$/.test(t) ? `${t}px` : (console.warn(`moq-watch: invalid visible="${e}", expected "never", "always", "tab", or a CSS length like "200px"`), "20%") : "20%";
37
37
  }
38
38
  function d(e) {
39
39
  let t = e?.trim();
@@ -298,6 +298,6 @@ var h = new FinalizationRegistry((e) => e.close()), g = class extends HTMLElemen
298
298
  };
299
299
  customElements.define("moq-watch", g);
300
300
  //#endregion
301
- export { g as default, p as parseCatalogFormat };
301
+ export { g as default, p as parseCatalogFormat, u as parseVisible };
302
302
 
303
303
  //# sourceMappingURL=element.js.map
package/element.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"element.js","names":[],"sources":["../src/duration.ts","../src/element.ts"],"sourcesContent":["import { Time } from \"@moq/net\";\n\n/**\n * Parse a duration attribute: `\"300ms\"`, `\"30s\"`, or a bare `\"0\"`.\n *\n * A bare number other than zero is rejected rather than guessed at. Most players measure these in\n * seconds while we store milliseconds, so assuming a unit would turn a value copied from another\n * player into a 1000x error. Zero is exempt because `0ms` and `0s` are the same value.\n *\n * Returns undefined when the value doesn't parse, leaving the fallback to the caller.\n *\n * @internal\n */\nexport function parseDuration(value: string): Time.Milli | undefined {\n\tconst match = /^(\\d+(?:\\.\\d+)?)(ms|s)?$/.exec(value.trim());\n\tif (!match) return undefined;\n\n\tconst amount = Number.parseFloat(match[1]);\n\tif (!Number.isFinite(amount)) return undefined;\n\tif (match[2] === undefined) return amount === 0 ? Time.Milli.zero : undefined;\n\n\treturn Time.Milli(match[2] === \"s\" ? amount * 1000 : amount);\n}\n\n/**\n * Render a duration back out as an attribute value, in the canonical unit.\n *\n * Not rounded: the reflected attribute is parsed straight back into the signal, so rounding here\n * would rewrite the value behind the caller (`1.5ms` becoming `2ms`, and `0.4ms` becoming zero,\n * which also switches buffered playback off).\n *\n * @internal\n */\nexport function formatDuration(value: Time.Milli): string {\n\treturn `${value}ms`;\n}\n","/**\n * The `<moq-watch>` custom element: a broadcast player driven by HTML attributes.\n *\n * Side-effectful: importing this registers the element.\n *\n * @module\n */\nimport type * as Catalog from \"@moq/hang/catalog\";\nimport type { Time } from \"@moq/net\";\nimport * as Moq from \"@moq/net\";\nimport { Effect, Signal, Computed } from \"@moq/signals\";\nimport type * as Audio from \"./audio\";\nimport { type Broadcast, CATALOG_FORMATS, type CatalogFormat } from \"./broadcast\";\nimport { formatDuration, parseDuration } from \"./duration\";\nimport { Player } from \"./player\";\nimport type { Delay, Sync } from \"./sync\";\nimport type * as Text from \"./text\";\nimport type * as Video from \"./video\";\n\nconst OBSERVED = [\n\t\"url\",\n\t\"name\",\n\t\"paused\",\n\t\"volume\",\n\t\"muted\",\n\t\"visible\",\n\t\"announced\",\n\t\"delay\",\n\t\"buffer\",\n\t// Released spellings are observed only so assigning them can fail loudly instead of being ignored.\n\t\"reload\",\n\t\"latency\",\n\t\"latency-min\",\n\t\"jitter\",\n\t\"catalog-format\",\n\t\"captions\",\n] as const;\ntype Observed = (typeof OBSERVED)[number];\n\n// Parse the `visible` attribute into a Visible value, falling back to \"20%\".\nfunction parseVisible(value: string | null): Video.Visible {\n\tconst trimmed = value?.trim();\n\tif (!trimmed) return \"20%\";\n\tif (trimmed === \"never\" || trimmed === \"always\") return trimmed;\n\t// A CSS length usable as an IntersectionObserver rootMargin (px or %).\n\tif (/^-?\\d+(\\.\\d+)?(px|%)$/.test(trimmed)) return trimmed;\n\t// Allow a bare number as a px convenience (e.g. visible=\"200\").\n\tif (/^-?\\d+(\\.\\d+)?$/.test(trimmed)) return `${trimmed}px`;\n\tconsole.warn(`moq-watch: invalid visible=\"${value}\", expected \"never\", \"always\", or a CSS length like \"200px\"`);\n\treturn \"20%\";\n}\n\n// Parse the `delay` attribute: \"auto\" (adaptive), \"instant\" (no buffer, no pacing), or a duration.\nfunction parseDelay(value: string | null): Delay {\n\tconst trimmed = value?.trim();\n\tif (!trimmed || trimmed === \"auto\") return \"auto\";\n\tif (trimmed === \"instant\") return \"instant\";\n\tconst parsed = parseDuration(trimmed);\n\tif (parsed !== undefined) return parsed;\n\tconsole.warn(`moq-watch: invalid delay=\"${value}\", expected \"auto\", \"instant\", or a duration like \"300ms\"`);\n\treturn \"auto\";\n}\n\n// Parse the `buffer` attribute: a duration, or none when absent.\nfunction parseBuffer(value: string | null): Time.Milli {\n\tconst trimmed = value?.trim();\n\tif (!trimmed) return Moq.Time.Milli.zero;\n\tconst parsed = parseDuration(trimmed);\n\tif (parsed !== undefined) return parsed;\n\tconsole.warn(`moq-watch: invalid buffer=\"${value}\", expected a duration like \"30s\"`);\n\treturn Moq.Time.Milli.zero;\n}\n\n/** Parse the element's catalog-format attribute. */\nexport function parseCatalogFormat(value: string | null): CatalogFormat | undefined {\n\tif (value === null) return undefined;\n\treturn CATALOG_FORMATS.find((format) => format === value);\n}\n\n/**\n * Parse a boolean attribute: absent uses `defaultValue`, bare presence is true, and an explicit\n * `\"false\"`/`\"0\"` is false. Presence alone can't express false, and attributes that default to\n * true (`announced`) need to, so every boolean attribute accepts the explicit form.\n */\nfunction parseBoolean(value: string | null, defaultValue: boolean): boolean {\n\tif (value === null) return defaultValue;\n\tconst normalized = value.trim().toLowerCase();\n\treturn normalized !== \"false\" && normalized !== \"0\";\n}\n\n// Close everything when this element is garbage collected.\n// This is primarily to avoid a console.warn that we didn't close() before GC.\n// There's no destructor for web components so this is the best we can do.\nconst cleanup = new FinalizationRegistry<Effect>((signals) => signals.close());\n\n// An optional web component that wraps a <canvas>\nexport default class MoqWatch extends HTMLElement {\n\tstatic observedAttributes = OBSERVED;\n\n\t// The connection to the moq-relay server.\n\t/**\n\t * The relay connection, shared with every other element on the page pointing at the\n\t * same URL. Its `origin` is where the broadcasts live. Replaced by {@link connectionProps}.\n\t */\n\tget connection(): Moq.Connection {\n\t\treturn this.#connection.peek();\n\t}\n\t#connection: Signal<Moq.Connection>;\n\t// The URL, one signal for every connection this element builds, so a rebuild keeps it.\n\t#url = new Signal<URL | undefined>(undefined);\n\n\t/** Headless playback pipeline behind this element. */\n\treadonly player: Player;\n\n\t// The broadcast being watched.\n\tbroadcast: Broadcast;\n\n\t/** Downloads and decodes the video track. `video.source` picks the rendition. */\n\tvideo: Video.Decoder;\n\n\t/** Downloads and decodes the audio track. `audio.source` picks the rendition. */\n\taudio: Audio.Decoder;\n\n\t/** Paints decoded frames to the nested <canvas>. */\n\trenderer: Video.Renderer;\n\n\t/** Plays decoded samples through the speakers. */\n\temitter: Audio.Emitter;\n\n\t/** Selects the caption track. `text.out.available` lists the renditions for a picker. */\n\ttext: Text.Source;\n\n\t/** Renders the selected text cues into an overlay above the canvas. */\n\ttextRenderer: Text.Renderer;\n\n\t/** Keeps audio and video playing at the configured delay. */\n\tsync: Sync;\n\n\t// The mutable user controls. As the top of the tree, this element owns the\n\t// writable Signals and wires read-only views into the pipeline. The UI and\n\t// the attribute/property accessors read and write these directly.\n\treadonly controls = {\n\t\tpaused: new Signal(false),\n\t\tvolume: new Signal(0.5),\n\t\tmuted: new Signal(false),\n\t\t// When video is downloaded relative to the canvas position. See {@link Video.Visible}.\n\t\tvisible: new Signal<Video.Visible>(\"20%\"),\n\t\t// How far playback trails the live edge.\n\t\tdelay: new Signal<Delay>(\"auto\"),\n\t\t// Future-dated media held beyond the live edge before playback skips ahead.\n\t\tbuffer: new Signal<Time.Milli>(Moq.Time.Milli.zero),\n\t\t// The desired video rendition (resolution/bitrate cap).\n\t\ttarget: new Signal<Video.Target | undefined>(undefined),\n\t\t// The desired audio rendition, by track name: a player choosing one sound\n\t\t// among several (a language, say). Unset, the first playable is taken.\n\t\taudioTarget: new Signal<Audio.Target | undefined>(undefined),\n\t\t// The selected caption track name, or undefined for off (the default; captions are opt-in).\n\t\tcaptions: new Signal<string | undefined>(undefined),\n\t};\n\n\t// Broadcast configuration owned here and wired into `broadcast` as inputs.\n\t#name = new Signal<Moq.Path.Valid>(Moq.Path.empty());\n\t#announced = new Signal(true);\n\t#catalogFormat = new Signal<CatalogFormat | undefined>(undefined);\n\t#catalog = new Signal<Catalog.Root | undefined>(undefined);\n\n\t// The canvas element to render into.\n\t#canvas = new Signal<HTMLCanvasElement | undefined>(undefined);\n\n\t// The overlay element captions are drawn into, created lazily on connect (custom elements may not\n\t// touch children in their constructor). Positioned to fill the element, above the canvas.\n\t#captionsOverlay = new Signal<HTMLElement | undefined>(undefined);\n\t#captionsOverlayEl?: HTMLDivElement;\n\n\t// Set when the element is connected to the DOM.\n\t#enabled = new Signal(false);\n\n\t// Stashed volume to restore on unmute.\n\t#unmuteVolume = 0.5;\n\n\t/**\n\t * Effects scoped to this element's lifetime, closed on disconnect.\n\t *\n\t * Public because the element is the top of the tree: it's where an application hangs its own\n\t * reactivity. The components underneath keep theirs private, so `close()` is the only handle.\n\t */\n\treadonly signals = new Effect();\n\n\tconstructor() {\n\t\tsuper();\n\n\t\tcleanup.register(this, this.signals);\n\n\t\tthis.#connection = new Signal(\n\t\t\tnew Moq.Connection({\n\t\t\t\turl: this.#url,\n\t\t\t\tenabled: this.#enabled,\n\t\t\t}),\n\t\t);\n\t\tthis.signals.cleanup(() => this.connection.close());\n\n\t\tthis.player = new Player({\n\t\t\t// through the signal, so a connection rebuilt by `connectionProps` is the one played\n\t\t\torigin: new Computed((effect) => effect.get(effect.get(this.#connection).origin)),\n\t\t\tprobe: new Computed((effect) => effect.get(effect.get(this.#connection).probe)),\n\t\t\tenabled: this.#enabled,\n\t\t\tname: this.#name,\n\t\t\tannounced: this.#announced,\n\t\t\tcatalogFormat: this.#catalogFormat,\n\t\t\tcatalog: this.#catalog,\n\t\t\tcanvas: this.#canvas,\n\t\t\tcontainer: this.#captionsOverlay,\n\t\t\t...this.controls,\n\t\t});\n\t\tthis.signals.cleanup(() => this.player.close());\n\t\tthis.broadcast = this.player.broadcast;\n\t\tthis.video = this.player.video;\n\t\tthis.audio = this.player.audio;\n\t\tthis.renderer = this.player.renderer;\n\t\tthis.emitter = this.player.emitter;\n\t\tthis.text = this.player.text;\n\t\tthis.textRenderer = this.player.textRenderer;\n\t\tthis.sync = this.player.sync;\n\n\t\t// Mute/volume coupling. The element owns the writable volume/muted Signals, so\n\t\t// the policy lives here: muting stashes and zeroes the volume; a zero volume\n\t\t// reports as muted.\n\t\tthis.signals.run((effect) => {\n\t\t\tconst muted = effect.get(this.controls.muted);\n\t\t\tif (muted) {\n\t\t\t\tthis.#unmuteVolume = this.controls.volume.peek() || 0.5;\n\t\t\t\tthis.controls.volume.set(0);\n\t\t\t} else {\n\t\t\t\tthis.controls.volume.set(this.#unmuteVolume);\n\t\t\t}\n\t\t});\n\t\tthis.signals.run((effect) => {\n\t\t\tconst volume = effect.get(this.controls.volume);\n\t\t\tthis.controls.muted.set(volume === 0);\n\t\t});\n\n\t\t// Watch to see if the canvas element is added or removed.\n\t\tconst setCanvas = () => {\n\t\t\tconst canvas = this.querySelector(\"canvas\") ?? undefined;\n\n\t\t\t// A <video> child used to render via MSE. Nothing renders it now, and audio still plays,\n\t\t\t// so the failure looks like a bug in the page instead of a removed feature.\n\t\t\tif (!canvas && this.querySelector(\"video\")) {\n\t\t\t\tconsole.warn(\"moq-watch: rendering requires a <canvas> child; a <video> child does nothing.\");\n\t\t\t}\n\n\t\t\tthis.#canvas.set(canvas);\n\t\t};\n\n\t\tconst observer = new MutationObserver(setCanvas);\n\t\tobserver.observe(this, { childList: true, subtree: true });\n\t\tthis.signals.cleanup(() => observer.disconnect());\n\t\tsetCanvas();\n\n\t\t// Optionally update attributes to match the library state.\n\t\t// This is kind of dangerous because it can create loops.\n\t\t// NOTE: This only runs when the element is connected to the DOM, which is not obvious.\n\t\t// This is because there's no destructor for web components to clean up our effects.\n\t\tthis.signals.run((effect) => {\n\t\t\tconst url = effect.get(this.#url);\n\t\t\tif (url) {\n\t\t\t\tthis.setAttribute(\"url\", url.toString());\n\t\t\t} else {\n\t\t\t\tthis.removeAttribute(\"url\");\n\t\t\t}\n\t\t});\n\n\t\tthis.signals.run((effect) => {\n\t\t\tconst name = effect.get(this.#name);\n\t\t\tthis.setAttribute(\"name\", name.toString());\n\t\t});\n\n\t\tthis.signals.run((effect) => {\n\t\t\tconst muted = effect.get(this.controls.muted);\n\t\t\tif (muted) {\n\t\t\t\tthis.setAttribute(\"muted\", \"\");\n\t\t\t} else {\n\t\t\t\tthis.removeAttribute(\"muted\");\n\t\t\t}\n\t\t});\n\n\t\tthis.signals.run((effect) => {\n\t\t\tconst paused = effect.get(this.controls.paused);\n\t\t\tif (paused) {\n\t\t\t\tthis.setAttribute(\"paused\", \"\");\n\t\t\t} else {\n\t\t\t\tthis.removeAttribute(\"paused\");\n\t\t\t}\n\t\t});\n\n\t\tthis.signals.run((effect) => {\n\t\t\tconst volume = effect.get(this.controls.volume);\n\t\t\tthis.setAttribute(\"volume\", volume.toString());\n\t\t});\n\n\t\tthis.signals.run((effect) => {\n\t\t\tconst visible = effect.get(this.controls.visible);\n\t\t\tthis.setAttribute(\"visible\", visible);\n\t\t});\n\n\t\t// Each knob is 1:1 with its attribute, so the echo back through attributeChangedCallback\n\t\t// parses to the value already held and the effect settles.\n\t\tthis.signals.run((effect) => {\n\t\t\tconst delay = effect.get(this.controls.delay);\n\t\t\tthis.setAttribute(\"delay\", typeof delay === \"number\" ? formatDuration(delay) : delay);\n\t\t});\n\n\t\tthis.signals.run((effect) => {\n\t\t\tthis.setAttribute(\"buffer\", formatDuration(effect.get(this.controls.buffer)));\n\t\t});\n\n\t\t// Track the element's rendered size and feed it into the rendition picker,\n\t\t// scaled by devicePixelRatio so high-DPI screens still get sharp renditions.\n\t\tconst updateDimensions = (width: number, height: number) => {\n\t\t\tif (width <= 0 || height <= 0) return;\n\t\t\tconst dpr = window.devicePixelRatio || 1;\n\t\t\tthis.controls.target.update((prev) => ({\n\t\t\t\t...prev,\n\t\t\t\twidth: Math.round(width * dpr),\n\t\t\t\theight: Math.round(height * dpr),\n\t\t\t}));\n\t\t};\n\n\t\tconst resizeObserver = new ResizeObserver((entries) => {\n\t\t\tconst entry = entries[0];\n\t\t\tif (!entry) return;\n\t\t\tupdateDimensions(entry.contentRect.width, entry.contentRect.height);\n\t\t});\n\t\tresizeObserver.observe(this);\n\t\tthis.signals.cleanup(() => resizeObserver.disconnect());\n\n\t\t// Seed with the current size in case the observer doesn't fire immediately\n\t\t// (e.g. the element is still 0x0 when we attach).\n\t\tconst rect = this.getBoundingClientRect();\n\t\tupdateDimensions(rect.width, rect.height);\n\t}\n\n\t// Annoyingly, we have to use these callbacks to figure out when the element is connected to the DOM.\n\t// This wouldn't be so bad if there was a destructor for web components to clean up our effects.\n\tconnectedCallback() {\n\t\tthis.#enabled.set(true);\n\t\tthis.style.display = \"block\";\n\t\tthis.style.position = \"relative\";\n\n\t\t// Create the caption overlay once, on first connect (the constructor may not add children).\n\t\tif (!this.#captionsOverlayEl) {\n\t\t\tconst overlay = document.createElement(\"div\");\n\t\t\toverlay.style.position = \"absolute\";\n\t\t\toverlay.style.inset = \"0\";\n\t\t\toverlay.style.pointerEvents = \"none\";\n\t\t\t// Above the canvas, which paints at the default stacking level.\n\t\t\toverlay.style.zIndex = \"1\";\n\t\t\tthis.appendChild(overlay);\n\t\t\tthis.#captionsOverlayEl = overlay;\n\t\t\tthis.#captionsOverlay.set(overlay);\n\t\t}\n\t}\n\n\tdisconnectedCallback() {\n\t\t// Stop everything but don't actually cleanup just in case we get added back to the DOM.\n\t\tthis.#enabled.set(false);\n\t}\n\n\tattributeChangedCallback(name: Observed, oldValue: string | null, newValue: string | null) {\n\t\tif (oldValue === newValue) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (name === \"url\") {\n\t\t\tthis.#url.set(newValue ? new URL(newValue) : undefined);\n\t\t} else if (name === \"name\") {\n\t\t\tthis.#name.set(Moq.Path.from(newValue ?? \"\"));\n\t\t} else if (name === \"paused\") {\n\t\t\tthis.controls.paused.set(parseBoolean(newValue, false));\n\t\t} else if (name === \"volume\") {\n\t\t\tconst volume = newValue ? Number.parseFloat(newValue) : 0.5;\n\t\t\tthis.controls.volume.set(volume);\n\t\t} else if (name === \"muted\") {\n\t\t\tthis.controls.muted.set(parseBoolean(newValue, false));\n\t\t} else if (name === \"visible\") {\n\t\t\tthis.controls.visible.set(parseVisible(newValue));\n\t\t} else if (name === \"announced\") {\n\t\t\tthis.#announced.set(parseBoolean(newValue, true));\n\t\t} else if (name === \"delay\") {\n\t\t\tthis.controls.delay.set(parseDelay(newValue));\n\t\t} else if (name === \"buffer\") {\n\t\t\tthis.controls.buffer.set(parseBuffer(newValue));\n\t\t} else if (name === \"reload\") {\n\t\t\tconsole.warn(\"moq-watch: `reload` was renamed to `announced`\");\n\t\t} else if (name === \"latency\" || name === \"latency-min\" || name === \"jitter\") {\n\t\t\tconsole.warn(`moq-watch: \\`${name}\\` is gone; use \\`delay\\` and \\`buffer\\``);\n\t\t} else if (name === \"catalog-format\") {\n\t\t\tthis.#catalogFormat.set(parseCatalogFormat(newValue));\n\t\t} else if (name === \"captions\") {\n\t\t\t// The selected caption track name; absent or empty turns captions off.\n\t\t\tthis.controls.captions.set(newValue || undefined);\n\t\t} else {\n\t\t\tconst exhaustive: never = name;\n\t\t\tthrow new Error(`Invalid attribute: ${exhaustive}`);\n\t\t}\n\t}\n\n\tget url(): URL | undefined {\n\t\treturn this.#url.peek();\n\t}\n\n\tset url(value: string | URL | undefined) {\n\t\tthis.#url.set(value ? new URL(value) : undefined);\n\t}\n\n\t/**\n\t * Rebuild the connection with these options, for a relay the shared pool cannot reach on\n\t * its own: a self-signed certificate pinned with `webtransport.serverCertificateHashes`,\n\t * no WebSocket fallback, no discovery. The URL and the enabled state carry over, and the\n\t * player follows the new connection. Set it before the URL to spare a wasted attempt.\n\t */\n\tset connectionProps(props: Omit<Moq.Connection.Props, \"url\" | \"enabled\">) {\n\t\tconst prev = this.#connection.peek();\n\t\tthis.#connection.set(new Moq.Connection({ ...props, url: this.#url, enabled: this.#enabled }));\n\t\tprev.close();\n\t}\n\n\tget name(): Moq.Path.Valid {\n\t\treturn this.#name.peek();\n\t}\n\n\tset name(value: string | Moq.Path.Valid) {\n\t\tthis.#name.set(Moq.Path.from(value));\n\t}\n\n\tget paused(): boolean {\n\t\treturn this.controls.paused.peek();\n\t}\n\n\tset paused(value: boolean) {\n\t\tthis.controls.paused.set(value);\n\t}\n\n\tget volume(): number {\n\t\treturn this.controls.volume.peek();\n\t}\n\n\tset volume(value: number) {\n\t\tthis.controls.volume.set(value);\n\t}\n\n\tget muted(): boolean {\n\t\treturn this.controls.muted.peek();\n\t}\n\n\tset muted(value: boolean) {\n\t\tthis.controls.muted.set(value);\n\t}\n\n\tget visible(): Video.Visible {\n\t\treturn this.controls.visible.peek();\n\t}\n\n\tset visible(value: Video.Visible) {\n\t\tthis.controls.visible.set(value);\n\t}\n\n\tget announced(): boolean {\n\t\treturn this.#announced.peek();\n\t}\n\n\tset announced(value: boolean) {\n\t\tthis.#announced.set(value);\n\t}\n\n\t/** @internal */\n\tset reload(_value: unknown) {\n\t\tthrow new Error(\"moq-watch: `reload` was renamed to `announced`\");\n\t}\n\n\t/**\n\t * How far playback trails the live edge, in milliseconds. See {@link Delay}.\n\t *\n\t * `\"auto\"` (the default) sizes the jitter buffer from the connection RTT. `\"instant\"` drops the\n\t * clock instead: video paints the moment it decodes and audio is disabled.\n\t */\n\tget delay(): Delay {\n\t\treturn this.controls.delay.peek();\n\t}\n\n\tset delay(value: Delay) {\n\t\tthis.controls.delay.set(value);\n\t}\n\n\t/**\n\t * Future-dated media held beyond the live edge before playback skips ahead, in milliseconds.\n\t *\n\t * Zero (the default) minimizes latency. A larger value enables buffered playback: build up a\n\t * buffer from future-dated frames (e.g. TTS written faster than real-time) and only skip ahead\n\t * once they would sit further than `delay + buffer` past the playhead. Call `reset()` at each\n\t * utterance boundary.\n\t */\n\tget buffer(): Time.Milli {\n\t\treturn this.controls.buffer.peek();\n\t}\n\n\tset buffer(value: Time.Milli) {\n\t\tthis.controls.buffer.set(value);\n\t}\n\n\t/** @internal */\n\tget latency(): Delay {\n\t\treturn this.controls.delay.peek();\n\t}\n\n\tset latency(_value: unknown) {\n\t\tthrow new Error(\"moq-watch: `latency` is gone; use `delay` and `buffer`\");\n\t}\n\n\t/** @internal */\n\tget latencyMin(): Delay {\n\t\treturn this.controls.delay.peek();\n\t}\n\n\tset latencyMin(_value: unknown) {\n\t\tthrow new Error(\"moq-watch: `latencyMin` is gone; use `delay` and `buffer`\");\n\t}\n\n\t/** @internal */\n\tset latencyMax(_value: unknown) {\n\t\tthrow new Error(\n\t\t\t\"moq-watch: `latencyMax` is gone. Use `buffer`, the media held beyond the live edge; the old ceiling included the floor, so it is `latencyMax - delay`.\",\n\t\t);\n\t}\n\n\t/** The jitter buffer in milliseconds. */\n\tget jitter(): Time.Milli {\n\t\treturn this.sync.out.jitter.peek();\n\t}\n\n\tset jitter(_value: unknown) {\n\t\tthrow new Error(\"moq-watch: `jitter` is a readout; set `delay` instead\");\n\t}\n\n\t/**\n\t * Re-anchor playback at an utterance boundary in buffered mode: reset the sync reference\n\t * and flush the audio buffer so the next utterance plays from its own first frame.\n\t */\n\treset(): void {\n\t\tthis.player.reset();\n\t}\n\n\tget catalogFormat(): CatalogFormat | undefined {\n\t\treturn this.#catalogFormat.peek();\n\t}\n\n\tset catalogFormat(value: CatalogFormat | undefined) {\n\t\tthis.#catalogFormat.set(value);\n\t}\n\n\t/**\n\t * The active catalog. Assign directly when `catalogFormat` is `\"manual\"`;\n\t * for `\"hang\"` and `\"msf\"` this is overwritten by the fetch loop.\n\t */\n\tget catalog(): Catalog.Root | undefined {\n\t\treturn this.broadcast.out.catalog.peek();\n\t}\n\n\tset catalog(value: Catalog.Root | undefined) {\n\t\tthis.#catalog.set(value);\n\t}\n\n\t/**\n\t * The selected caption track name, or `undefined` for off (the default). Captions are opt-in:\n\t * assign a track name from `text.out.available` to turn them on. See the `text` source for the\n\t * list of renditions the broadcast publishes.\n\t */\n\tget captions(): string | undefined {\n\t\treturn this.controls.captions.peek();\n\t}\n\n\tset captions(value: string | undefined) {\n\t\tthis.controls.captions.set(value || undefined);\n\t}\n}\n\ncustomElements.define(\"moq-watch\", MoqWatch);\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t\"moq-watch\": MoqWatch;\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"element.js","names":[],"sources":["../src/duration.ts","../src/element.ts"],"sourcesContent":["import { Time } from \"@moq/net\";\n\n/**\n * Parse a duration attribute: `\"300ms\"`, `\"30s\"`, or a bare `\"0\"`.\n *\n * A bare number other than zero is rejected rather than guessed at. Most players measure these in\n * seconds while we store milliseconds, so assuming a unit would turn a value copied from another\n * player into a 1000x error. Zero is exempt because `0ms` and `0s` are the same value.\n *\n * Returns undefined when the value doesn't parse, leaving the fallback to the caller.\n *\n * @internal\n */\nexport function parseDuration(value: string): Time.Milli | undefined {\n\tconst match = /^(\\d+(?:\\.\\d+)?)(ms|s)?$/.exec(value.trim());\n\tif (!match) return undefined;\n\n\tconst amount = Number.parseFloat(match[1]);\n\tif (!Number.isFinite(amount)) return undefined;\n\tif (match[2] === undefined) return amount === 0 ? Time.Milli.zero : undefined;\n\n\treturn Time.Milli(match[2] === \"s\" ? amount * 1000 : amount);\n}\n\n/**\n * Render a duration back out as an attribute value, in the canonical unit.\n *\n * Not rounded: the reflected attribute is parsed straight back into the signal, so rounding here\n * would rewrite the value behind the caller (`1.5ms` becoming `2ms`, and `0.4ms` becoming zero,\n * which also switches buffered playback off).\n *\n * @internal\n */\nexport function formatDuration(value: Time.Milli): string {\n\treturn `${value}ms`;\n}\n","/**\n * The `<moq-watch>` custom element: a broadcast player driven by HTML attributes.\n *\n * Side-effectful: importing this registers the element.\n *\n * @module\n */\nimport type * as Catalog from \"@moq/hang/catalog\";\nimport type { Time } from \"@moq/net\";\nimport * as Moq from \"@moq/net\";\nimport { Effect, Signal, Computed } from \"@moq/signals\";\nimport type * as Audio from \"./audio\";\nimport { type Broadcast, CATALOG_FORMATS, type CatalogFormat } from \"./broadcast\";\nimport { formatDuration, parseDuration } from \"./duration\";\nimport { Player } from \"./player\";\nimport type { Delay, Sync } from \"./sync\";\nimport type * as Text from \"./text\";\nimport type * as Video from \"./video\";\n\nconst OBSERVED = [\n\t\"url\",\n\t\"name\",\n\t\"paused\",\n\t\"volume\",\n\t\"muted\",\n\t\"visible\",\n\t\"announced\",\n\t\"delay\",\n\t\"buffer\",\n\t// Released spellings are observed only so assigning them can fail loudly instead of being ignored.\n\t\"reload\",\n\t\"latency\",\n\t\"latency-min\",\n\t\"jitter\",\n\t\"catalog-format\",\n\t\"captions\",\n] as const;\ntype Observed = (typeof OBSERVED)[number];\n\n// Parse the `visible` attribute into a Visible value, falling back to \"20%\". Exported for tests.\nexport function parseVisible(value: string | null): Video.Visible {\n\tconst trimmed = value?.trim();\n\tif (!trimmed) return \"20%\";\n\tif (trimmed === \"never\" || trimmed === \"always\" || trimmed === \"tab\") return trimmed;\n\t// A CSS length usable as an IntersectionObserver rootMargin (px or %).\n\tif (/^-?\\d+(\\.\\d+)?(px|%)$/.test(trimmed)) return trimmed;\n\t// Allow a bare number as a px convenience (e.g. visible=\"200\").\n\tif (/^-?\\d+(\\.\\d+)?$/.test(trimmed)) return `${trimmed}px`;\n\tconsole.warn(\n\t\t`moq-watch: invalid visible=\"${value}\", expected \"never\", \"always\", \"tab\", or a CSS length like \"200px\"`,\n\t);\n\treturn \"20%\";\n}\n\n// Parse the `delay` attribute: \"auto\" (adaptive), \"instant\" (no buffer, no pacing), or a duration.\nfunction parseDelay(value: string | null): Delay {\n\tconst trimmed = value?.trim();\n\tif (!trimmed || trimmed === \"auto\") return \"auto\";\n\tif (trimmed === \"instant\") return \"instant\";\n\tconst parsed = parseDuration(trimmed);\n\tif (parsed !== undefined) return parsed;\n\tconsole.warn(`moq-watch: invalid delay=\"${value}\", expected \"auto\", \"instant\", or a duration like \"300ms\"`);\n\treturn \"auto\";\n}\n\n// Parse the `buffer` attribute: a duration, or none when absent.\nfunction parseBuffer(value: string | null): Time.Milli {\n\tconst trimmed = value?.trim();\n\tif (!trimmed) return Moq.Time.Milli.zero;\n\tconst parsed = parseDuration(trimmed);\n\tif (parsed !== undefined) return parsed;\n\tconsole.warn(`moq-watch: invalid buffer=\"${value}\", expected a duration like \"30s\"`);\n\treturn Moq.Time.Milli.zero;\n}\n\n/** Parse the element's catalog-format attribute. */\nexport function parseCatalogFormat(value: string | null): CatalogFormat | undefined {\n\tif (value === null) return undefined;\n\treturn CATALOG_FORMATS.find((format) => format === value);\n}\n\n/**\n * Parse a boolean attribute: absent uses `defaultValue`, bare presence is true, and an explicit\n * `\"false\"`/`\"0\"` is false. Presence alone can't express false, and attributes that default to\n * true (`announced`) need to, so every boolean attribute accepts the explicit form.\n */\nfunction parseBoolean(value: string | null, defaultValue: boolean): boolean {\n\tif (value === null) return defaultValue;\n\tconst normalized = value.trim().toLowerCase();\n\treturn normalized !== \"false\" && normalized !== \"0\";\n}\n\n// Close everything when this element is garbage collected.\n// This is primarily to avoid a console.warn that we didn't close() before GC.\n// There's no destructor for web components so this is the best we can do.\nconst cleanup = new FinalizationRegistry<Effect>((signals) => signals.close());\n\n// An optional web component that wraps a <canvas>\nexport default class MoqWatch extends HTMLElement {\n\tstatic observedAttributes = OBSERVED;\n\n\t// The connection to the moq-relay server.\n\t/**\n\t * The relay connection, shared with every other element on the page pointing at the\n\t * same URL. Its `origin` is where the broadcasts live. Replaced by {@link connectionProps}.\n\t */\n\tget connection(): Moq.Connection {\n\t\treturn this.#connection.peek();\n\t}\n\t#connection: Signal<Moq.Connection>;\n\t// The URL, one signal for every connection this element builds, so a rebuild keeps it.\n\t#url = new Signal<URL | undefined>(undefined);\n\n\t/** Headless playback pipeline behind this element. */\n\treadonly player: Player;\n\n\t// The broadcast being watched.\n\tbroadcast: Broadcast;\n\n\t/** Downloads and decodes the video track. `video.source` picks the rendition. */\n\tvideo: Video.Decoder;\n\n\t/** Downloads and decodes the audio track. `audio.source` picks the rendition. */\n\taudio: Audio.Decoder;\n\n\t/** Paints decoded frames to the nested <canvas>. */\n\trenderer: Video.Renderer;\n\n\t/** Plays decoded samples through the speakers. */\n\temitter: Audio.Emitter;\n\n\t/** Selects the caption track. `text.out.available` lists the renditions for a picker. */\n\ttext: Text.Source;\n\n\t/** Renders the selected text cues into an overlay above the canvas. */\n\ttextRenderer: Text.Renderer;\n\n\t/** Keeps audio and video playing at the configured delay. */\n\tsync: Sync;\n\n\t// The mutable user controls. As the top of the tree, this element owns the\n\t// writable Signals and wires read-only views into the pipeline. The UI and\n\t// the attribute/property accessors read and write these directly.\n\treadonly controls = {\n\t\tpaused: new Signal(false),\n\t\tvolume: new Signal(0.5),\n\t\tmuted: new Signal(false),\n\t\t// When video is downloaded relative to the canvas position. See {@link Video.Visible}.\n\t\tvisible: new Signal<Video.Visible>(\"20%\"),\n\t\t// How far playback trails the live edge.\n\t\tdelay: new Signal<Delay>(\"auto\"),\n\t\t// Future-dated media held beyond the live edge before playback skips ahead.\n\t\tbuffer: new Signal<Time.Milli>(Moq.Time.Milli.zero),\n\t\t// The desired video rendition (resolution/bitrate cap).\n\t\ttarget: new Signal<Video.Target | undefined>(undefined),\n\t\t// The desired audio rendition, by track name: a player choosing one sound\n\t\t// among several (a language, say). Unset, the first playable is taken.\n\t\taudioTarget: new Signal<Audio.Target | undefined>(undefined),\n\t\t// The selected caption track name, or undefined for off (the default; captions are opt-in).\n\t\tcaptions: new Signal<string | undefined>(undefined),\n\t};\n\n\t// Broadcast configuration owned here and wired into `broadcast` as inputs.\n\t#name = new Signal<Moq.Path.Valid>(Moq.Path.empty());\n\t#announced = new Signal(true);\n\t#catalogFormat = new Signal<CatalogFormat | undefined>(undefined);\n\t#catalog = new Signal<Catalog.Root | undefined>(undefined);\n\n\t// The canvas element to render into.\n\t#canvas = new Signal<HTMLCanvasElement | undefined>(undefined);\n\n\t// The overlay element captions are drawn into, created lazily on connect (custom elements may not\n\t// touch children in their constructor). Positioned to fill the element, above the canvas.\n\t#captionsOverlay = new Signal<HTMLElement | undefined>(undefined);\n\t#captionsOverlayEl?: HTMLDivElement;\n\n\t// Set when the element is connected to the DOM.\n\t#enabled = new Signal(false);\n\n\t// Stashed volume to restore on unmute.\n\t#unmuteVolume = 0.5;\n\n\t/**\n\t * Effects scoped to this element's lifetime, closed on disconnect.\n\t *\n\t * Public because the element is the top of the tree: it's where an application hangs its own\n\t * reactivity. The components underneath keep theirs private, so `close()` is the only handle.\n\t */\n\treadonly signals = new Effect();\n\n\tconstructor() {\n\t\tsuper();\n\n\t\tcleanup.register(this, this.signals);\n\n\t\tthis.#connection = new Signal(\n\t\t\tnew Moq.Connection({\n\t\t\t\turl: this.#url,\n\t\t\t\tenabled: this.#enabled,\n\t\t\t}),\n\t\t);\n\t\tthis.signals.cleanup(() => this.connection.close());\n\n\t\tthis.player = new Player({\n\t\t\t// through the signal, so a connection rebuilt by `connectionProps` is the one played\n\t\t\torigin: new Computed((effect) => effect.get(effect.get(this.#connection).origin)),\n\t\t\tprobe: new Computed((effect) => effect.get(effect.get(this.#connection).probe)),\n\t\t\tenabled: this.#enabled,\n\t\t\tname: this.#name,\n\t\t\tannounced: this.#announced,\n\t\t\tcatalogFormat: this.#catalogFormat,\n\t\t\tcatalog: this.#catalog,\n\t\t\tcanvas: this.#canvas,\n\t\t\tcontainer: this.#captionsOverlay,\n\t\t\t...this.controls,\n\t\t});\n\t\tthis.signals.cleanup(() => this.player.close());\n\t\tthis.broadcast = this.player.broadcast;\n\t\tthis.video = this.player.video;\n\t\tthis.audio = this.player.audio;\n\t\tthis.renderer = this.player.renderer;\n\t\tthis.emitter = this.player.emitter;\n\t\tthis.text = this.player.text;\n\t\tthis.textRenderer = this.player.textRenderer;\n\t\tthis.sync = this.player.sync;\n\n\t\t// Mute/volume coupling. The element owns the writable volume/muted Signals, so\n\t\t// the policy lives here: muting stashes and zeroes the volume; a zero volume\n\t\t// reports as muted.\n\t\tthis.signals.run((effect) => {\n\t\t\tconst muted = effect.get(this.controls.muted);\n\t\t\tif (muted) {\n\t\t\t\tthis.#unmuteVolume = this.controls.volume.peek() || 0.5;\n\t\t\t\tthis.controls.volume.set(0);\n\t\t\t} else {\n\t\t\t\tthis.controls.volume.set(this.#unmuteVolume);\n\t\t\t}\n\t\t});\n\t\tthis.signals.run((effect) => {\n\t\t\tconst volume = effect.get(this.controls.volume);\n\t\t\tthis.controls.muted.set(volume === 0);\n\t\t});\n\n\t\t// Watch to see if the canvas element is added or removed.\n\t\tconst setCanvas = () => {\n\t\t\tconst canvas = this.querySelector(\"canvas\") ?? undefined;\n\n\t\t\t// A <video> child used to render via MSE. Nothing renders it now, and audio still plays,\n\t\t\t// so the failure looks like a bug in the page instead of a removed feature.\n\t\t\tif (!canvas && this.querySelector(\"video\")) {\n\t\t\t\tconsole.warn(\"moq-watch: rendering requires a <canvas> child; a <video> child does nothing.\");\n\t\t\t}\n\n\t\t\tthis.#canvas.set(canvas);\n\t\t};\n\n\t\tconst observer = new MutationObserver(setCanvas);\n\t\tobserver.observe(this, { childList: true, subtree: true });\n\t\tthis.signals.cleanup(() => observer.disconnect());\n\t\tsetCanvas();\n\n\t\t// Optionally update attributes to match the library state.\n\t\t// This is kind of dangerous because it can create loops.\n\t\t// NOTE: This only runs when the element is connected to the DOM, which is not obvious.\n\t\t// This is because there's no destructor for web components to clean up our effects.\n\t\tthis.signals.run((effect) => {\n\t\t\tconst url = effect.get(this.#url);\n\t\t\tif (url) {\n\t\t\t\tthis.setAttribute(\"url\", url.toString());\n\t\t\t} else {\n\t\t\t\tthis.removeAttribute(\"url\");\n\t\t\t}\n\t\t});\n\n\t\tthis.signals.run((effect) => {\n\t\t\tconst name = effect.get(this.#name);\n\t\t\tthis.setAttribute(\"name\", name.toString());\n\t\t});\n\n\t\tthis.signals.run((effect) => {\n\t\t\tconst muted = effect.get(this.controls.muted);\n\t\t\tif (muted) {\n\t\t\t\tthis.setAttribute(\"muted\", \"\");\n\t\t\t} else {\n\t\t\t\tthis.removeAttribute(\"muted\");\n\t\t\t}\n\t\t});\n\n\t\tthis.signals.run((effect) => {\n\t\t\tconst paused = effect.get(this.controls.paused);\n\t\t\tif (paused) {\n\t\t\t\tthis.setAttribute(\"paused\", \"\");\n\t\t\t} else {\n\t\t\t\tthis.removeAttribute(\"paused\");\n\t\t\t}\n\t\t});\n\n\t\tthis.signals.run((effect) => {\n\t\t\tconst volume = effect.get(this.controls.volume);\n\t\t\tthis.setAttribute(\"volume\", volume.toString());\n\t\t});\n\n\t\tthis.signals.run((effect) => {\n\t\t\tconst visible = effect.get(this.controls.visible);\n\t\t\tthis.setAttribute(\"visible\", visible);\n\t\t});\n\n\t\t// Each knob is 1:1 with its attribute, so the echo back through attributeChangedCallback\n\t\t// parses to the value already held and the effect settles.\n\t\tthis.signals.run((effect) => {\n\t\t\tconst delay = effect.get(this.controls.delay);\n\t\t\tthis.setAttribute(\"delay\", typeof delay === \"number\" ? formatDuration(delay) : delay);\n\t\t});\n\n\t\tthis.signals.run((effect) => {\n\t\t\tthis.setAttribute(\"buffer\", formatDuration(effect.get(this.controls.buffer)));\n\t\t});\n\n\t\t// Track the element's rendered size and feed it into the rendition picker,\n\t\t// scaled by devicePixelRatio so high-DPI screens still get sharp renditions.\n\t\tconst updateDimensions = (width: number, height: number) => {\n\t\t\tif (width <= 0 || height <= 0) return;\n\t\t\tconst dpr = window.devicePixelRatio || 1;\n\t\t\tthis.controls.target.update((prev) => ({\n\t\t\t\t...prev,\n\t\t\t\twidth: Math.round(width * dpr),\n\t\t\t\theight: Math.round(height * dpr),\n\t\t\t}));\n\t\t};\n\n\t\tconst resizeObserver = new ResizeObserver((entries) => {\n\t\t\tconst entry = entries[0];\n\t\t\tif (!entry) return;\n\t\t\tupdateDimensions(entry.contentRect.width, entry.contentRect.height);\n\t\t});\n\t\tresizeObserver.observe(this);\n\t\tthis.signals.cleanup(() => resizeObserver.disconnect());\n\n\t\t// Seed with the current size in case the observer doesn't fire immediately\n\t\t// (e.g. the element is still 0x0 when we attach).\n\t\tconst rect = this.getBoundingClientRect();\n\t\tupdateDimensions(rect.width, rect.height);\n\t}\n\n\t// Annoyingly, we have to use these callbacks to figure out when the element is connected to the DOM.\n\t// This wouldn't be so bad if there was a destructor for web components to clean up our effects.\n\tconnectedCallback() {\n\t\tthis.#enabled.set(true);\n\t\tthis.style.display = \"block\";\n\t\tthis.style.position = \"relative\";\n\n\t\t// Create the caption overlay once, on first connect (the constructor may not add children).\n\t\tif (!this.#captionsOverlayEl) {\n\t\t\tconst overlay = document.createElement(\"div\");\n\t\t\toverlay.style.position = \"absolute\";\n\t\t\toverlay.style.inset = \"0\";\n\t\t\toverlay.style.pointerEvents = \"none\";\n\t\t\t// Above the canvas, which paints at the default stacking level.\n\t\t\toverlay.style.zIndex = \"1\";\n\t\t\tthis.appendChild(overlay);\n\t\t\tthis.#captionsOverlayEl = overlay;\n\t\t\tthis.#captionsOverlay.set(overlay);\n\t\t}\n\t}\n\n\tdisconnectedCallback() {\n\t\t// Stop everything but don't actually cleanup just in case we get added back to the DOM.\n\t\tthis.#enabled.set(false);\n\t}\n\n\tattributeChangedCallback(name: Observed, oldValue: string | null, newValue: string | null) {\n\t\tif (oldValue === newValue) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (name === \"url\") {\n\t\t\tthis.#url.set(newValue ? new URL(newValue) : undefined);\n\t\t} else if (name === \"name\") {\n\t\t\tthis.#name.set(Moq.Path.from(newValue ?? \"\"));\n\t\t} else if (name === \"paused\") {\n\t\t\tthis.controls.paused.set(parseBoolean(newValue, false));\n\t\t} else if (name === \"volume\") {\n\t\t\tconst volume = newValue ? Number.parseFloat(newValue) : 0.5;\n\t\t\tthis.controls.volume.set(volume);\n\t\t} else if (name === \"muted\") {\n\t\t\tthis.controls.muted.set(parseBoolean(newValue, false));\n\t\t} else if (name === \"visible\") {\n\t\t\tthis.controls.visible.set(parseVisible(newValue));\n\t\t} else if (name === \"announced\") {\n\t\t\tthis.#announced.set(parseBoolean(newValue, true));\n\t\t} else if (name === \"delay\") {\n\t\t\tthis.controls.delay.set(parseDelay(newValue));\n\t\t} else if (name === \"buffer\") {\n\t\t\tthis.controls.buffer.set(parseBuffer(newValue));\n\t\t} else if (name === \"reload\") {\n\t\t\tconsole.warn(\"moq-watch: `reload` was renamed to `announced`\");\n\t\t} else if (name === \"latency\" || name === \"latency-min\" || name === \"jitter\") {\n\t\t\tconsole.warn(`moq-watch: \\`${name}\\` is gone; use \\`delay\\` and \\`buffer\\``);\n\t\t} else if (name === \"catalog-format\") {\n\t\t\tthis.#catalogFormat.set(parseCatalogFormat(newValue));\n\t\t} else if (name === \"captions\") {\n\t\t\t// The selected caption track name; absent or empty turns captions off.\n\t\t\tthis.controls.captions.set(newValue || undefined);\n\t\t} else {\n\t\t\tconst exhaustive: never = name;\n\t\t\tthrow new Error(`Invalid attribute: ${exhaustive}`);\n\t\t}\n\t}\n\n\tget url(): URL | undefined {\n\t\treturn this.#url.peek();\n\t}\n\n\tset url(value: string | URL | undefined) {\n\t\tthis.#url.set(value ? new URL(value) : undefined);\n\t}\n\n\t/**\n\t * Rebuild the connection with these options, for a relay the shared pool cannot reach on\n\t * its own: a self-signed certificate pinned with `webtransport.serverCertificateHashes`,\n\t * no WebSocket fallback, no discovery. The URL and the enabled state carry over, and the\n\t * player follows the new connection. Set it before the URL to spare a wasted attempt.\n\t */\n\tset connectionProps(props: Omit<Moq.Connection.Props, \"url\" | \"enabled\">) {\n\t\tconst prev = this.#connection.peek();\n\t\tthis.#connection.set(new Moq.Connection({ ...props, url: this.#url, enabled: this.#enabled }));\n\t\tprev.close();\n\t}\n\n\tget name(): Moq.Path.Valid {\n\t\treturn this.#name.peek();\n\t}\n\n\tset name(value: string | Moq.Path.Valid) {\n\t\tthis.#name.set(Moq.Path.from(value));\n\t}\n\n\tget paused(): boolean {\n\t\treturn this.controls.paused.peek();\n\t}\n\n\tset paused(value: boolean) {\n\t\tthis.controls.paused.set(value);\n\t}\n\n\tget volume(): number {\n\t\treturn this.controls.volume.peek();\n\t}\n\n\tset volume(value: number) {\n\t\tthis.controls.volume.set(value);\n\t}\n\n\tget muted(): boolean {\n\t\treturn this.controls.muted.peek();\n\t}\n\n\tset muted(value: boolean) {\n\t\tthis.controls.muted.set(value);\n\t}\n\n\tget visible(): Video.Visible {\n\t\treturn this.controls.visible.peek();\n\t}\n\n\tset visible(value: Video.Visible) {\n\t\tthis.controls.visible.set(value);\n\t}\n\n\tget announced(): boolean {\n\t\treturn this.#announced.peek();\n\t}\n\n\tset announced(value: boolean) {\n\t\tthis.#announced.set(value);\n\t}\n\n\t/** @internal */\n\tset reload(_value: unknown) {\n\t\tthrow new Error(\"moq-watch: `reload` was renamed to `announced`\");\n\t}\n\n\t/**\n\t * How far playback trails the live edge, in milliseconds. See {@link Delay}.\n\t *\n\t * `\"auto\"` (the default) sizes the jitter buffer from the connection RTT. `\"instant\"` drops the\n\t * clock instead: video paints the moment it decodes and audio is disabled.\n\t */\n\tget delay(): Delay {\n\t\treturn this.controls.delay.peek();\n\t}\n\n\tset delay(value: Delay) {\n\t\tthis.controls.delay.set(value);\n\t}\n\n\t/**\n\t * Future-dated media held beyond the live edge before playback skips ahead, in milliseconds.\n\t *\n\t * Zero (the default) minimizes latency. A larger value enables buffered playback: build up a\n\t * buffer from future-dated frames (e.g. TTS written faster than real-time) and only skip ahead\n\t * once they would sit further than `delay + buffer` past the playhead. Call `reset()` at each\n\t * utterance boundary.\n\t */\n\tget buffer(): Time.Milli {\n\t\treturn this.controls.buffer.peek();\n\t}\n\n\tset buffer(value: Time.Milli) {\n\t\tthis.controls.buffer.set(value);\n\t}\n\n\t/** @internal */\n\tget latency(): Delay {\n\t\treturn this.controls.delay.peek();\n\t}\n\n\tset latency(_value: unknown) {\n\t\tthrow new Error(\"moq-watch: `latency` is gone; use `delay` and `buffer`\");\n\t}\n\n\t/** @internal */\n\tget latencyMin(): Delay {\n\t\treturn this.controls.delay.peek();\n\t}\n\n\tset latencyMin(_value: unknown) {\n\t\tthrow new Error(\"moq-watch: `latencyMin` is gone; use `delay` and `buffer`\");\n\t}\n\n\t/** @internal */\n\tset latencyMax(_value: unknown) {\n\t\tthrow new Error(\n\t\t\t\"moq-watch: `latencyMax` is gone. Use `buffer`, the media held beyond the live edge; the old ceiling included the floor, so it is `latencyMax - delay`.\",\n\t\t);\n\t}\n\n\t/** The jitter buffer in milliseconds. */\n\tget jitter(): Time.Milli {\n\t\treturn this.sync.out.jitter.peek();\n\t}\n\n\tset jitter(_value: unknown) {\n\t\tthrow new Error(\"moq-watch: `jitter` is a readout; set `delay` instead\");\n\t}\n\n\t/**\n\t * Re-anchor playback at an utterance boundary in buffered mode: reset the sync reference\n\t * and flush the audio buffer so the next utterance plays from its own first frame.\n\t */\n\treset(): void {\n\t\tthis.player.reset();\n\t}\n\n\tget catalogFormat(): CatalogFormat | undefined {\n\t\treturn this.#catalogFormat.peek();\n\t}\n\n\tset catalogFormat(value: CatalogFormat | undefined) {\n\t\tthis.#catalogFormat.set(value);\n\t}\n\n\t/**\n\t * The active catalog. Assign directly when `catalogFormat` is `\"manual\"`;\n\t * for `\"hang\"` and `\"msf\"` this is overwritten by the fetch loop.\n\t */\n\tget catalog(): Catalog.Root | undefined {\n\t\treturn this.broadcast.out.catalog.peek();\n\t}\n\n\tset catalog(value: Catalog.Root | undefined) {\n\t\tthis.#catalog.set(value);\n\t}\n\n\t/**\n\t * The selected caption track name, or `undefined` for off (the default). Captions are opt-in:\n\t * assign a track name from `text.out.available` to turn them on. See the `text` source for the\n\t * list of renditions the broadcast publishes.\n\t */\n\tget captions(): string | undefined {\n\t\treturn this.controls.captions.peek();\n\t}\n\n\tset captions(value: string | undefined) {\n\t\tthis.controls.captions.set(value || undefined);\n\t}\n}\n\ncustomElements.define(\"moq-watch\", MoqWatch);\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t\"moq-watch\": MoqWatch;\n\t}\n}\n"],"mappings":"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"}
package/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { _ as e, a as t, c as n, d as r, f as i, g as a, h as o, i as s, l as c, m as l, n as u, o as d, p as f, r as p, s as m, t as h, u as g, v as _ } from "./player-BFRzc5zw.js";
1
+ import { _ as e, a as t, c as n, d as r, f as i, g as a, h as o, i as s, l as c, m as l, n as u, o as d, p as f, r as p, s as m, t as h, u as g, v as _ } from "./player-C7Ih0ExN.js";
2
2
  import * as v from "@norskvideo/moq-hang";
3
3
  import * as y from "@norskvideo/moq-net";
4
4
  import * as b from "@norskvideo/moq-signals";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@norskvideo/moq-watch",
3
3
  "type": "module",
4
- "version": "0.2.1",
4
+ "version": "0.2.2",
5
5
  "description": "Watch Media over QUIC broadcasts",
6
6
  "license": "(MIT OR Apache-2.0)",
7
7
  "repository": {
@@ -38,10 +38,10 @@
38
38
  ],
39
39
  "dependencies": {
40
40
  "media-captions": "0.0.18",
41
- "@norskvideo/moq-hang": "^0.2.1",
42
- "@norskvideo/moq-json": "^0.2.1",
43
- "@norskvideo/moq-net": "^0.2.1",
44
- "@norskvideo/moq-msf": "^0.2.1",
45
- "@norskvideo/moq-signals": "^0.2.1"
41
+ "@norskvideo/moq-hang": "^0.2.2",
42
+ "@norskvideo/moq-json": "^0.2.2",
43
+ "@norskvideo/moq-net": "^0.2.2",
44
+ "@norskvideo/moq-msf": "^0.2.2",
45
+ "@norskvideo/moq-signals": "^0.2.2"
46
46
  }
47
47
  }
@@ -1815,8 +1815,26 @@ async function tt(e) {
1815
1815
  }
1816
1816
  Object.assign(tt, { [Y]: We });
1817
1817
  //#endregion
1818
- //#region src/video/renderer.ts
1819
- var nt = .01, rt = class {
1818
+ //#region src/video/tab-visible.ts
1819
+ var nt = class {
1820
+ visible;
1821
+ #e;
1822
+ #t;
1823
+ #n = new i();
1824
+ constructor(e = {}) {
1825
+ let t = e.doc ?? globalThis.document;
1826
+ this.#e = new a(t?.hidden ?? !1), this.visible = new a(!this.#e.peek()), this.#t = o(e.grace ?? 5e3), t && this.#n.event(t, "visibilitychange", () => this.#e.set(t.hidden)), this.#n.run((e) => {
1827
+ if (!e.get(this.#e)) {
1828
+ this.visible.set(!0);
1829
+ return;
1830
+ }
1831
+ e.timer(() => this.visible.set(!1), e.get(this.#t));
1832
+ });
1833
+ }
1834
+ close() {
1835
+ this.#n.close();
1836
+ }
1837
+ }, rt = .01, it = class {
1820
1838
  decoder;
1821
1839
  in;
1822
1840
  #e = {
@@ -1827,22 +1845,27 @@ var nt = .01, rt = class {
1827
1845
  out = s(this.#e);
1828
1846
  #t = new a(void 0);
1829
1847
  #n = new i();
1848
+ #r;
1830
1849
  constructor(e) {
1831
1850
  this.decoder = e.decoder, this.in = {
1832
1851
  canvas: o(e?.canvas),
1833
- visible: o(e?.visible ?? "20%")
1834
- }, this.#n.run((e) => {
1852
+ visible: o(e?.visible ?? "20%"),
1853
+ hiddenGrace: o(e?.hiddenGrace ?? 5e3)
1854
+ }, this.#r = new nt({
1855
+ doc: e.document,
1856
+ grace: this.in.hiddenGrace
1857
+ }), this.#n.run((e) => {
1835
1858
  let t = e.get(this.in.canvas);
1836
1859
  this.#t.set(t?.getContext("2d") ?? void 0);
1837
- }), this.#n.run(this.#i.bind(this)), this.#n.run(this.#a.bind(this)), this.#n.run(this.#r.bind(this));
1860
+ }), this.#n.run(this.#a.bind(this)), this.#n.run(this.#o.bind(this)), this.#n.run(this.#i.bind(this));
1838
1861
  }
1839
- #r(e) {
1862
+ #i(e) {
1840
1863
  let t = e.getAll([this.in.canvas, this.decoder.out.display]);
1841
1864
  if (!t) return;
1842
1865
  let [n, r] = t;
1843
1866
  (n.width !== r.width || n.height !== r.height) && (n.width = r.width, n.height = r.height);
1844
1867
  }
1845
- #i(e) {
1868
+ #a(e) {
1846
1869
  let t = e.get(this.in.visible);
1847
1870
  if (t === "never") {
1848
1871
  this.#e.visible.set(!1);
@@ -1852,31 +1875,36 @@ var nt = .01, rt = class {
1852
1875
  this.#e.visible.set(!0), e.cleanup(() => this.#e.visible.set(!1));
1853
1876
  return;
1854
1877
  }
1855
- let n = e.get(this.in.canvas);
1856
- if (!n) {
1878
+ let n = e.get(this.#r.visible);
1879
+ if (t === "tab") {
1880
+ this.#e.visible.set(n), e.cleanup(() => this.#e.visible.set(!1));
1881
+ return;
1882
+ }
1883
+ let r = e.get(this.in.canvas);
1884
+ if (!r) {
1857
1885
  this.#e.visible.set(!1);
1858
1886
  return;
1859
1887
  }
1860
- let r = !1, i = () => {
1861
- this.#e.visible.set(r && !document.hidden);
1862
- }, a = (e) => {
1863
- for (let t of e) r = t.isIntersecting, i();
1864
- }, o;
1888
+ let i = !1, a = () => {
1889
+ this.#e.visible.set(i && n);
1890
+ }, o = (e) => {
1891
+ for (let t of e) i = t.isIntersecting, a();
1892
+ }, s;
1865
1893
  try {
1866
- o = new IntersectionObserver(a, {
1867
- threshold: nt,
1894
+ s = new IntersectionObserver(o, {
1895
+ threshold: rt,
1868
1896
  rootMargin: t
1869
1897
  });
1870
1898
  } catch {
1871
- console.warn(`moq-watch: invalid visible margin "${t}", using "0px"`), o = new IntersectionObserver(a, { threshold: nt });
1899
+ console.warn(`moq-watch: invalid visible margin "${t}", using "0px"`), s = new IntersectionObserver(o, { threshold: rt });
1872
1900
  }
1873
- i(), e.event(document, "visibilitychange", i), o.observe(n), e.cleanup(() => o.disconnect()), e.cleanup(() => this.#e.visible.set(!1));
1901
+ a(), s.observe(r), e.cleanup(() => s.disconnect()), e.cleanup(() => this.#e.visible.set(!1));
1874
1902
  }
1875
- #a(e) {
1903
+ #o(e) {
1876
1904
  let t = e.get(this.#t);
1877
1905
  if (!t) return;
1878
1906
  let n = e.get(this.decoder.out.frame), i = e.get(this.decoder.source.out.catalog), a = requestAnimationFrame(() => {
1879
- this.#o(t, n, i), n ? (this.#e.frame.update((e) => (e?.close(), n.clone())), this.#e.timestamp.set(r.Milli.fromMicro(n.timestamp))) : (this.#e.frame.update((e) => {
1907
+ this.#s(t, n, i), n ? (this.#e.frame.update((e) => (e?.close(), n.clone())), this.#e.timestamp.set(r.Milli.fromMicro(n.timestamp))) : (this.#e.frame.update((e) => {
1880
1908
  e?.close();
1881
1909
  }), this.#e.timestamp.set(void 0)), a = void 0;
1882
1910
  });
@@ -1884,7 +1912,7 @@ var nt = .01, rt = class {
1884
1912
  a !== void 0 && cancelAnimationFrame(a);
1885
1913
  });
1886
1914
  }
1887
- #o(e, t, n) {
1915
+ #s(e, t, n) {
1888
1916
  if (!t) {
1889
1917
  e.fillStyle = "#000", e.fillRect(0, 0, e.canvas.width, e.canvas.height);
1890
1918
  return;
@@ -1899,12 +1927,12 @@ var nt = .01, rt = class {
1899
1927
  close() {
1900
1928
  this.#e.frame.update((e) => {
1901
1929
  e?.close();
1902
- }), this.#e.timestamp.set(void 0), this.#n.close();
1930
+ }), this.#e.timestamp.set(void 0), this.#n.close(), this.#r.close();
1903
1931
  }
1904
1932
  };
1905
1933
  //#endregion
1906
1934
  //#region src/video/source.ts
1907
- function it(e) {
1935
+ function at(e) {
1908
1936
  return (t) => {
1909
1937
  let n = [], r = [];
1910
1938
  for (let [i, a] of t) if (a.codedWidth && a.codedHeight) {
@@ -1920,7 +1948,7 @@ function it(e) {
1920
1948
  return n.sort((e, t) => t.size - e.size), n.length > 0 ? n.map((e) => e.name) : r.length > 0 ? (r.sort((e, t) => e.size - t.size), [r[0].name]) : t.map(([e]) => e);
1921
1949
  };
1922
1950
  }
1923
- function at(e, t) {
1951
+ function ot(e, t) {
1924
1952
  return (n) => {
1925
1953
  let r = [], i = [];
1926
1954
  for (let [a, o] of n) {
@@ -1950,7 +1978,7 @@ function $(e) {
1950
1978
  return n.sort((e, t) => t.bitrate - e.bitrate), n.length > 0 ? n.map((e) => e.name) : r.length > 0 ? (r.sort((e, t) => e.bitrate - t.bitrate), [r[0].name]) : t.map(([e]) => e);
1951
1979
  };
1952
1980
  }
1953
- function ot(e) {
1981
+ function st(e) {
1954
1982
  let t = e[0];
1955
1983
  for (let n of e) {
1956
1984
  let [, e] = n, [, r] = t, i = (e.codedWidth ?? 0) * (e.codedHeight ?? 0), a = (r.codedWidth ?? 0) * (r.codedHeight ?? 0);
@@ -1962,15 +1990,15 @@ function ot(e) {
1962
1990
  }
1963
1991
  return t[0];
1964
1992
  }
1965
- function st(e) {
1993
+ function ct(e) {
1966
1994
  let t = Object.entries(e).filter(([, e]) => !e.stalled);
1967
1995
  if (t.length > 0) return Object.fromEntries(t);
1968
1996
  let n = Object.entries(e);
1969
1997
  if (n.length === 0) return {};
1970
- let r = $(0)(n), i = r.length === 1 ? r[0] : at(0, 0)(n)[0];
1998
+ let r = $(0)(n), i = r.length === 1 ? r[0] : ot(0, 0)(n)[0];
1971
1999
  return { [i]: e[i] };
1972
2000
  }
1973
- var ct = class {
2001
+ var lt = class {
1974
2002
  in;
1975
2003
  #e = {
1976
2004
  catalog: new a(void 0),
@@ -2039,7 +2067,7 @@ var ct = class {
2039
2067
  e.set(this.#e.track, n.name), e.set(this.#e.config, r);
2040
2068
  return;
2041
2069
  }
2042
- let r = st(t);
2070
+ let r = ct(t);
2043
2071
  if (Object.keys(r).length === 0) return;
2044
2072
  let i = n;
2045
2073
  if (!n?.bitrate) {
@@ -2062,7 +2090,7 @@ var ct = class {
2062
2090
  if (n.length === 0) return;
2063
2091
  if (n.length === 1) return n[0][0];
2064
2092
  let r = [];
2065
- if (t?.pixels != null && r.push(it(t.pixels)), (t?.width != null || t?.height != null) && r.push(at(t.width, t.height)), t?.bitrate != null && r.push($(t.bitrate)), r.length === 0) return ot(n);
2093
+ if (t?.pixels != null && r.push(at(t.pixels)), (t?.width != null || t?.height != null) && r.push(ot(t.width, t.height)), t?.bitrate != null && r.push($(t.bitrate)), r.length === 0) return st(n);
2066
2094
  let i = r.map((e) => e(n)), a = i.map((e) => new Set(e));
2067
2095
  for (let e of i[0]) if (a.every((t) => t.has(e))) return e;
2068
2096
  console.warn("conflicting rendition filters, no rendition satisfies all criteria");
@@ -2070,7 +2098,7 @@ var ct = class {
2070
2098
  close() {
2071
2099
  this.#t.close();
2072
2100
  }
2073
- }, lt = class {
2101
+ }, ut = class {
2074
2102
  in;
2075
2103
  broadcast;
2076
2104
  sync;
@@ -2105,7 +2133,7 @@ var ct = class {
2105
2133
  audioTarget: o(e.audioTarget),
2106
2134
  captions: o(e.captions)
2107
2135
  }), this.broadcast = new R(this.in), this.#r.cleanup(() => this.broadcast.close());
2108
- let t = new ct({
2136
+ let t = new lt({
2109
2137
  broadcast: this.broadcast,
2110
2138
  target: this.in.target,
2111
2139
  supported: Q.supported,
@@ -2139,7 +2167,7 @@ var ct = class {
2139
2167
  volume: this.in.volume,
2140
2168
  muted: this.in.muted,
2141
2169
  paused: this.in.paused
2142
- }), this.renderer = new rt({
2170
+ }), this.renderer = new it({
2143
2171
  decoder: this.video,
2144
2172
  canvas: this.in.canvas,
2145
2173
  visible: this.in.visible
@@ -2167,6 +2195,6 @@ var ct = class {
2167
2195
  }
2168
2196
  };
2169
2197
  //#endregion
2170
- export { Ae as _, J as a, G as c, W as d, V as f, N as g, Le as h, Q as i, H as l, R as m, ct as n, q as o, B as p, rt as r, K as s, lt as t, U as u, j as v };
2198
+ export { Ae as _, J as a, G as c, W as d, V as f, N as g, Le as h, Q as i, H as l, R as m, lt as n, q as o, B as p, it as r, K as s, ut as t, U as u, j as v };
2171
2199
 
2172
- //# sourceMappingURL=player-BFRzc5zw.js.map
2200
+ //# sourceMappingURL=player-C7Ih0ExN.js.map