@mango-iiif/av 0.1.0 → 0.2.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.
Files changed (44) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +1 -0
  3. package/dist/annotations.d.ts +2 -2
  4. package/dist/annotations.js +3 -3
  5. package/dist/chapters.d.ts +2 -2
  6. package/dist/chapters.js +3 -3
  7. package/dist/{chunk-HOISQL65.js → chunk-4NOWGMI5.js} +38 -6
  8. package/dist/chunk-4NOWGMI5.js.map +1 -0
  9. package/dist/{chunk-RJDPI4KL.js → chunk-4V6FME6B.js} +76 -20
  10. package/dist/chunk-4V6FME6B.js.map +1 -0
  11. package/dist/{chunk-QMUWPXP5.js → chunk-C4H46HDS.js} +4 -3
  12. package/dist/chunk-C4H46HDS.js.map +1 -0
  13. package/dist/{chunk-3PPOT5YI.js → chunk-CIAQZ2EB.js} +2 -2
  14. package/dist/{chunk-ARYOSUFI.js → chunk-EHIKFRFV.js} +3 -3
  15. package/dist/{chunk-ZRPA2VFR.js → chunk-ELGGWWDS.js} +48 -9
  16. package/dist/chunk-ELGGWWDS.js.map +1 -0
  17. package/dist/{chunk-DF3YH3BM.js → chunk-FVYEV2LO.js} +4 -4
  18. package/dist/{chunk-ZMC36FZ2.js → chunk-YHTJZXOE.js} +80 -21
  19. package/dist/chunk-YHTJZXOE.js.map +1 -0
  20. package/dist/{chunk-IMQ4IJY6.js → chunk-YX32ZQPR.js} +3 -3
  21. package/dist/{controller-BLA8XNsG.d.ts → controller-SkwGkVE8.d.ts} +8 -0
  22. package/dist/{controller-element-BY0157ve.d.ts → controller-element-CkXIA6w2.d.ts} +1 -1
  23. package/dist/core.d.ts +2 -2
  24. package/dist/core.js +2 -2
  25. package/dist/index.d.ts +2 -2
  26. package/dist/index.js +9 -9
  27. package/dist/media.d.ts +1 -1
  28. package/dist/media.js +4 -4
  29. package/dist/player.d.ts +2 -2
  30. package/dist/player.js +5 -5
  31. package/dist/transcript-element.d.ts +2 -2
  32. package/dist/transcript-element.js +4 -4
  33. package/docs/API.md +8 -0
  34. package/package.json +2 -1
  35. package/dist/chunk-HOISQL65.js.map +0 -1
  36. package/dist/chunk-QMUWPXP5.js.map +0 -1
  37. package/dist/chunk-RJDPI4KL.js.map +0 -1
  38. package/dist/chunk-ZMC36FZ2.js.map +0 -1
  39. package/dist/chunk-ZRPA2VFR.js.map +0 -1
  40. package/docs/FEATURE_PARITY.md +0 -43
  41. /package/dist/{chunk-3PPOT5YI.js.map → chunk-CIAQZ2EB.js.map} +0 -0
  42. /package/dist/{chunk-ARYOSUFI.js.map → chunk-EHIKFRFV.js.map} +0 -0
  43. /package/dist/{chunk-DF3YH3BM.js.map → chunk-FVYEV2LO.js.map} +0 -0
  44. /package/dist/{chunk-IMQ4IJY6.js.map → chunk-YX32ZQPR.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/mango-av-media.ts"],"sourcesContent":["import { AVPlayerController } from '../controller';\nimport { loadTranscript } from '../transcript';\nimport { ensureMediaChrome } from './media-chrome';\nimport { playFromInteraction } from './playback';\nimport { AudioArtVisualizer, audioArtContentHtml, audioArtTranscriptHtml, resolveAudioVisualizerMode, syncAudioArtTranscript } from './audio-art';\nimport type {\n AVAnnotation,\n AVCanvas,\n AVManifest,\n AVPlayerConfig,\n AVPlayerEventMap,\n AVStrings,\n Chapter,\n JsonObject,\n MetadataEntry,\n TranscriptCue,\n} from '../types';\nimport { escapeHtml, formatString, formatTime, resolveStrings } from '../utils';\nimport { applyMediaSource, resolveAudioMediaPolicy } from './media-policy';\n\nconst HTMLElementBase = (globalThis.HTMLElement ?? class {}) as typeof HTMLElement;\n\nlet hlsElementRegistration: Promise<unknown> | undefined;\nconst ensureHlsElement = (): Promise<unknown> => {\n hlsElementRegistration ??= import('hls-video-element');\n return hlsElementRegistration;\n};\n\nconst eventTypes: Array<keyof AVPlayerEventMap> = [\n 'av-loadstart',\n 'av-ready',\n 'av-error',\n 'av-canvaschange',\n 'av-sourcechange',\n 'av-play',\n 'av-pause',\n 'av-timeupdate',\n 'av-seek',\n 'av-ended',\n 'av-annotationschange',\n 'av-configchange',\n 'av-statechange',\n 'av-visualizerfallback',\n];\n\nconst styles = `\n :host {\n --mango-av-accent: #5b3fd6;\n --mango-av-accent-contrast: #fff;\n --mango-av-background: #fff;\n --mango-av-surface: #f5f5f8;\n --mango-av-text: #18181b;\n --mango-av-muted: #62626a;\n --mango-av-border: #d7d7de;\n --mango-av-radius: .65rem;\n --mango-av-font: ui-sans-serif, system-ui, sans-serif;\n --mango-av-panel-width: 24rem;\n --mango-av-player-aspect-ratio: 16 / 9;\n --media-primary-color: var(--mango-av-accent-contrast);\n --media-secondary-color: var(--mango-av-accent);\n color: var(--mango-av-text);\n display: block;\n font-family: var(--mango-av-font);\n }\n *, *::before, *::after { box-sizing: border-box; }\n button, input, select { font: inherit; }\n button, select { cursor: pointer; }\n .shell { background: var(--mango-av-background); border: 1px solid var(--mango-av-border); border-radius: var(--mango-av-radius); overflow: clip; }\n .header { padding: 1rem 1.1rem; }\n .header h2 { font-size: 1.15rem; margin: 0; }\n .header p { color: var(--mango-av-muted); margin: .35rem 0 0; }\n .layout { display: grid; grid-template-columns: minmax(0, 1fr) minmax(18rem, var(--mango-av-panel-width)); }\n .player-column { align-self: start; background: #08080a; min-width: 0; position: relative; }\n media-controller { display: block; width: 100%; }\n media-controller:not([audio]) { aspect-ratio: var(--mango-av-player-aspect-ratio); }\n video, hls-video { display: block; height: 100%; object-fit: contain; width: 100%; }\n audio { width: 100%; }\n media-control-bar { width: 100%; }\n .audio-art { --mango-av-wave-edge-strength: 0%; --mango-av-wave-mid-strength: 0%; --mango-av-wave-rise: 8%; --mango-av-wave-strength: 0%; align-items: center; aspect-ratio: 16 / 7; background: linear-gradient(#09080d, #111019); color: white; display: flex; justify-content: center; overflow: hidden; padding: 2rem; position: relative; text-align: center; }\n .audio-art.visualizer--pulse { background: radial-gradient(ellipse 140% 72% at 50% calc(100% + var(--mango-av-wave-rise)), color-mix(in srgb, var(--mango-av-accent) var(--mango-av-wave-strength), transparent) 0%, color-mix(in srgb, var(--mango-av-accent) var(--mango-av-wave-mid-strength), transparent) 40%, color-mix(in srgb, var(--mango-av-accent) var(--mango-av-wave-edge-strength), transparent) 62%, transparent 78%), linear-gradient(#09080d, #111019); }\n .audio-art-text { display: contents; }\n .audio-art-visualizer-canvas { height: 100%; inset: 0; pointer-events: none; position: absolute; width: 100%; }\n .audio-art img { height: 100%; inset: 0; object-fit: contain; position: absolute; width: 100%; }\n .audio-art-title { font-size: clamp(1.1rem, 3vw, 2rem); font-weight: 700; position: relative; z-index: 1; }\n .audio-art-transcript { height: 100%; inset: 0; overflow: hidden; position: absolute; z-index: 1; }\n .audio-art-cue { align-items: center; display: flex; flex-direction: column; font-size: clamp(.7rem, 1.3vw, .875rem); font-weight: 550; gap: .15rem; left: 12%; line-height: 1.35; position: absolute; right: 12%; top: 50%; transition: opacity 360ms ease, transform 480ms cubic-bezier(.22, 1, .36, 1); }\n .audio-art-transcript.seeking .audio-art-cue { transition: none; }\n .audio-art-cue.active { font-size: clamp(.78rem, 1.5vw, 1rem); font-weight: 650; }\n .audio-art-cue > span:last-child { display: -webkit-box; overflow: hidden; -webkit-box-orient: vertical; -webkit-line-clamp: 2; }\n .audio-art-speaker { font-size: .7em; letter-spacing: .04em; text-transform: uppercase; }\n .player-toolbar { align-items: center; background: #151518; color: #fff; display: flex; flex-wrap: wrap; gap: .5rem; justify-content: space-between; padding: .55rem .7rem; }\n .player-toolbar button, .player-toolbar select { background: #29292e; border: 1px solid #44444a; border-radius: .35rem; color: white; padding: .4rem .55rem; }\n .player-toolbar .group { align-items: center; display: flex; gap: .45rem; }\n .search-markers { background: #242429; height: .45rem; position: relative; }\n .search-marker { background: #ffe24a; border: 0; border-radius: 50%; height: .7rem; padding: 0; position: absolute; top: 50%; transform: translate(-50%, -50%); width: .7rem; }\n .resume { align-items: center; background: #fff8d7; color: #312900; display: flex; gap: .5rem; justify-content: center; padding: .6rem; }\n .resume button { background: var(--mango-av-accent); border: 0; border-radius: .35rem; color: var(--mango-av-accent-contrast); padding: .35rem .65rem; }\n .panel { border-left: 1px solid var(--mango-av-border); display: flex; flex-direction: column; min-height: 22rem; min-width: 0; }\n .tabs { background: var(--mango-av-surface); display: flex; gap: .1rem; overflow-x: auto; padding: .35rem; }\n .tab { background: transparent; border: 0; border-bottom: 3px solid transparent; color: var(--mango-av-muted); padding: .55rem .6rem; white-space: nowrap; }\n .tab[aria-selected=true] { border-color: var(--mango-av-accent); color: var(--mango-av-text); font-weight: 650; }\n .panel-content { flex: 1; max-height: 34rem; overflow: auto; padding: .85rem; }\n .empty { color: var(--mango-av-muted); font-style: italic; }\n .chapter-list, .annotation-list, .cue-list, .file-list { list-style: none; margin: 0; padding: 0; }\n .chapter-list .chapter-list { border-left: 1px solid var(--mango-av-border); margin-left: .8rem; padding-left: .55rem; }\n .chapter { margin-block: .3rem; }\n .chapter button, .annotation button, .cue button { background: transparent; border: 0; border-radius: .35rem; color: inherit; display: grid; gap: .15rem; padding: .5rem; text-align: left; width: 100%; }\n .chapter button { border-radius: 0; }\n .chapter button:hover, .annotation button:hover, .cue button:hover { background: var(--mango-av-surface); }\n .chapter button.active, .annotation button.active, .cue button.active { background: color-mix(in srgb, var(--mango-av-accent) 14%, transparent); box-shadow: inset 3px 0 var(--mango-av-accent); }\n .time { color: var(--mango-av-muted); font-size: .78rem; }\n .panel-tools { display: flex; flex-wrap: wrap; gap: .45rem; margin-bottom: .75rem; }\n .panel-tools input, .panel-tools select { border: 1px solid var(--mango-av-border); border-radius: .35rem; min-width: 0; padding: .45rem; }\n .panel-tools input { flex: 1; }\n .panel-tools a { align-content: center; color: var(--mango-av-accent); }\n .cue mark { background: #ffe770; }\n .speaker { font-weight: 700; }\n .tags { display: flex; flex-wrap: wrap; gap: .25rem; }\n .tag { background: var(--mango-av-surface); border-radius: 999px; font-size: .75rem; padding: .15rem .45rem; }\n dl { margin: 0; }\n dt { font-weight: 700; margin-top: .75rem; }\n dd { color: var(--mango-av-muted); margin: .2rem 0 0; }\n .file-list li { border-bottom: 1px solid var(--mango-av-border); padding: .55rem 0; }\n a { color: var(--mango-av-accent); }\n .error { background: #fff0f0; color: #8a1717; padding: 1rem; }\n .sr-only { clip: rect(0,0,0,0); clip-path: inset(50%); height: 1px; overflow: hidden; position: absolute; white-space: nowrap; width: 1px; }\n @media (max-width: 760px) {\n .layout { grid-template-columns: 1fr; }\n .panel { border-left: 0; border-top: 1px solid var(--mango-av-border); }\n }\n`;\n\nconst metadataHtml = (entries: MetadataEntry[], ui: AVStrings): string => {\n if (entries.length === 0) return `<p class=\"empty\">${escapeHtml(ui.noMetadata)}</p>`;\n return `<dl>${entries.map((entry) => `\n <dt>${escapeHtml(entry.label)}</dt>\n <dd>${entry.values.map((value) => escapeHtml(value.value)).join('<br>')}</dd>\n `).join('')}</dl>`;\n};\n\nconst chapterHtml = (chapters: Chapter[], canvasId: string, ui: AVStrings, level = 1): string => {\n if (chapters.length === 0 && level === 1) return `<p class=\"empty\">${escapeHtml(ui.noChapters)}</p>`;\n return `<ul class=\"chapter-list\">${chapters.map((chapter) => {\n const active = chapter.canvasId === canvasId;\n const target = chapter.canvasId ? `data-canvas=\"${escapeHtml(chapter.canvasId)}\"` : '';\n const time = chapter.time ? `data-time=\"${chapter.time.start}\"` : '';\n return `<li class=\"chapter\">\n <button type=\"button\" class=\"${active ? 'active' : ''}\" ${target} ${time} ${chapter.canvasId ? '' : 'disabled'}>\n <span>${escapeHtml(chapter.label)}</span>\n ${chapter.time ? `<span class=\"time\">${formatTime(chapter.time.start)}</span>` : ''}\n </button>\n ${chapterHtml(chapter.children, canvasId, ui, level + 1)}\n </li>`;\n }).join('')}</ul>`;\n};\n\nconst highlight = (text: string, query: string): string => {\n const escaped = escapeHtml(text);\n if (!query) return escaped;\n const safeQuery = query.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n return escaped.replace(new RegExp(`(${safeQuery})`, 'gi'), '<mark>$1</mark>');\n};\n\nexport class MangoAVMediaElement extends HTMLElementBase {\n static observedAttributes = ['manifest-url', 'config'];\n\n readonly player: AVPlayerController;\n #root: ShadowRoot;\n #inputManifest: JsonObject | string | undefined;\n #config: AVPlayerConfig = {};\n #activePanel: NonNullable<AVPlayerConfig['activePanel']> = 'chapters';\n #transcriptCues: TranscriptCue[] = [];\n #transcriptIndex = 0;\n #transcriptQuery = '';\n #resumeTime: number | undefined;\n #loadingToken = 0;\n #resolvedSourceUrl: string | undefined;\n #sourceToken = 0;\n #fallbackSourceUrl: string | undefined;\n #visualizer = new AudioArtVisualizer();\n\n constructor() {\n super();\n this.#root = this.attachShadow({ mode: 'open' });\n this.player = new AVPlayerController();\n for (const type of eventTypes) {\n this.player.addEventListener(type, ((event: CustomEvent) => {\n if (type === 'av-ready') {\n if (this.player.source?.isHls) void ensureHlsElement();\n this.#resetSourceLoad();\n this.#resumeTime = this.player.getSavedPosition();\n this.#activePanel = this.player.config.activePanel;\n this.#render();\n void this.#resolveSource();\n void this.#loadActiveTranscript();\n } else if (type === 'av-canvaschange' || type === 'av-sourcechange') {\n if (this.player.source?.isHls) void ensureHlsElement();\n this.#resetSourceLoad();\n this.#transcriptCues = [];\n this.#transcriptIndex = 0;\n this.#render();\n void this.#resolveSource();\n void this.#loadActiveTranscript();\n } else if (type === 'av-configchange') {\n this.#resetSourceLoad();\n this.#activePanel = this.player.config.activePanel;\n void ensureMediaChrome(this.player.config.preferredLanguages[0]).then(() => {\n this.#render();\n void this.#resolveSource();\n });\n } else if (type === 'av-error') {\n this.#render();\n } else if (type === 'av-annotationschange') {\n if (this.#activePanel === 'annotations') this.#renderPanelOnly();\n } else if (type === 'av-timeupdate') {\n this.#syncTimedContent();\n } else if (type === 'av-seek') {\n this.#syncTimedContent((event.detail as AVPlayerEventMap['av-seek']).to, true);\n }\n this.dispatchEvent(new CustomEvent(type, {\n detail: event.detail,\n bubbles: true,\n composed: true,\n }));\n }) as EventListener);\n }\n }\n\n get manifest(): JsonObject | string | undefined {\n return this.#inputManifest;\n }\n\n get controller(): AVPlayerController {\n return this.player;\n }\n\n set manifest(value: JsonObject | string | undefined) {\n this.#inputManifest = value;\n if (this.isConnected && value !== undefined) void this.load(value);\n }\n\n get config(): AVPlayerConfig {\n return this.#config;\n }\n\n set config(value: AVPlayerConfig) {\n this.#config = value;\n this.player.configure(value);\n this.#activePanel = value.activePanel ?? this.player.config.activePanel;\n if (this.isConnected) this.#render();\n }\n\n connectedCallback(): void {\n this.#readConfigAttribute();\n void ensureMediaChrome(this.player.config.preferredLanguages[0]);\n this.#render();\n const source = this.#inputManifest ?? this.getAttribute('manifest-url') ?? undefined;\n if (source !== undefined) void this.load(source);\n }\n\n disconnectedCallback(): void {\n this.#visualizer.disconnect();\n this.player.destroy();\n }\n\n attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void {\n if (!this.isConnected || oldValue === newValue) return;\n if (name === 'manifest-url' && newValue) {\n this.#inputManifest = newValue;\n void this.load(newValue);\n }\n if (name === 'config') {\n this.#readConfigAttribute();\n this.#render();\n }\n }\n\n async load(input: JsonObject | string): Promise<AVManifest> {\n this.#inputManifest = input;\n return await this.player.load(input);\n }\n\n play(): Promise<void> {\n return this.player.play();\n }\n\n pause(): void {\n this.player.pause();\n }\n\n seekTo(time: number): void {\n this.player.seekTo(time);\n }\n\n selectCanvas(indexOrId: number | string, options?: { time?: number; autoplay?: boolean }): AVCanvas | undefined {\n return this.player.selectCanvas(indexOrId, options);\n }\n\n #readConfigAttribute(): void {\n const attribute = this.getAttribute('config');\n if (!attribute) return;\n try {\n this.#config = JSON.parse(attribute) as AVPlayerConfig;\n this.player.configure(this.#config);\n } catch (cause) {\n const error = cause instanceof Error ? cause : new Error(String(cause));\n this.dispatchEvent(new CustomEvent('av-error', { detail: { error }, bubbles: true, composed: true }));\n }\n }\n\n #render(): void {\n this.#visualizer.disconnect();\n const state = this.player.state;\n const manifest = state.manifest;\n const canvas = this.player.canvas;\n const source = this.player.source;\n const ui = resolveStrings(this.player.config.strings);\n this.#root.innerHTML = `<style>${styles}</style><div class=\"shell\">\n <slot name=\"header\">\n ${manifest ? `<header class=\"header\"><h2>${escapeHtml(manifest.label)}</h2>${manifest.summary ? `<p>${escapeHtml(manifest.summary)}</p>` : ''}</header>` : ''}\n </slot>\n ${state.status === 'error' ? `<div class=\"error\" role=\"alert\">${escapeHtml(state.error?.message ?? ui.unableToLoadPlayer)}</div>` : ''}\n ${state.status === 'loading' ? `<p class=\"header\" role=\"status\">${escapeHtml(ui.loadingManifest)}</p>` : ''}\n ${manifest && canvas ? this.#renderReady(manifest, canvas, source?.kind ?? 'video') : ''}\n </div>`;\n this.#bindInteractions();\n const media = this.#root.querySelector('video, audio, hls-video') as HTMLMediaElement | null;\n if (media && this.#resolvedSourceUrl) this.#activateMedia(media, this.#resolvedSourceUrl);\n this.#syncTimedContent();\n }\n\n #renderReady(manifest: AVManifest, canvas: AVCanvas, kind: 'audio' | 'video'): string {\n const source = this.player.source;\n const panels = this.player.config.panels;\n const controls = this.player.config.controls;\n const ui = resolveStrings(this.player.config.strings);\n const panelTabs = [\n panels.chapters ? ['chapters', ui.chapters] : undefined,\n panels.transcript ? ['transcript', ui.transcript] : undefined,\n panels.annotations ? ['annotations', ui.annotations] : undefined,\n panels.metadata ? ['metadata', ui.metadata] : undefined,\n panels.supplementalFiles ? ['files', ui.files] : undefined,\n ].filter((value): value is [string, string] => Boolean(value));\n const showPanel = this.#activePanel !== 'none' && panelTabs.length > 0;\n const showSourceSelector = controls.sourceSelector && canvas.sources.length > 1;\n const showDownload = controls.download && canvas.rendering.length > 0;\n const showPlayerToolbar = controls.navigation || controls.autoAdvance || showSourceSelector || showDownload;\n const visualizerMode = resolveAudioVisualizerMode(this.player.config.audioArt.visualizer);\n return `\n ${this.#resumeTime !== undefined ? `<div class=\"resume\">${escapeHtml(formatString(ui.resumePrompt, { time: formatTime(this.#resumeTime) }))} <button type=\"button\" data-action=\"resume\">${escapeHtml(ui.resume)}</button><button type=\"button\" data-action=\"dismiss-resume\">${escapeHtml(ui.startOver)}</button></div>` : ''}\n <div class=\"layout\" ${showPanel ? '' : 'style=\"grid-template-columns: 1fr\"'}>\n <section class=\"player-column\" aria-label=\"${escapeHtml(ui.mediaPlayer)}\">\n <slot name=\"before-player\"></slot>\n <media-controller ${kind === 'audio' ? 'audio' : ''} ${this.#mediaChromeLanguage()}>\n ${kind === 'audio' ? `<div class=\"audio-art ${visualizerMode ? `visualizer visualizer--${visualizerMode}` : ''}\">${audioArtContentHtml(canvas.label, this.player.config.audioArt.title ?? '', this.#transcriptCues, Boolean(this.player.config.audioArt.transcript), visualizerMode)}</div>` : ''}\n ${source ? this.#mediaHtml(canvas, source.kind) : `<div class=\"audio-art\"><span>${escapeHtml(ui.noPlayableMedia)}</span></div>`}\n <slot name=\"player-overlay\"></slot>\n <slot name=\"controls\">\n <media-control-bar>\n <media-play-button></media-play-button>\n ${controls.seek ? `<media-seek-backward-button seekoffset=\"${controls.skipSeconds ?? 10}\"></media-seek-backward-button><media-seek-forward-button seekoffset=\"${controls.skipSeconds ?? 10}\"></media-seek-forward-button>` : ''}\n <media-time-display showduration></media-time-display>\n ${controls.seek ? '<media-time-range></media-time-range>' : ''}\n ${controls.volume ? '<media-mute-button></media-mute-button><media-volume-range></media-volume-range>' : ''}\n ${controls.captions && canvas.tracks.length > 0 ? '<media-captions-menu-button></media-captions-menu-button>' : ''}\n ${controls.playbackRate ? '<media-playback-rate-menu-button></media-playback-rate-menu-button>' : ''}\n ${controls.pictureInPicture && kind === 'video' ? '<media-pip-button></media-pip-button>' : ''}\n ${controls.fullscreen && kind === 'video' ? '<media-fullscreen-button></media-fullscreen-button>' : ''}\n </media-control-bar>\n ${controls.captions && canvas.tracks.length > 0 ? '<media-captions-menu hidden></media-captions-menu>' : ''}\n ${controls.playbackRate ? `<media-playback-rate-menu rates=\"${this.player.config.playbackRates.join(' ')}\" hidden></media-playback-rate-menu>` : ''}\n </slot>\n </media-controller>\n ${this.#searchMarkersHtml()}\n ${showPlayerToolbar ? `<div class=\"player-toolbar\">\n ${controls.navigation ? `<div class=\"group\">\n <button type=\"button\" data-action=\"previous\" ${canvas.index === 0 ? 'disabled' : ''} aria-label=\"${escapeHtml(ui.previousSection)}\">← ${escapeHtml(ui.previous)}</button>\n <button type=\"button\" data-action=\"next\" ${canvas.index === manifest.canvases.length - 1 && !this.player.state.repeat ? 'disabled' : ''} aria-label=\"${escapeHtml(ui.nextSection)}\">${escapeHtml(ui.next)} →</button>\n </div>` : ''}\n ${showSourceSelector || showDownload || controls.autoAdvance ? `<div class=\"group\">\n ${showSourceSelector ? `<label><span class=\"sr-only\">${escapeHtml(ui.mediaSource)}</span><select data-action=\"source\">${canvas.sources.map((item, index) => `<option value=\"${index}\" ${index === this.player.state.sourceIndex ? 'selected' : ''}>${escapeHtml(item.label ?? item.format ?? formatString(ui.sourceNumber, { number: index + 1 }))}</option>`).join('')}</select></label>` : ''}\n ${showDownload ? `<label><span class=\"sr-only\">${escapeHtml(ui.downloadMediaFile)}</span><select data-action=\"download\"><option value=\"\">${escapeHtml(ui.download)}…</option>${canvas.rendering.map((file) => `<option value=\"${escapeHtml(file.id)}\" data-filename=\"${escapeHtml(file.filename ?? '')}\">${escapeHtml(file.label)}</option>`).join('')}</select></label>` : ''}\n ${controls.autoAdvance ? `<label><input type=\"checkbox\" data-action=\"auto-advance\" ${this.player.state.autoAdvance ? 'checked' : ''}> ${escapeHtml(ui.autoAdvance)}</label>` : ''}\n </div>` : ''}\n </div>` : ''}\n <slot name=\"after-player\"></slot>\n </section>\n ${showPanel ? `<aside class=\"panel\">\n <nav class=\"tabs\" role=\"tablist\" aria-label=\"${escapeHtml(ui.mediaInformation)}\">${panelTabs.map(([id, tabLabel]) => `<button class=\"tab\" type=\"button\" role=\"tab\" data-panel=\"${id}\" aria-selected=\"${this.#activePanel === id}\">${escapeHtml(tabLabel)}</button>`).join('')}</nav>\n <div class=\"panel-content\" role=\"tabpanel\">${this.#panelHtml(manifest, canvas)}</div>\n </aside>` : ''}\n </div>`;\n }\n\n #mediaHtml(canvas: AVCanvas, kind: 'audio' | 'video'): string {\n const source = this.player.source;\n if (!source) return '';\n const tag = source.isHls ? 'hls-video' : kind;\n const attributes = [\n 'slot=\"media\"',\n `preload=\"${this.player.config.preload}\"`,\n this.player.config.crossorigin ? `crossorigin=\"${this.player.config.crossorigin}\"` : '',\n kind === 'video' && canvas.poster ? `poster=\"${escapeHtml(canvas.poster)}\"` : '',\n `aria-label=\"${escapeHtml(canvas.label)}\"`,\n `data-canvasindex=\"${canvas.index}\"`,\n ].filter(Boolean).join(' ');\n const tracks = canvas.tracks.map((track) => `<track src=\"${escapeHtml(track.src)}\" kind=\"${track.kind}\" label=\"${escapeHtml(track.label)}\" ${track.language ? `srclang=\"${escapeHtml(track.language)}\"` : ''} ${track.default ? 'default' : ''}>`).join('');\n return `<${tag} ${attributes}>${tracks}</${tag}>`;\n }\n\n #mediaChromeLanguage(): string {\n const language = this.player.config.preferredLanguages.find((value) => value !== 'none');\n return language ? `lang=\"${escapeHtml(language)}\"` : '';\n }\n\n #panelHtml(manifest: AVManifest, canvas: AVCanvas): string {\n const ui = resolveStrings(this.player.config.strings);\n switch (this.#activePanel) {\n case 'chapters':\n return chapterHtml(manifest.chapters, canvas.id, ui);\n case 'transcript':\n return this.#transcriptHtml(canvas);\n case 'annotations':\n return this.#annotationsHtml(canvas.annotations);\n case 'metadata':\n return `<h3>${escapeHtml(canvas.label)}</h3>${metadataHtml([...manifest.metadata, ...canvas.metadata], ui)}${manifest.requiredStatement ? metadataHtml([manifest.requiredStatement], ui) : ''}${manifest.rights ? `<p><strong>${escapeHtml(ui.rights)}:</strong> <a href=\"${escapeHtml(manifest.rights)}\" target=\"_blank\" rel=\"noreferrer\">${escapeHtml(manifest.rights)}</a></p>` : ''}`;\n case 'files': {\n const files = [...manifest.rendering, ...canvas.rendering];\n return files.length > 0 ? `<ul class=\"file-list\">${files.map((file) => `<li><a href=\"${escapeHtml(file.id)}\" download=\"${escapeHtml(file.filename ?? '')}\">${escapeHtml(file.label)}</a>${file.format ? `<div class=\"time\">${escapeHtml(file.format)}</div>` : ''}</li>`).join('')}</ul>` : `<p class=\"empty\">${escapeHtml(ui.noSupplementalFiles)}</p>`;\n }\n default:\n return '';\n }\n }\n\n #transcriptHtml(canvas: AVCanvas): string {\n const ui = resolveStrings(this.player.config.strings);\n if (canvas.transcripts.length === 0) return `<p class=\"empty\">${escapeHtml(ui.noTranscript)}</p>`;\n const resource = canvas.transcripts[this.#transcriptIndex];\n const query = this.#transcriptQuery.trim();\n const visible = query\n ? this.#transcriptCues.filter((cue) => cue.text.toLowerCase().includes(query.toLowerCase()))\n : this.#transcriptCues;\n return `<div class=\"panel-tools\">\n ${canvas.transcripts.length > 1 ? `<select data-action=\"transcript\" aria-label=\"${escapeHtml(ui.transcript)}\">${canvas.transcripts.map((item, index) => `<option value=\"${index}\" ${index === this.#transcriptIndex ? 'selected' : ''}>${escapeHtml(item.label)}</option>`).join('')}</select>` : ''}\n ${this.player.config.transcript.search ? `<input type=\"search\" data-action=\"transcript-search\" value=\"${escapeHtml(this.#transcriptQuery)}\" placeholder=\"${escapeHtml(ui.searchTranscript)}\" aria-label=\"${escapeHtml(ui.searchTranscript)}\">` : ''}\n ${this.player.config.transcript.download && resource?.src ? `<a href=\"${escapeHtml(resource.src)}\" download>${escapeHtml(ui.download)}</a>` : ''}\n </div>\n ${this.#transcriptCues.length === 0 ? `<p class=\"empty\">${escapeHtml(ui.loadingTranscript)}</p>` : `<ol class=\"cue-list\">${visible.map((cue) => `<li class=\"cue\" data-cue-id=\"${escapeHtml(cue.id)}\" ${cue.start !== undefined ? `data-start=\"${cue.start}\" data-end=\"${cue.end ?? cue.start}\"` : ''}><button type=\"button\" ${cue.start === undefined ? 'disabled' : ''}>${cue.start !== undefined ? `<span class=\"time\">${formatTime(cue.start)}</span>` : ''}${cue.speaker ? `<span class=\"speaker\">${escapeHtml(cue.speaker)}</span>` : ''}<span>${highlight(cue.text, query)}</span></button></li>`).join('')}</ol>`}`;\n }\n\n #annotationsHtml(annotations: AVAnnotation[]): string {\n const ui = resolveStrings(this.player.config.strings);\n if (annotations.length === 0) return `<p class=\"empty\">${escapeHtml(ui.noAnnotations)}</p>`;\n return `<ol class=\"annotation-list\">${annotations.map((annotation) => `<li class=\"annotation\" data-start=\"${annotation.time?.start ?? ''}\" data-end=\"${annotation.time?.end ?? annotation.time?.start ?? ''}\"><button type=\"button\" ${annotation.time ? '' : 'disabled'}>${annotation.time ? `<span class=\"time\">${formatTime(annotation.time.start)}</span>` : ''}<span>${escapeHtml(annotation.text)}</span>${annotation.tags.length > 0 ? `<span class=\"tags\">${annotation.tags.map((tag) => `<span class=\"tag\">${escapeHtml(tag)}</span>`).join('')}</span>` : ''}</button></li>`).join('')}</ol>`;\n }\n\n #searchMarkersHtml(): string {\n const ui = resolveStrings(this.player.config.strings);\n const query = this.#transcriptQuery.trim().toLowerCase();\n const duration = this.player.state.duration || this.player.canvas?.duration || 0;\n if (!query || !this.player.config.transcript.showSearchMarkers || duration <= 0) return '';\n const matches = this.#transcriptCues.filter((cue) => cue.start !== undefined && cue.text.toLowerCase().includes(query));\n if (matches.length === 0) return '';\n return `<div class=\"search-markers\" aria-label=\"${escapeHtml(ui.transcriptSearchMatches)}\">${matches.map((cue) => `<button type=\"button\" class=\"search-marker\" data-search-time=\"${cue.start}\" style=\"left:${Math.min(100, Math.max(0, ((cue.start ?? 0) / duration) * 100))}%\" aria-label=\"${escapeHtml(formatString(ui.searchMatchAt, { time: formatTime(cue.start ?? 0) }))}\"></button>`).join('')}</div>`;\n }\n\n #bindInteractions(): void {\n this.#root.querySelectorAll<HTMLElement>('[data-panel]').forEach((button) => button.addEventListener('click', () => {\n this.#activePanel = button.dataset.panel as NonNullable<AVPlayerConfig['activePanel']>;\n this.#renderPanelOnly();\n if (this.#activePanel === 'transcript') void this.#loadActiveTranscript();\n }));\n this.#root.querySelector<HTMLElement>('[data-action=previous]')?.addEventListener('click', () => this.player.previous({ autoplay: this.player.state.status === 'playing' }));\n this.#root.querySelector<HTMLElement>('[data-action=next]')?.addEventListener('click', () => this.player.next({ autoplay: this.player.state.status === 'playing' }));\n this.#root.querySelector<HTMLSelectElement>('[data-action=source]')?.addEventListener('change', (event) => this.player.selectSource(Number((event.target as HTMLSelectElement).value)));\n this.#root.querySelector<HTMLSelectElement>('[data-action=download]')?.addEventListener('change', (event) => {\n const select = event.target as HTMLSelectElement;\n if (!select.value) return;\n const anchor = document.createElement('a');\n anchor.href = select.value;\n anchor.download = select.selectedOptions[0]?.dataset.filename ?? '';\n anchor.click();\n select.value = '';\n });\n this.#root.querySelector<HTMLInputElement>('[data-action=auto-advance]')?.addEventListener('change', (event) => this.player.setAutoAdvance((event.target as HTMLInputElement).checked));\n this.#root.querySelector<HTMLElement>('[data-action=resume]')?.addEventListener('click', () => {\n if (this.#resumeTime !== undefined) this.player.seekTo(this.#resumeTime);\n this.#resumeTime = undefined;\n this.#render();\n });\n this.#root.querySelector<HTMLElement>('[data-action=dismiss-resume]')?.addEventListener('click', () => {\n this.#resumeTime = undefined;\n this.#render();\n });\n this.#bindPanelInteractions();\n }\n\n #bindPanelInteractions(): void {\n this.#root.querySelectorAll<HTMLElement>('[data-canvas]').forEach((button) => button.addEventListener('click', () => {\n const canvasId = button.dataset.canvas;\n if (canvasId) this.player.selectCanvas(canvasId, { time: Number(button.dataset.time ?? 0), autoplay: true });\n }));\n this.#root.querySelector<HTMLSelectElement>('[data-action=transcript]')?.addEventListener('change', (event) => {\n this.#transcriptIndex = Number((event.target as HTMLSelectElement).value);\n this.#transcriptCues = [];\n this.#renderPanelOnly();\n void this.#loadActiveTranscript();\n });\n this.#root.querySelector<HTMLInputElement>('[data-action=transcript-search]')?.addEventListener('input', (event) => {\n this.#transcriptQuery = (event.target as HTMLInputElement).value;\n const selection = (event.target as HTMLInputElement).selectionStart;\n this.#renderPanelOnly();\n const input = this.#root.querySelector<HTMLInputElement>('[data-action=transcript-search]');\n input?.focus();\n if (selection !== null) input?.setSelectionRange(selection, selection);\n });\n this.#root.querySelectorAll<HTMLElement>('.cue[data-start] button, .annotation[data-start] button').forEach((button) => button.addEventListener('click', () => {\n const row = button.parentElement;\n const start = Number(row?.dataset.start);\n if (Number.isFinite(start)) {\n this.player.seekTo(start);\n playFromInteraction(this.player, this);\n }\n }));\n this.#root.querySelectorAll<HTMLElement>('[data-search-time]').forEach((button) => button.addEventListener('click', () => {\n const time = Number(button.dataset.searchTime);\n if (Number.isFinite(time)) this.player.seekTo(time);\n }));\n }\n\n #renderPanelOnly(): void {\n const manifest = this.player.manifest;\n const canvas = this.player.canvas;\n if (!manifest || !canvas) return;\n this.#root.querySelectorAll<HTMLElement>('[data-panel]').forEach((tab) => {\n tab.setAttribute('aria-selected', String(tab.dataset.panel === this.#activePanel));\n });\n const content = this.#root.querySelector<HTMLElement>('.panel-content');\n if (content) content.innerHTML = this.#panelHtml(manifest, canvas);\n this.#root.querySelector('.search-markers')?.remove();\n const toolbar = this.#root.querySelector('.player-toolbar');\n const markers = this.#searchMarkersHtml();\n if (toolbar && markers) toolbar.insertAdjacentHTML('beforebegin', markers);\n this.#bindPanelInteractions();\n }\n\n async #loadActiveTranscript(): Promise<void> {\n const resource = this.player.canvas?.transcripts[this.#transcriptIndex];\n if (!resource) return;\n const token = ++this.#loadingToken;\n try {\n const transcriptOptions: Parameters<typeof loadTranscript>[1] = {};\n if (this.player.config.fetch) transcriptOptions.fetch = this.player.config.fetch;\n if (this.player.config.requestInit) transcriptOptions.requestInit = this.player.config.requestInit;\n if (this.player.config.transcript.showNotes !== undefined) transcriptOptions.showNotes = this.player.config.transcript.showNotes;\n if (this.player.config.transcript.showMetadata !== undefined) transcriptOptions.showMetadata = this.player.config.transcript.showMetadata;\n const cues = await loadTranscript(resource, transcriptOptions);\n if (token !== this.#loadingToken) return;\n this.#transcriptCues = cues;\n this.#renderAudioArt();\n if (this.#activePanel === 'transcript') this.#renderPanelOnly();\n } catch (cause) {\n if (token !== this.#loadingToken) return;\n const error = cause instanceof Error ? cause : new Error(String(cause));\n this.dispatchEvent(new CustomEvent('av-error', { detail: { error }, bubbles: true, composed: true }));\n this.#transcriptCues = [];\n if (this.#activePanel === 'transcript') this.#renderPanelOnly();\n }\n }\n\n async #resolveSource(): Promise<void> {\n const token = ++this.#sourceToken;\n try {\n const sourceUrl = await this.player.resolvedSourceUrl();\n if (token !== this.#sourceToken || !sourceUrl) return;\n this.#resolvedSourceUrl = sourceUrl;\n const media = this.#root.querySelector('video, audio, hls-video') as HTMLMediaElement | null;\n if (media) this.#activateMedia(media, sourceUrl);\n } catch (cause) {\n const error = cause instanceof Error ? cause : new Error(String(cause));\n this.dispatchEvent(new CustomEvent('av-error', { detail: { error }, bubbles: true, composed: true }));\n }\n }\n\n #activateMedia(media: HTMLMediaElement, sourceUrl: string): void {\n const mode = resolveAudioVisualizerMode(this.player.config.audioArt.visualizer);\n const policy = resolveAudioMediaPolicy(\n sourceUrl,\n this.player.config.crossorigin,\n this.player.crossoriginExplicit,\n media instanceof HTMLAudioElement ? mode : undefined,\n this.#fallbackSourceUrl === sourceUrl,\n );\n if (media instanceof HTMLAudioElement && policy.implicitCors) {\n media.addEventListener('error', (event) => {\n if (this.#fallbackSourceUrl === sourceUrl) return;\n event.stopImmediatePropagation();\n this.#fallbackToOrdinaryAudio(media, sourceUrl);\n }, { once: true });\n }\n if (media instanceof HTMLAudioElement && mode && policy.visualize) {\n const connect = () => {\n const art = this.#root.querySelector<HTMLElement>('.audio-art.visualizer');\n if (art && media.isConnected) this.#visualizer.connect(media, art, mode);\n };\n media.addEventListener('loadedmetadata', connect, { once: true });\n if (media.readyState >= HTMLMediaElement.HAVE_METADATA) queueMicrotask(connect);\n }\n this.player.attachMedia(media);\n applyMediaSource(media, sourceUrl, policy.crossOrigin);\n }\n\n #fallbackToOrdinaryAudio(failed: HTMLAudioElement, sourceUrl: string): void {\n this.#fallbackSourceUrl = sourceUrl;\n this.#visualizer.disconnect();\n const requestedTime = this.player.state.currentTime;\n const shouldPlay = this.player.state.status === 'playing' || !failed.paused;\n const replacement = failed.cloneNode(true) as HTMLAudioElement;\n replacement.removeAttribute('src');\n replacement.removeAttribute('crossorigin');\n replacement.volume = failed.volume;\n replacement.muted = failed.muted;\n replacement.playbackRate = failed.playbackRate;\n const hadFocus = this.#root.activeElement === failed;\n failed.replaceWith(replacement);\n this.player.attachMedia(replacement);\n this.player.seekTo(requestedTime);\n if (hadFocus) replacement.focus();\n this.#root.querySelector<HTMLElement>('.audio-art')?.classList.remove('visualizer');\n applyMediaSource(replacement, sourceUrl);\n if (shouldPlay) replacement.addEventListener('loadedmetadata', () => void replacement.play().catch(() => undefined), { once: true });\n this.player.dispatchEvent(new CustomEvent('av-visualizerfallback', {\n detail: { canvasId: this.player.canvas?.id ?? '', sourceUrl, reason: 'cors' },\n }));\n }\n\n #resetSourceLoad(): void {\n this.#sourceToken += 1;\n this.#resolvedSourceUrl = undefined;\n this.#fallbackSourceUrl = undefined;\n this.#visualizer.disconnect();\n }\n\n #syncTimedContent(time = this.player.state.currentTime, instant = false): void {\n syncAudioArtTranscript(this.#root, time, instant);\n const rows = this.#root.querySelectorAll<HTMLElement>('[data-start][data-end]');\n let active: HTMLElement | undefined;\n rows.forEach((row) => {\n const start = Number(row.dataset.start);\n const end = Number(row.dataset.end);\n const isActive = Number.isFinite(start) && time >= start && (!Number.isFinite(end) || time < end);\n row.querySelector('button')?.classList.toggle('active', isActive);\n if (isActive) active = row;\n });\n if (active && this.player.config.transcript.autoScroll) {\n active.scrollIntoView({ block: 'nearest', behavior: 'smooth' });\n }\n }\n\n #renderAudioArt(): void {\n const art = this.#root.querySelector<HTMLElement>('.audio-art');\n const canvas = this.player.canvas;\n if (!art || !canvas || this.player.source?.kind !== 'audio') return;\n const text = art.querySelector<HTMLElement>('.audio-art-text');\n if (text) text.innerHTML = audioArtTranscriptHtml(canvas.label, this.player.config.audioArt.title ?? '', this.#transcriptCues, Boolean(this.player.config.audioArt.transcript));\n syncAudioArtTranscript(art, this.player.state.currentTime);\n }\n}\n\nexport const defineMangoAVMedia = (tagName = 'mango-av-media'): typeof MangoAVMediaElement => {\n if (typeof customElements === 'undefined') return MangoAVMediaElement;\n if (!customElements.get(tagName)) customElements.define(tagName, MangoAVMediaElement);\n return MangoAVMediaElement;\n};\n"],"mappings":"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","names":[]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  AVPlayerController
3
- } from "./chunk-HOISQL65.js";
3
+ } from "./chunk-4NOWGMI5.js";
4
4
 
5
5
  // src/components/controller-element.ts
6
6
  var HTMLElementBase = globalThis.HTMLElement ?? class {
@@ -18,7 +18,8 @@ var controllerEventTypes = [
18
18
  "av-ended",
19
19
  "av-annotationschange",
20
20
  "av-configchange",
21
- "av-statechange"
21
+ "av-statechange",
22
+ "av-visualizerfallback"
22
23
  ];
23
24
  var AVControllerElement = class extends HTMLElementBase {
24
25
  #controller = new AVPlayerController();
@@ -91,4 +92,4 @@ export {
91
92
  AVControllerElement,
92
93
  componentTheme
93
94
  };
94
- //# sourceMappingURL=chunk-QMUWPXP5.js.map
95
+ //# sourceMappingURL=chunk-C4H46HDS.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/controller-element.ts","../src/components/component-styles.ts"],"sourcesContent":["import { AVPlayerController } from '../controller';\nimport type { AVPlayerEventMap } from '../types';\n\nexport const HTMLElementBase = (globalThis.HTMLElement ?? class {}) as typeof HTMLElement;\n\nexport const controllerEventTypes: Array<keyof AVPlayerEventMap> = [\n 'av-loadstart',\n 'av-ready',\n 'av-error',\n 'av-canvaschange',\n 'av-sourcechange',\n 'av-play',\n 'av-pause',\n 'av-timeupdate',\n 'av-seek',\n 'av-ended',\n 'av-annotationschange',\n 'av-configchange',\n 'av-statechange',\n 'av-visualizerfallback',\n];\n\nexport abstract class AVControllerElement extends HTMLElementBase {\n #controller = new AVPlayerController();\n #connected = false;\n #listeners = new Map<keyof AVPlayerEventMap, EventListener>();\n\n get controller(): AVPlayerController {\n return this.#controller;\n }\n\n set controller(value: AVPlayerController) {\n if (value === this.#controller) return;\n this.#detachController();\n this.#controller = value;\n if (this.#connected) this.#attachController();\n this.controllerChanged();\n }\n\n connectedCallback(): void {\n this.#connected = true;\n this.#attachController();\n this.controllerChanged();\n }\n\n disconnectedCallback(): void {\n this.#connected = false;\n this.#detachController();\n }\n\n protected get controllerEvents(): Array<keyof AVPlayerEventMap> {\n return controllerEventTypes;\n }\n\n protected abstract controllerChanged(): void;\n\n protected abstract handleControllerEvent<K extends keyof AVPlayerEventMap>(\n type: K,\n event: CustomEvent<AVPlayerEventMap[K]>,\n ): void;\n\n #attachController(): void {\n if (this.#listeners.size > 0) return;\n for (const type of this.controllerEvents) {\n const listener = ((event: CustomEvent) => {\n this.handleControllerEvent(type, event as CustomEvent<AVPlayerEventMap[typeof type]>);\n }) as EventListener;\n this.#controller.addEventListener(type, listener);\n this.#listeners.set(type, listener);\n }\n }\n\n #detachController(): void {\n for (const [type, listener] of this.#listeners) {\n this.#controller.removeEventListener(type, listener);\n }\n this.#listeners.clear();\n }\n}\n","export const componentTheme = `\n :host {\n --mango-av-accent: #5b3fd6;\n --mango-av-accent-contrast: #fff;\n --mango-av-background: #fff;\n --mango-av-surface: #f5f5f8;\n --mango-av-text: #18181b;\n --mango-av-muted: #62626a;\n --mango-av-border: #d7d7de;\n --mango-av-radius: .65rem;\n --mango-av-font: ui-sans-serif, system-ui, sans-serif;\n color: var(--mango-av-text);\n display: block;\n font-family: var(--mango-av-font);\n }\n *, *::before, *::after { box-sizing: border-box; }\n button, input, select { font: inherit; }\n button, select { cursor: pointer; }\n .empty { color: var(--mango-av-muted); font-style: italic; }\n .time { color: var(--mango-av-muted); font-size: .78rem; }\n`;\n"],"mappings":";;;;;AAGO,IAAM,kBAAmB,WAAW,eAAe,MAAM;AAAC;AAE1D,IAAM,uBAAsD;AAAA,EACjE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEO,IAAe,sBAAf,cAA2C,gBAAgB;AAAA,EAChE,cAAc,IAAI,mBAAmB;AAAA,EACrC,aAAa;AAAA,EACb,aAAa,oBAAI,IAA2C;AAAA,EAE5D,IAAI,aAAiC;AACnC,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,WAAW,OAA2B;AACxC,QAAI,UAAU,KAAK,YAAa;AAChC,SAAK,kBAAkB;AACvB,SAAK,cAAc;AACnB,QAAI,KAAK,WAAY,MAAK,kBAAkB;AAC5C,SAAK,kBAAkB;AAAA,EACzB;AAAA,EAEA,oBAA0B;AACxB,SAAK,aAAa;AAClB,SAAK,kBAAkB;AACvB,SAAK,kBAAkB;AAAA,EACzB;AAAA,EAEA,uBAA6B;AAC3B,SAAK,aAAa;AAClB,SAAK,kBAAkB;AAAA,EACzB;AAAA,EAEA,IAAc,mBAAkD;AAC9D,WAAO;AAAA,EACT;AAAA,EASA,oBAA0B;AACxB,QAAI,KAAK,WAAW,OAAO,EAAG;AAC9B,eAAW,QAAQ,KAAK,kBAAkB;AACxC,YAAM,YAAY,CAAC,UAAuB;AACxC,aAAK,sBAAsB,MAAM,KAAmD;AAAA,MACtF;AACA,WAAK,YAAY,iBAAiB,MAAM,QAAQ;AAChD,WAAK,WAAW,IAAI,MAAM,QAAQ;AAAA,IACpC;AAAA,EACF;AAAA,EAEA,oBAA0B;AACxB,eAAW,CAAC,MAAM,QAAQ,KAAK,KAAK,YAAY;AAC9C,WAAK,YAAY,oBAAoB,MAAM,QAAQ;AAAA,IACrD;AACA,SAAK,WAAW,MAAM;AAAA,EACxB;AACF;;;AC9EO,IAAM,iBAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;","names":[]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  parseTime
3
- } from "./chunk-HOISQL65.js";
3
+ } from "./chunk-4NOWGMI5.js";
4
4
 
5
5
  // src/transcript.ts
6
6
  var stripMarkup = (value) => value.replace(/<br\s*\/?>/gi, "\n").replace(/<[^>]+>/g, "").replace(/&nbsp;/g, " ").replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').trim();
@@ -123,4 +123,4 @@ export {
123
123
  parseTranscript,
124
124
  loadTranscript
125
125
  };
126
- //# sourceMappingURL=chunk-3PPOT5YI.js.map
126
+ //# sourceMappingURL=chunk-CIAQZ2EB.js.map
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  AVControllerElement,
3
3
  componentTheme
4
- } from "./chunk-QMUWPXP5.js";
4
+ } from "./chunk-C4H46HDS.js";
5
5
  import {
6
6
  escapeHtml,
7
7
  formatTime,
8
8
  resolveStrings
9
- } from "./chunk-HOISQL65.js";
9
+ } from "./chunk-4NOWGMI5.js";
10
10
 
11
11
  // src/components/mango-av-chapters.ts
12
12
  var styles = `${componentTheme}
@@ -72,4 +72,4 @@ export {
72
72
  MangoAVChaptersElement,
73
73
  defineMangoAVChapters
74
74
  };
75
- //# sourceMappingURL=chunk-ARYOSUFI.js.map
75
+ //# sourceMappingURL=chunk-EHIKFRFV.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  escapeHtml
3
- } from "./chunk-HOISQL65.js";
3
+ } from "./chunk-4NOWGMI5.js";
4
4
 
5
5
  // src/components/media-chrome.ts
6
6
  var registration;
@@ -37,14 +37,14 @@ var ensureMediaChrome = async (language) => {
37
37
  };
38
38
 
39
39
  // src/components/audio-art.ts
40
- var audioArtTranscriptHtml = (label, cues, enabled) => {
41
- if (!enabled || cues.length === 0) return `<span class="audio-art-title">${escapeHtml(label)}</span>`;
40
+ var audioArtTranscriptHtml = (label, title, cues, enabled) => {
41
+ if (!enabled || cues.length === 0) return title ? `<span class="audio-art-title">${escapeHtml(title)}</span>` : "";
42
42
  return `<div class="audio-art-transcript" aria-live="polite" aria-label="${escapeHtml(label)}">
43
43
  ${cues.map((cue) => `<div class="audio-art-cue" data-audio-cue data-start="${cue.start ?? ""}" data-end="${cue.end ?? cue.start ?? ""}">${cue.speaker ? `<span class="audio-art-speaker">${escapeHtml(cue.speaker)}</span>` : ""}<span>${escapeHtml(cue.text)}</span></div>`).join("")}
44
44
  </div>`;
45
45
  };
46
46
  var resolveAudioVisualizerMode = (value) => value === true ? "pulse" : value || void 0;
47
- var audioArtContentHtml = (label, cues, transcriptEnabled, visualizerMode) => `<div class="audio-art-text">${audioArtTranscriptHtml(label, cues, transcriptEnabled)}</div>${visualizerMode === "waveform" || visualizerMode === "spectrum" ? `<canvas class="audio-art-visualizer-canvas" aria-hidden="true"></canvas>` : ""}`;
47
+ var audioArtContentHtml = (label, title, cues, transcriptEnabled, visualizerMode) => `<div class="audio-art-text">${audioArtTranscriptHtml(label, title, cues, transcriptEnabled)}</div>${visualizerMode === "waveform" || visualizerMode === "spectrum" ? `<canvas class="audio-art-visualizer-canvas" aria-hidden="true"></canvas>` : ""}`;
48
48
  var syncAudioArtTranscript = (root, time, instant = false) => {
49
49
  const cues = [...root.querySelectorAll("[data-audio-cue]")];
50
50
  if (cues.length === 0) return;
@@ -85,6 +85,8 @@ var AudioArtVisualizer = class {
85
85
  #analyzer;
86
86
  #frame = 0;
87
87
  #level = 0;
88
+ #media;
89
+ #resume;
88
90
  connect(media, art, mode) {
89
91
  this.disconnect();
90
92
  const token = ++this.#token;
@@ -92,15 +94,16 @@ var AudioArtVisualizer = class {
92
94
  try {
93
95
  if (token !== this.#token || !media.isConnected || !art.isConnected) return;
94
96
  const context = new AudioContext();
97
+ this.#context = context;
95
98
  const source = context.createMediaElementSource(media);
99
+ this.#source = source;
96
100
  const analyzer = context.createAnalyser();
101
+ this.#analyzer = analyzer;
97
102
  analyzer.fftSize = 512;
98
103
  analyzer.smoothingTimeConstant = 0.95;
99
104
  source.connect(context.destination);
100
105
  source.connect(analyzer);
101
- this.#context = context;
102
- this.#source = source;
103
- this.#analyzer = analyzer;
106
+ this.#media = media;
104
107
  const samples = new Uint8Array(analyzer.fftSize);
105
108
  const frequencies = new Uint8Array(analyzer.frequencyBinCount);
106
109
  const canvas = art.querySelector(".audio-art-visualizer-canvas");
@@ -185,6 +188,7 @@ var AudioArtVisualizer = class {
185
188
  };
186
189
  draw();
187
190
  const resume = () => void context.resume();
191
+ this.#resume = resume;
188
192
  media.addEventListener("play", resume, { once: true });
189
193
  if (!media.paused) resume();
190
194
  } catch {
@@ -196,23 +200,58 @@ var AudioArtVisualizer = class {
196
200
  if (typeof cancelAnimationFrame !== "undefined") cancelAnimationFrame(this.#frame);
197
201
  this.#frame = 0;
198
202
  this.#level = 0;
203
+ if (this.#media && this.#resume) this.#media.removeEventListener("play", this.#resume);
199
204
  try {
200
205
  this.#source?.disconnect();
201
206
  } catch {
202
207
  }
208
+ try {
209
+ this.#analyzer?.disconnect();
210
+ } catch {
211
+ }
203
212
  if (this.#context && this.#context.state !== "closed") void this.#context.close();
204
213
  this.#analyzer = void 0;
205
214
  this.#source = void 0;
206
215
  this.#context = void 0;
216
+ this.#media = void 0;
217
+ this.#resume = void 0;
207
218
  }
208
219
  };
209
220
 
221
+ // src/components/media-policy.ts
222
+ var isCrossOriginHttp = (sourceUrl) => {
223
+ try {
224
+ const url = new URL(sourceUrl, document.baseURI);
225
+ return (url.protocol === "http:" || url.protocol === "https:") && url.origin !== location.origin;
226
+ } catch {
227
+ return false;
228
+ }
229
+ };
230
+ var resolveAudioMediaPolicy = (sourceUrl, configuredCrossOrigin, crossOriginExplicit, visualizerMode, fallback) => {
231
+ const crossOrigin = isCrossOriginHttp(sourceUrl);
232
+ const implicitCors = Boolean(visualizerMode) && crossOrigin && !crossOriginExplicit && !fallback;
233
+ const selectedCrossOrigin = implicitCors ? "anonymous" : configuredCrossOrigin || void 0;
234
+ const visualize = Boolean(visualizerMode) && !fallback && !(crossOrigin && !selectedCrossOrigin);
235
+ return {
236
+ ...selectedCrossOrigin ? { crossOrigin: selectedCrossOrigin } : {},
237
+ implicitCors,
238
+ visualize
239
+ };
240
+ };
241
+ var applyMediaSource = (media, sourceUrl, crossOrigin) => {
242
+ if (crossOrigin) media.crossOrigin = crossOrigin;
243
+ else media.removeAttribute("crossorigin");
244
+ media.src = sourceUrl;
245
+ };
246
+
210
247
  export {
211
248
  ensureMediaChrome,
212
249
  audioArtTranscriptHtml,
213
250
  resolveAudioVisualizerMode,
214
251
  audioArtContentHtml,
215
252
  syncAudioArtTranscript,
216
- AudioArtVisualizer
253
+ AudioArtVisualizer,
254
+ resolveAudioMediaPolicy,
255
+ applyMediaSource
217
256
  };
218
- //# sourceMappingURL=chunk-ZRPA2VFR.js.map
257
+ //# sourceMappingURL=chunk-ELGGWWDS.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/media-chrome.ts","../src/components/audio-art.ts","../src/components/media-policy.ts"],"sourcesContent":["let registration: Promise<unknown> | undefined;\nconst languageRegistrations = new Map<string, Promise<unknown>>();\n\nconst loadLanguage = (language: string): Promise<unknown> | undefined => {\n const normalized = language.toLowerCase();\n const code = normalized === 'zh-cn' || normalized === 'zh-tw'\n ? normalized\n : normalized.split('-')[0] ?? normalized;\n if (languageRegistrations.has(code)) return languageRegistrations.get(code);\n const pending = (() => {\n switch (code) {\n case 'de': return import('media-chrome/lang/de');\n case 'es': return import('media-chrome/lang/es');\n case 'fr': return import('media-chrome/lang/fr');\n case 'pt': return import('media-chrome/lang/pt');\n case 'zh-cn': return import('media-chrome/lang/zh-CN');\n case 'zh-tw': return import('media-chrome/lang/zh-TW');\n default: return undefined;\n }\n })();\n if (pending) languageRegistrations.set(code, pending);\n return pending;\n};\n\nexport const ensureMediaChrome = async (language?: string): Promise<void> => {\n if (language) await loadLanguage(language);\n registration ??= import('media-chrome');\n await registration;\n};\n","import type { AVAudioVisualizerMode, TranscriptCue } from '../types';\nimport { escapeHtml } from '../utils';\n\nexport const audioArtTranscriptHtml = (label: string, title: string, cues: TranscriptCue[], enabled: boolean): string => {\n if (!enabled || cues.length === 0) return title ? `<span class=\"audio-art-title\">${escapeHtml(title)}</span>` : '';\n return `<div class=\"audio-art-transcript\" aria-live=\"polite\" aria-label=\"${escapeHtml(label)}\">\n ${cues.map((cue) => `<div class=\"audio-art-cue\" data-audio-cue data-start=\"${cue.start ?? ''}\" data-end=\"${cue.end ?? cue.start ?? ''}\">${cue.speaker ? `<span class=\"audio-art-speaker\">${escapeHtml(cue.speaker)}</span>` : ''}<span>${escapeHtml(cue.text)}</span></div>`).join('')}\n </div>`;\n};\n\nexport const resolveAudioVisualizerMode = (\n value: boolean | AVAudioVisualizerMode | undefined,\n): AVAudioVisualizerMode | undefined => value === true ? 'pulse' : value || undefined;\n\nexport const audioArtContentHtml = (\n label: string,\n title: string,\n cues: TranscriptCue[],\n transcriptEnabled: boolean,\n visualizerMode?: AVAudioVisualizerMode,\n): string => `<div class=\"audio-art-text\">${audioArtTranscriptHtml(label, title, cues, transcriptEnabled)}</div>${visualizerMode === 'waveform' || visualizerMode === 'spectrum' ? `<canvas class=\"audio-art-visualizer-canvas\" aria-hidden=\"true\"></canvas>` : ''}`;\n\nexport const syncAudioArtTranscript = (root: ParentNode, time: number, instant = false): void => {\n const cues = [...root.querySelectorAll<HTMLElement>('[data-audio-cue]')];\n if (cues.length === 0) return;\n const transcript = root.querySelector<HTMLElement>('.audio-art-transcript');\n if (instant && transcript) {\n transcript.classList.add('seeking');\n requestAnimationFrame(() => transcript.classList.remove('seeking'));\n }\n\n let activeIndex = cues.findIndex((cue) => {\n const start = Number(cue.dataset.start);\n const end = Number(cue.dataset.end);\n return Number.isFinite(start) && time >= start && (!Number.isFinite(end) || time < end);\n });\n if (activeIndex < 0) {\n for (let index = cues.length - 1; index >= 0; index -= 1) {\n const cue = cues[index];\n if (cue && Number.isFinite(Number(cue.dataset.start)) && time >= Number(cue.dataset.start)) {\n activeIndex = index;\n break;\n }\n }\n }\n if (activeIndex < 0) activeIndex = 0;\n\n cues.forEach((cue, index) => {\n const offset = index - activeIndex;\n cue.classList.toggle('active', offset === 0);\n cue.classList.toggle('adjacent', Math.abs(offset) === 1);\n cue.toggleAttribute('aria-hidden', offset !== 0);\n cue.style.opacity = offset === 0 ? '1' : Math.abs(offset) === 1 ? '.2' : '0';\n cue.style.transform = `translateY(calc(-50% + ${offset * 4.25}rem))`;\n cue.style.visibility = Math.abs(offset) <= 1 ? 'visible' : 'hidden';\n });\n};\n\nexport class AudioArtVisualizer {\n #token = 0;\n #context: AudioContext | undefined;\n #source: MediaElementAudioSourceNode | undefined;\n #analyzer: AnalyserNode | undefined;\n #frame = 0;\n #level = 0;\n #media: HTMLMediaElement | undefined;\n #resume: (() => void) | undefined;\n\n connect(media: HTMLMediaElement, art: HTMLElement, mode: AVAudioVisualizerMode): void {\n this.disconnect();\n const token = ++this.#token;\n if (typeof AudioContext === 'undefined') return;\n\n try {\n if (token !== this.#token || !media.isConnected || !art.isConnected) return;\n const context = new AudioContext();\n this.#context = context;\n const source = context.createMediaElementSource(media);\n this.#source = source;\n const analyzer = context.createAnalyser();\n this.#analyzer = analyzer;\n analyzer.fftSize = 512;\n analyzer.smoothingTimeConstant = .95;\n source.connect(context.destination);\n source.connect(analyzer);\n this.#media = media;\n const samples = new Uint8Array(analyzer.fftSize);\n const frequencies = new Uint8Array(analyzer.frequencyBinCount);\n const canvas = art.querySelector<HTMLCanvasElement>('.audio-art-visualizer-canvas');\n const canvasContext = canvas?.getContext('2d');\n const reducedMotion = typeof matchMedia !== 'undefined' && matchMedia('(prefers-reduced-motion: reduce)').matches;\n const resizeCanvas = () => {\n if (!canvas || !canvasContext) return;\n const scale = Math.min(2, devicePixelRatio || 1);\n const width = Math.max(1, Math.round(art.clientWidth * scale));\n const height = Math.max(1, Math.round(art.clientHeight * scale));\n if (canvas.width !== width || canvas.height !== height) {\n canvas.width = width;\n canvas.height = height;\n }\n canvasContext.setTransform(scale, 0, 0, scale, 0, 0);\n };\n const drawWaveform = (strength: number) => {\n if (!canvas || !canvasContext) return;\n resizeCanvas();\n const width = art.clientWidth;\n const height = art.clientHeight;\n canvasContext.clearRect(0, 0, width, height);\n if (strength === 0) return;\n const accent = getComputedStyle(art).getPropertyValue('--mango-av-accent').trim() || '#5b3fd6';\n canvasContext.globalAlpha = Math.min(.7, .18 + strength / 100);\n canvasContext.strokeStyle = accent;\n canvasContext.lineWidth = 1.5;\n canvasContext.beginPath();\n const amplitude = reducedMotion ? 0 : Math.min(height * .16, 5 + this.#level * height * .12);\n for (let index = 0; index < samples.length; index += 1) {\n const x = (index / (samples.length - 1)) * width;\n const sample = (samples[index]! - 128) / 128;\n const y = height * .72 + sample * amplitude;\n if (index === 0) canvasContext.moveTo(x, y);\n else canvasContext.lineTo(x, y);\n }\n canvasContext.stroke();\n canvasContext.globalAlpha = 1;\n };\n const drawSpectrum = (strength: number) => {\n if (!canvas || !canvasContext) return;\n resizeCanvas();\n const width = art.clientWidth;\n const height = art.clientHeight;\n canvasContext.clearRect(0, 0, width, height);\n if (strength === 0) return;\n analyzer.getByteFrequencyData(frequencies);\n const accent = getComputedStyle(art).getPropertyValue('--mango-av-accent').trim() || '#5b3fd6';\n const barCount = 24;\n const gap = 3;\n const barWidth = Math.max(1, (width - gap * (barCount - 1)) / barCount);\n canvasContext.fillStyle = accent;\n canvasContext.globalAlpha = reducedMotion ? .2 : Math.min(.55, .15 + strength / 120);\n for (let index = 0; index < barCount; index += 1) {\n const frequencyIndex = Math.floor((index / barCount) ** 1.7 * frequencies.length * .7);\n const energy = reducedMotion ? .08 : (frequencies[frequencyIndex] ?? 0) / 255;\n const barHeight = Math.max(1, energy * height * .32);\n canvasContext.fillRect(index * (barWidth + gap), height - barHeight, barWidth, barHeight);\n }\n canvasContext.globalAlpha = 1;\n };\n const draw = () => {\n if (token !== this.#token) return;\n analyzer.getByteTimeDomainData(samples);\n let sum = 0;\n for (const sample of samples) {\n const normalized = (sample - 128) / 128;\n sum += normalized * normalized;\n }\n const rms = Math.sqrt(sum / samples.length);\n const target = media.paused || rms < .012 ? 0 : Math.min(1, (rms - .012) * 5);\n this.#level += (target - this.#level) * (target > this.#level ? .1 : .04);\n if (target === 0 && this.#level < .01) this.#level = 0;\n const strength = reducedMotion\n ? this.#level * 18\n : this.#level === 0 ? 0 : Math.min(52, 12 + this.#level * 40);\n art.style.setProperty('--mango-av-wave-rise', `${(8 - (reducedMotion ? 0 : this.#level * 6)).toFixed(2)}%`);\n art.style.setProperty('--mango-av-wave-strength', `${strength.toFixed(1)}%`);\n art.style.setProperty('--mango-av-wave-mid-strength', `${(strength * .45).toFixed(1)}%`);\n art.style.setProperty('--mango-av-wave-edge-strength', `${(strength * .18).toFixed(1)}%`);\n if (mode === 'waveform') drawWaveform(strength);\n else if (mode === 'spectrum') drawSpectrum(strength);\n this.#frame = requestAnimationFrame(draw);\n };\n draw();\n const resume = () => void context.resume();\n this.#resume = resume;\n media.addEventListener('play', resume, { once: true });\n if (!media.paused) resume();\n } catch {\n this.disconnect();\n }\n }\n\n disconnect(): void {\n this.#token += 1;\n if (typeof cancelAnimationFrame !== 'undefined') cancelAnimationFrame(this.#frame);\n this.#frame = 0;\n this.#level = 0;\n if (this.#media && this.#resume) this.#media.removeEventListener('play', this.#resume);\n try { this.#source?.disconnect(); } catch { /* Already disconnected. */ }\n try { this.#analyzer?.disconnect(); } catch { /* Already disconnected. */ }\n if (this.#context && this.#context.state !== 'closed') void this.#context.close();\n this.#analyzer = undefined;\n this.#source = undefined;\n this.#context = undefined;\n this.#media = undefined;\n this.#resume = undefined;\n }\n}\n","import type { AVAudioVisualizerMode } from '../types';\n\nexport interface AudioMediaPolicy {\n crossOrigin?: 'anonymous' | 'use-credentials';\n implicitCors: boolean;\n visualize: boolean;\n}\n\nconst isCrossOriginHttp = (sourceUrl: string): boolean => {\n try {\n const url = new URL(sourceUrl, document.baseURI);\n return (url.protocol === 'http:' || url.protocol === 'https:') && url.origin !== location.origin;\n } catch {\n return false;\n }\n};\n\nexport const resolveAudioMediaPolicy = (\n sourceUrl: string,\n configuredCrossOrigin: 'anonymous' | 'use-credentials' | '',\n crossOriginExplicit: boolean,\n visualizerMode: AVAudioVisualizerMode | undefined,\n fallback: boolean,\n): AudioMediaPolicy => {\n const crossOrigin = isCrossOriginHttp(sourceUrl);\n const implicitCors = Boolean(visualizerMode) && crossOrigin && !crossOriginExplicit && !fallback;\n const selectedCrossOrigin = implicitCors ? 'anonymous' : configuredCrossOrigin || undefined;\n // Explicit no-CORS loading is respected, but a cross-origin element loaded that\n // way cannot safely be connected to Web Audio without risking silent output.\n const visualize = Boolean(visualizerMode) && !fallback && !(crossOrigin && !selectedCrossOrigin);\n return {\n ...(selectedCrossOrigin ? { crossOrigin: selectedCrossOrigin } : {}),\n implicitCors,\n visualize,\n };\n};\n\nexport const applyMediaSource = (\n media: HTMLMediaElement,\n sourceUrl: string,\n crossOrigin?: 'anonymous' | 'use-credentials',\n): void => {\n if (crossOrigin) media.crossOrigin = crossOrigin;\n else media.removeAttribute('crossorigin');\n media.src = sourceUrl;\n};\n"],"mappings":"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","names":[]}
@@ -4,15 +4,15 @@ import {
4
4
  import {
5
5
  AVControllerElement,
6
6
  componentTheme
7
- } from "./chunk-QMUWPXP5.js";
7
+ } from "./chunk-C4H46HDS.js";
8
8
  import {
9
9
  loadTranscript
10
- } from "./chunk-3PPOT5YI.js";
10
+ } from "./chunk-CIAQZ2EB.js";
11
11
  import {
12
12
  escapeHtml,
13
13
  formatTime,
14
14
  resolveStrings
15
- } from "./chunk-HOISQL65.js";
15
+ } from "./chunk-4NOWGMI5.js";
16
16
 
17
17
  // src/components/mango-av-transcript.ts
18
18
  var styles = `${componentTheme}
@@ -160,4 +160,4 @@ export {
160
160
  MangoAVTranscriptElement,
161
161
  defineMangoAVTranscript
162
162
  };
163
- //# sourceMappingURL=chunk-DF3YH3BM.js.map
163
+ //# sourceMappingURL=chunk-FVYEV2LO.js.map
@@ -1,23 +1,25 @@
1
1
  import {
2
2
  AudioArtVisualizer,
3
+ applyMediaSource,
3
4
  audioArtContentHtml,
4
5
  audioArtTranscriptHtml,
5
6
  ensureMediaChrome,
7
+ resolveAudioMediaPolicy,
6
8
  resolveAudioVisualizerMode,
7
9
  syncAudioArtTranscript
8
- } from "./chunk-ZRPA2VFR.js";
10
+ } from "./chunk-ELGGWWDS.js";
9
11
  import {
10
12
  AVControllerElement,
11
13
  componentTheme
12
- } from "./chunk-QMUWPXP5.js";
14
+ } from "./chunk-C4H46HDS.js";
13
15
  import {
14
16
  loadTranscript
15
- } from "./chunk-3PPOT5YI.js";
17
+ } from "./chunk-CIAQZ2EB.js";
16
18
  import {
17
19
  escapeHtml,
18
20
  formatString,
19
21
  resolveStrings
20
- } from "./chunk-HOISQL65.js";
22
+ } from "./chunk-4NOWGMI5.js";
21
23
 
22
24
  // src/components/mango-av-player.ts
23
25
  var hlsElementRegistration;
@@ -57,6 +59,7 @@ var MangoAVPlayerElement = class extends AVControllerElement {
57
59
  #config = {};
58
60
  #resolvedSourceUrl;
59
61
  #sourceToken = 0;
62
+ #fallbackSourceUrl;
60
63
  #transcriptCues = [];
61
64
  #transcriptToken = 0;
62
65
  #visualizer = new AudioArtVisualizer();
@@ -117,30 +120,34 @@ var MangoAVPlayerElement = class extends AVControllerElement {
117
120
  return this.controller.selectCanvas(indexOrId, options);
118
121
  }
119
122
  controllerChanged() {
123
+ this.#resetSourceLoad();
120
124
  this.#transcriptCues = [];
121
125
  this.#render();
122
126
  void this.#loadTranscript();
123
127
  }
124
128
  handleControllerEvent(type, event) {
125
129
  if (type === "av-configchange") {
126
- this.#resolvedSourceUrl = void 0;
130
+ this.#resetSourceLoad();
127
131
  void ensureMediaChrome(this.controller.config.preferredLanguages[0]).then(() => {
128
132
  this.#render();
129
133
  void this.#loadTranscript();
130
134
  void this.#resolveSource();
131
135
  });
132
136
  } else if (type === "av-ready" || type === "av-canvaschange") {
133
- this.#resolvedSourceUrl = void 0;
137
+ this.#resetSourceLoad();
134
138
  this.#transcriptCues = [];
135
139
  if (this.controller.source?.isHls) void ensureHlsElement();
136
140
  this.#render();
137
141
  void this.#loadTranscript();
138
142
  void this.#resolveSource();
139
- } else if (type === "av-sourcechange" || type === "av-error") {
140
- this.#resolvedSourceUrl = void 0;
143
+ } else if (type === "av-sourcechange") {
144
+ this.#resetSourceLoad();
141
145
  if (this.controller.source?.isHls) void ensureHlsElement();
142
146
  this.#render();
143
147
  void this.#resolveSource();
148
+ } else if (type === "av-error") {
149
+ this.#resolvedSourceUrl = void 0;
150
+ this.#render();
144
151
  } else if (type === "av-seek") {
145
152
  syncAudioArtTranscript(this.#root, event.detail.to, true);
146
153
  } else if (type === "av-timeupdate" || type === "av-statechange") {
@@ -172,12 +179,7 @@ var MangoAVPlayerElement = class extends AVControllerElement {
172
179
  ${canvas && source ? this.#readyHtml(canvas, source) : state.status !== "loading" && state.status !== "error" ? `<div class="audio-art"><span>${escapeHtml(ui.noMediaLoaded)}</span></div>` : ""}
173
180
  </div>`;
174
181
  const media = this.#root.querySelector("video, audio, hls-video");
175
- if (media) {
176
- this.controller.attachMedia(media);
177
- const art = this.#root.querySelector(".audio-art.visualizer");
178
- const visualizerMode = resolveAudioVisualizerMode(this.controller.config.audioArt.visualizer);
179
- if (art && media instanceof HTMLAudioElement && visualizerMode) this.#visualizer.connect(media, art, visualizerMode);
180
- }
182
+ if (media && this.#resolvedSourceUrl) this.#activateMedia(media, this.#resolvedSourceUrl);
181
183
  syncAudioArtTranscript(this.#root, this.controller.state.currentTime);
182
184
  this.#bind();
183
185
  }
@@ -191,7 +193,7 @@ var MangoAVPlayerElement = class extends AVControllerElement {
191
193
  const showToolbar = controls.navigation || controls.autoAdvance || showSourceSelector;
192
194
  const visualizerMode = resolveAudioVisualizerMode(this.controller.config.audioArt.visualizer);
193
195
  return `<media-controller ${kind === "audio" ? "audio" : ""} ${language ? `lang="${escapeHtml(language)}"` : ""}>
194
- ${kind === "audio" ? `<div class="audio-art ${visualizerMode ? `visualizer visualizer--${visualizerMode}` : ""}">${audioArtContentHtml(canvas.label, this.#transcriptCues, Boolean(this.controller.config.audioArt.transcript), visualizerMode)}</div>` : ""}
196
+ ${kind === "audio" ? `<div class="audio-art ${visualizerMode ? `visualizer visualizer--${visualizerMode}` : ""}">${audioArtContentHtml(canvas.label, this.controller.config.audioArt.title ?? "", this.#transcriptCues, Boolean(this.controller.config.audioArt.transcript), visualizerMode)}</div>` : ""}
195
197
  ${this.#mediaHtml(canvas, source)}
196
198
  <slot name="overlay"></slot>
197
199
  <slot name="controls">
@@ -223,10 +225,8 @@ var MangoAVPlayerElement = class extends AVControllerElement {
223
225
  }
224
226
  #mediaHtml(canvas, source) {
225
227
  const tag = source.isHls ? "hls-video" : source.kind;
226
- const sourceUrl = this.#resolvedSourceUrl ?? (this.controller.config.resolveRequest ? "" : source.src);
227
228
  const attributes = [
228
229
  'slot="media"',
229
- sourceUrl ? `src="${escapeHtml(sourceUrl)}"` : "",
230
230
  `preload="${this.controller.config.preload}"`,
231
231
  this.controller.config.crossorigin ? `crossorigin="${this.controller.config.crossorigin}"` : "",
232
232
  source.kind === "video" && canvas.poster ? `poster="${escapeHtml(canvas.poster)}"` : "",
@@ -243,18 +243,77 @@ var MangoAVPlayerElement = class extends AVControllerElement {
243
243
  this.#root.querySelector("[data-action=auto-advance]")?.addEventListener("change", (event) => this.controller.setAutoAdvance(event.target.checked));
244
244
  }
245
245
  async #resolveSource() {
246
- if (!this.controller.config.resolveRequest) return;
247
246
  const token = ++this.#sourceToken;
248
247
  try {
249
248
  const url = await this.controller.resolvedSourceUrl();
250
249
  if (token !== this.#sourceToken || !url) return;
251
250
  this.#resolvedSourceUrl = url;
252
- this.#root.querySelector("video, audio, hls-video")?.setAttribute("src", url);
251
+ const media = this.#root.querySelector("video, audio, hls-video");
252
+ if (media) this.#activateMedia(media, url);
253
253
  } catch (cause) {
254
254
  const error = cause instanceof Error ? cause : new Error(String(cause));
255
255
  this.dispatchEvent(new CustomEvent("av-error", { detail: { error }, bubbles: true, composed: true }));
256
256
  }
257
257
  }
258
+ #activateMedia(media, sourceUrl) {
259
+ const mode = resolveAudioVisualizerMode(this.controller.config.audioArt.visualizer);
260
+ const policy = resolveAudioMediaPolicy(
261
+ sourceUrl,
262
+ this.controller.config.crossorigin,
263
+ this.controller.crossoriginExplicit,
264
+ media instanceof HTMLAudioElement ? mode : void 0,
265
+ this.#fallbackSourceUrl === sourceUrl
266
+ );
267
+ if (media instanceof HTMLAudioElement && policy.implicitCors) {
268
+ media.addEventListener("error", (event) => {
269
+ if (this.#fallbackSourceUrl === sourceUrl) return;
270
+ event.stopImmediatePropagation();
271
+ this.#fallbackToOrdinaryAudio(media, sourceUrl);
272
+ }, { once: true });
273
+ }
274
+ if (media instanceof HTMLAudioElement && mode && policy.visualize) {
275
+ const connect = () => {
276
+ const art = this.#root.querySelector(".audio-art.visualizer");
277
+ if (art && media.isConnected && (media.currentSrc === sourceUrl || media.src === sourceUrl)) {
278
+ this.#visualizer.connect(media, art, mode);
279
+ }
280
+ };
281
+ media.addEventListener("loadedmetadata", connect, { once: true });
282
+ if (media.readyState >= HTMLMediaElement.HAVE_METADATA) queueMicrotask(connect);
283
+ }
284
+ this.controller.attachMedia(media);
285
+ applyMediaSource(media, sourceUrl, policy.crossOrigin);
286
+ }
287
+ #fallbackToOrdinaryAudio(failed, sourceUrl) {
288
+ this.#fallbackSourceUrl = sourceUrl;
289
+ this.#visualizer.disconnect();
290
+ const requestedTime = this.controller.state.currentTime;
291
+ const shouldPlay = this.controller.state.status === "playing" || !failed.paused;
292
+ const replacement = failed.cloneNode(true);
293
+ replacement.removeAttribute("src");
294
+ replacement.removeAttribute("crossorigin");
295
+ replacement.volume = failed.volume;
296
+ replacement.muted = failed.muted;
297
+ replacement.playbackRate = failed.playbackRate;
298
+ const hadFocus = this.#root.activeElement === failed;
299
+ failed.replaceWith(replacement);
300
+ this.controller.attachMedia(replacement);
301
+ this.controller.seekTo(requestedTime);
302
+ if (hadFocus) replacement.focus();
303
+ const art = this.#root.querySelector(".audio-art");
304
+ art?.classList.remove("visualizer");
305
+ applyMediaSource(replacement, sourceUrl);
306
+ if (shouldPlay) replacement.addEventListener("loadedmetadata", () => void replacement.play().catch(() => void 0), { once: true });
307
+ this.controller.dispatchEvent(new CustomEvent("av-visualizerfallback", {
308
+ detail: { canvasId: this.controller.canvas?.id ?? "", sourceUrl, reason: "cors" }
309
+ }));
310
+ }
311
+ #resetSourceLoad() {
312
+ this.#sourceToken += 1;
313
+ this.#resolvedSourceUrl = void 0;
314
+ this.#fallbackSourceUrl = void 0;
315
+ this.#visualizer.disconnect();
316
+ }
258
317
  async #loadTranscript() {
259
318
  const resource = this.controller.canvas?.transcripts[0];
260
319
  if (!resource || !this.controller.config.audioArt.transcript) return;
@@ -272,7 +331,7 @@ var MangoAVPlayerElement = class extends AVControllerElement {
272
331
  const canvas = this.controller.canvas;
273
332
  if (art && canvas && this.controller.source?.kind === "audio") {
274
333
  const text = art.querySelector(".audio-art-text");
275
- if (text) text.innerHTML = audioArtTranscriptHtml(canvas.label, cues, true);
334
+ if (text) text.innerHTML = audioArtTranscriptHtml(canvas.label, this.controller.config.audioArt.title ?? "", cues, true);
276
335
  syncAudioArtTranscript(art, this.controller.state.currentTime);
277
336
  }
278
337
  } catch (cause) {
@@ -292,4 +351,4 @@ export {
292
351
  MangoAVPlayerElement,
293
352
  defineMangoAVPlayer
294
353
  };
295
- //# sourceMappingURL=chunk-ZMC36FZ2.js.map
354
+ //# sourceMappingURL=chunk-YHTJZXOE.js.map