@mango-iiif/av 0.4.0 → 1.0.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 (69) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/README.md +24 -17
  3. package/dist/annotations.d.ts +2 -2
  4. package/dist/annotations.js +4 -4
  5. package/dist/captions.d.ts +23 -0
  6. package/dist/captions.js +16 -0
  7. package/dist/captions.js.map +1 -0
  8. package/dist/chapters.d.ts +2 -2
  9. package/dist/chapters.js +3 -3
  10. package/dist/{chunk-4NOWGMI5.js → chunk-53ZN4Y4L.js} +28 -66
  11. package/dist/chunk-53ZN4Y4L.js.map +1 -0
  12. package/dist/chunk-AWLDINQR.js +73 -0
  13. package/dist/chunk-AWLDINQR.js.map +1 -0
  14. package/dist/chunk-BB7RE7EO.js +407 -0
  15. package/dist/chunk-BB7RE7EO.js.map +1 -0
  16. package/dist/chunk-BMMQFCD3.js +86 -0
  17. package/dist/chunk-BMMQFCD3.js.map +1 -0
  18. package/dist/chunk-IZYEGSGT.js +148 -0
  19. package/dist/chunk-IZYEGSGT.js.map +1 -0
  20. package/dist/chunk-JXZR5DL2.js +160 -0
  21. package/dist/chunk-JXZR5DL2.js.map +1 -0
  22. package/dist/{chunk-CIAQZ2EB.js → chunk-LMZIFVHN.js} +8 -4
  23. package/dist/chunk-LMZIFVHN.js.map +1 -0
  24. package/dist/chunk-TQNEVWUH.js +39 -0
  25. package/dist/chunk-TQNEVWUH.js.map +1 -0
  26. package/dist/chunk-XXM4G3K5.js +296 -0
  27. package/dist/chunk-XXM4G3K5.js.map +1 -0
  28. package/dist/chunk-YVI7J43Q.js +29 -0
  29. package/dist/chunk-YVI7J43Q.js.map +1 -0
  30. package/dist/chunk-Z2OF3RKT.js +152 -0
  31. package/dist/chunk-Z2OF3RKT.js.map +1 -0
  32. package/dist/{controller-SkwGkVE8.d.ts → controller-Ag3nFtwP.d.ts} +16 -24
  33. package/dist/{controller-element-CkXIA6w2.d.ts → controller-element-Cn5cxKnD.d.ts} +4 -2
  34. package/dist/core.d.ts +2 -2
  35. package/dist/core.js +2 -2
  36. package/dist/index.d.ts +6 -3
  37. package/dist/index.js +21 -14
  38. package/dist/index.js.map +1 -1
  39. package/dist/media.d.ts +3 -3
  40. package/dist/media.js +11 -5
  41. package/dist/media.js.map +1 -1
  42. package/dist/player.d.ts +8 -2
  43. package/dist/player.js +3 -5
  44. package/dist/player.js.map +1 -1
  45. package/dist/ramp-2XKALEGA.js +98 -0
  46. package/dist/ramp-2XKALEGA.js.map +1 -0
  47. package/dist/transcript-element.d.ts +2 -2
  48. package/dist/transcript-element.js +6 -5
  49. package/dist/transcript-element.js.map +1 -1
  50. package/docs/API.md +70 -45
  51. package/package.json +18 -11
  52. package/dist/chunk-4NOWGMI5.js.map +0 -1
  53. package/dist/chunk-BAE4FKBJ.js +0 -645
  54. package/dist/chunk-BAE4FKBJ.js.map +0 -1
  55. package/dist/chunk-C4H46HDS.js +0 -95
  56. package/dist/chunk-C4H46HDS.js.map +0 -1
  57. package/dist/chunk-CDLYT3KB.js +0 -17
  58. package/dist/chunk-CDLYT3KB.js.map +0 -1
  59. package/dist/chunk-CIAQZ2EB.js.map +0 -1
  60. package/dist/chunk-EHIKFRFV.js +0 -75
  61. package/dist/chunk-EHIKFRFV.js.map +0 -1
  62. package/dist/chunk-FVYEV2LO.js +0 -163
  63. package/dist/chunk-FVYEV2LO.js.map +0 -1
  64. package/dist/chunk-GQD675FA.js +0 -289
  65. package/dist/chunk-GQD675FA.js.map +0 -1
  66. package/dist/chunk-M5ZZRBGD.js +0 -357
  67. package/dist/chunk-M5ZZRBGD.js.map +0 -1
  68. package/dist/chunk-YX32ZQPR.js +0 -83
  69. package/dist/chunk-YX32ZQPR.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -2,6 +2,39 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ### Playback through Samvera RAMP
6
+
7
+ - `<mango-av-player>`, and the player in `<mango-av-media>`, now play through [Samvera RAMP](https://github.com/samvera-labs/ramp) 5.1.1 in place of Media Chrome. RAMP gets a Presentation 3 Manifest holding only the active Canvas and source, rebuilt from the normalized model, so Presentation 2 Manifests, `resolveRequest`, source selection and Canvas navigation keep working through the controller. The controller attaches to RAMP's media element, so its API and events are unchanged.
8
+ - React, RAMP and Video.js are new dependencies, loaded with a dynamic import the first time a player renders. `media-chrome` and `hls-video-element` are removed; RAMP's Video.js plays HLS.
9
+ - The player controls wear Video.js's default skin, a solid bar under the picture, in place of RAMP's overlay theme. The skip buttons use a circular-arrow icon matching the other controls.
10
+ - Player controls map onto RAMP: play, progress, time, volume, captions, playback rate, picture in picture, download and fullscreen, plus skip buttons from `controls.skipSeconds` (rounded to 5, 10 or 30 seconds). Previous, Next, Auto-advance and the source selector stay in the toolbar under the player.
11
+ - `<mango-av-media>` takes the layout and styling from the Mango annotation editor's audio and video view. The player comes first, then a `Position 0:12 / 3:45` line, then segmented tabs for Transcript, Chapters, Annotations, Metadata and Files. Its tabs are the standalone panel elements sharing its controller, and `activePanel` now defaults to `'transcript'`.
12
+ - Transcript, chapters and annotations are rows of a time column and text, with the passage being heard tinted. While playing, the transcript keeps the current cue at the top of its list. Chapters now highlight the chapter being heard rather than every chapter on the Canvas.
13
+ - Audio shows a card with the Canvas poster or Manifest thumbnail, a `Recording` eyebrow, and `audioArt.title`, which now defaults to the Manifest label.
14
+ - New theme properties: `--mango-av-accent-tint`, `--mango-av-danger`, `--mango-av-list-height`, and `--mango-av-panel-background`, `--mango-av-panel-border` and `--mango-av-panel-padding` for the standalone panel card. A theme set on `<mango-av-media>` now reaches the components inside it.
15
+ - `AVManifest.thumbnail` is normalized from the Manifest `thumbnail`.
16
+
17
+ ### Audio captions
18
+
19
+ - New `<mango-av-captions>` element, also at `@mango-iiif/av/captions`, which shows the transcript's line being spoken as captions are shown on video, with the lines either side faded (or only the current line with `line`).
20
+ - `<mango-av-player>` has a `stage` slot for content shown with the media: in the audio card above the controls, or over the foot of a video.
21
+ - `audioArt.captions` (`'context'`, `'line'` or `false`) shows captions in the audio card of `<mango-av-media>`.
22
+ - WebVTT voice tags (`<v Ana>`) now set each cue's speaker.
23
+ - The transcript panel and captions share one load of a transcript.
24
+ - A new demo page, `captions.html`, shows captions built in and in a layout of the page's own.
25
+ - Fixed the line being heard losing its highlight while the pointer is over it.
26
+
27
+ ### Breaking changes
28
+
29
+ - Removed the audio artwork visualizer and moving transcript (`audioArt.visualizer`, `audioArt.transcript`, `--mango-av-visualizer-color`, `--mango-av-visualizer-intensity`) and the `av-visualizerfallback` event. They needed to own the media element, which RAMP now does.
30
+ - Removed `crossorigin` and `preload`. `withCredentials` sends credentials with the requests Video.js makes itself, such as HLS segments; RAMP gives no way to set the media element's own `crossorigin` attribute.
31
+ - Resume uses RAMP's position cache and prompt. Removed `resume.prompt`, `controller.getSavedPosition()`, and the `resumePrompt`, `resume` and `startOver` strings.
32
+ - Removed transcript search markers (`transcript.showSearchMarkers`) and the `transcriptSearchMatches`, `searchMatchAt`, `downloadMediaFile` and `unableToLoadMedia` strings. Added the `position` and `recording` strings; `searchTranscript` now reads `Find in transcript`.
33
+ - Removed `--mango-av-panel-width`, and the Media Chrome `player-overlay` and `controls` slots of `<mango-av-media>` and `overlay` and `controls` slots of `<mango-av-player>`.
34
+ - `controller.selectCanvas()`, `next()`, `previous()` and `selectSource()` now detach the current media element. Playback that was running resumes once the next media element can play.
35
+
36
+ ### Earlier unreleased changes
37
+
5
38
  - Added `--mango-av-visualizer-color` and `--mango-av-visualizer-intensity` so the audio artwork visualizer can carry a colour and a strength of its own instead of only following `--mango-av-accent`. Intensity applies to all three modes, scaling the pulse tint, the waveform trace and the spectrum bars in size as well as opacity, and each mode keeps its existing ceiling. Both properties are read at the point of use, so a value set on the custom element inherits through to the artwork, and both default to the existing appearance.
6
39
  - The configuration builder demo gained visualizer colour and intensity controls alongside the visualizer mode select.
7
40
 
package/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # @mango-iiif/av
2
2
 
3
- Framework-free IIIF audio and video components powered by [Media Chrome](https://www.media-chrome.org/). The package reads Presentation 2 and Presentation 3 manifests and exposes an all-in-one element, independently placeable components, and a typed controller API. It has no React or Svelte runtime dependency.
3
+ IIIF audio and video custom elements, played through [Samvera RAMP](https://github.com/samvera-labs/ramp). The package reads Presentation 2 and Presentation 3 manifests and exposes an all-in-one element, independently placeable components, and a typed controller API.
4
+
5
+ The elements are framework-free to use. RAMP is a React and Video.js library, so React, RAMP and Video.js are installed as dependencies. They are loaded with a dynamic import the first time a player renders, so pages that only use the panels or the controller never download them.
4
6
 
5
7
  ## Install
6
8
 
@@ -10,7 +12,7 @@ npm install @mango-iiif/av
10
12
 
11
13
  ## Complete media experience
12
14
 
13
- `<mango-av-media>` contains the player, chapters, transcript, annotations, metadata, and supplemental files:
15
+ `<mango-av-media>` puts the RAMP player first, then the playback position, then the transcript, chapters, annotations, metadata and supplemental files in tabs underneath:
14
16
 
15
17
  ```ts
16
18
  import '@mango-iiif/av';
@@ -34,11 +36,7 @@ media.config = {
34
36
  pictureInPicture: true,
35
37
  fullscreen: true,
36
38
  },
37
- audioArt: {
38
- title: 'Listen to this recording',
39
- transcript: true,
40
- visualizer: 'waveform',
41
- },
39
+ audioArt: { title: 'Listen to this recording' },
42
40
  resume: { enabled: true },
43
41
  };
44
42
 
@@ -110,12 +108,11 @@ const controller = new AVPlayerController({
110
108
  next: 'Nesaf',
111
109
  searchTranscript: 'Chwilio’r trawsgrifiad',
112
110
  sourceNumber: 'Ffynhonnell {number}',
113
- resumePrompt: 'Parhau o {time}?',
114
111
  },
115
112
  });
116
113
  ```
117
114
 
118
- `preferredLanguages` chooses localized IIIF labels and sets the Media Chrome language. The package automatically loads Media Chrome's bundled German, Spanish, French, Portuguese, and Chinese dictionaries. `strings` covers the Mango AV wrapper UI and generated fallback labels for any language. The complete English dictionary is exported as `defaultStrings` for building and validating translation files.
115
+ `preferredLanguages` chooses localized IIIF labels and is passed to RAMP, which loads the matching Video.js dictionary for the player controls. `strings` covers the Mango AV wrapper UI and generated fallback labels for any language. The complete English dictionary is exported as `defaultStrings` for building and validating translation files.
119
116
 
120
117
  ## Svelte
121
118
 
@@ -169,23 +166,33 @@ mango-av-annotations.tailwind-audio {
169
166
  --mango-av-border: #cbd5e1;
170
167
  --mango-av-radius: 0.875rem;
171
168
  --mango-av-font: Inter, ui-sans-serif, system-ui;
172
- --mango-av-panel-width: 26rem;
169
+ --mango-av-list-height: 30rem;
173
170
  }
174
171
  ```
175
172
 
176
- The audio artwork visualizer follows `--mango-av-accent` unless it is given
177
- `--mango-av-visualizer-color`, and `--mango-av-visualizer-intensity` scales how
178
- strongly it renders (`1` by default, `0` to hide it). See
179
- [docs/API.md](docs/API.md) for the full property list.
173
+ A theme set on `<mango-av-media>` reaches the player and panels inside it. See [docs/API.md](docs/API.md) for the full property list. The player controls wear Video.js's default skin.
174
+
175
+ The complete element provides `header`, `before-player` and `after-player` slots. The standalone player provides `stage`, for content shown with the media such as captions, and `after-player`. The information components provide a `heading` slot.
176
+
177
+ ## Audio captions
178
+
179
+ Audio can read like video: `<mango-av-captions>` shows the line being spoken, with the lines either side faded. In `<mango-av-media>`, set `audioArt: { captions: 'context' }` (or `'line'`). In a layout of your own, put it in a player's `stage` slot, or anywhere else, on the same controller:
180
+
181
+ ```html
182
+ <mango-av-transcript></mango-av-transcript>
183
+ <mango-av-player>
184
+ <mango-av-captions slot="stage"></mango-av-captions>
185
+ </mango-av-player>
186
+ ```
180
187
 
181
- Media Chrome variables such as `--media-primary-color` and `--media-secondary-color` can also be set directly. The complete element provides `header`, `before-player`, `player-overlay`, `controls`, and `after-player` slots. The standalone player provides `overlay` and `controls`; the information components provide a `heading` slot.
188
+ `npm run dev` and open `/captions.html` to see both.
182
189
 
183
190
  Run `npm run dev` to use the interactive configuration builder and the linked composable-components demonstration. They include Video, HLS, and Audio presets, feature switches, generated API configuration, independently positioned components, and multiple style recipes.
184
191
 
185
192
  ## Supported IIIF AV behavior
186
193
 
187
194
  - Presentation 2 sequences and Presentation 3 Canvas items
188
- - Audio, video, native HLS, and hls.js-backed HLS through `<hls-video>`
195
+ - Audio, video and HLS, played by RAMP's Video.js
189
196
  - `Choice` and multiple painting bodies, media source selection, temporal fragments, and clipped playback
190
197
  - Multi-Canvas previous/next, `start`/`startCanvas`, `auto-advance`, and `repeat`
191
198
  - Nested Range navigation and temporal chapters
@@ -195,7 +202,7 @@ Run `npm run dev` to use the interactive configuration builder and the linked co
195
202
  - Commenting, supplementing, transcribing, tagging, and highlighting annotations
196
203
  - Manifest/Canvas metadata, rights, required statements, homepages, and `rendering` downloads
197
204
  - Placeholder posters, request resolution for protected media, and service metadata normalization
198
- - Optional persisted playback positions
205
+ - Optional persisted playback positions, through RAMP's resume cache
199
206
  - Configurable annotation create/update/delete adapters
200
207
 
201
208
  See [API.md](docs/API.md) for the complete API and [FEATURE_PARITY.md](docs/FEATURE_PARITY.md) for feature coverage.
@@ -1,5 +1,5 @@
1
- import { A as AVControllerElement } from './controller-element-CkXIA6w2.js';
2
- import { k as AVPlayerEventMap } from './controller-SkwGkVE8.js';
1
+ import { A as AVControllerElement } from './controller-element-Cn5cxKnD.js';
2
+ import { j as AVPlayerEventMap } from './controller-Ag3nFtwP.js';
3
3
 
4
4
  declare class MangoAVAnnotationsElement extends AVControllerElement {
5
5
  #private;
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  MangoAVAnnotationsElement,
3
3
  defineMangoAVAnnotations
4
- } from "./chunk-YX32ZQPR.js";
5
- import "./chunk-CDLYT3KB.js";
6
- import "./chunk-C4H46HDS.js";
7
- import "./chunk-4NOWGMI5.js";
4
+ } from "./chunk-AWLDINQR.js";
5
+ import "./chunk-TQNEVWUH.js";
6
+ import "./chunk-JXZR5DL2.js";
7
+ import "./chunk-53ZN4Y4L.js";
8
8
 
9
9
  // src/annotations.ts
10
10
  if (typeof window !== "undefined" && "customElements" in window) defineMangoAVAnnotations();
@@ -0,0 +1,23 @@
1
+ import { A as AVControllerElement } from './controller-element-Cn5cxKnD.js';
2
+ import { j as AVPlayerEventMap } from './controller-Ag3nFtwP.js';
3
+
4
+ /**
5
+ * The line being spoken, as captions are shown on video, with the lines
6
+ * either side faded. Reads the active Canvas transcript through the controller,
7
+ * so it can sit anywhere: inside a player's `stage` slot, or in a layout of
8
+ * the host's own.
9
+ *
10
+ * Attributes: `line` shows only the line being spoken; `audio-only` hides it
11
+ * while a video plays, since video has captions of its own.
12
+ */
13
+ declare class MangoAVCaptionsElement extends AVControllerElement {
14
+ #private;
15
+ static observedAttributes: string[];
16
+ constructor();
17
+ attributeChangedCallback(): void;
18
+ protected controllerChanged(): void;
19
+ protected handleControllerEvent<K extends keyof AVPlayerEventMap>(type: K, _event: CustomEvent<AVPlayerEventMap[K]>): void;
20
+ }
21
+ declare const defineMangoAVCaptions: (tagName?: string) => typeof MangoAVCaptionsElement;
22
+
23
+ export { MangoAVCaptionsElement, defineMangoAVCaptions };
@@ -0,0 +1,16 @@
1
+ import {
2
+ MangoAVCaptionsElement,
3
+ defineMangoAVCaptions
4
+ } from "./chunk-IZYEGSGT.js";
5
+ import "./chunk-YVI7J43Q.js";
6
+ import "./chunk-LMZIFVHN.js";
7
+ import "./chunk-JXZR5DL2.js";
8
+ import "./chunk-53ZN4Y4L.js";
9
+
10
+ // src/captions.ts
11
+ if (typeof window !== "undefined" && "customElements" in window) defineMangoAVCaptions();
12
+ export {
13
+ MangoAVCaptionsElement,
14
+ defineMangoAVCaptions
15
+ };
16
+ //# sourceMappingURL=captions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/captions.ts"],"sourcesContent":["export { defineMangoAVCaptions, MangoAVCaptionsElement } from './components/mango-av-captions';\n\nimport { defineMangoAVCaptions } from './components/mango-av-captions';\nif (typeof window !== 'undefined' && 'customElements' in window) defineMangoAVCaptions();\n"],"mappings":";;;;;;;;;;AAGA,IAAI,OAAO,WAAW,eAAe,oBAAoB,OAAQ,uBAAsB;","names":[]}
@@ -1,5 +1,5 @@
1
- import { A as AVControllerElement } from './controller-element-CkXIA6w2.js';
2
- import { k as AVPlayerEventMap } from './controller-SkwGkVE8.js';
1
+ import { A as AVControllerElement } from './controller-element-Cn5cxKnD.js';
2
+ import { j as AVPlayerEventMap } from './controller-Ag3nFtwP.js';
3
3
 
4
4
  declare class MangoAVChaptersElement extends AVControllerElement {
5
5
  #private;
package/dist/chapters.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  MangoAVChaptersElement,
3
3
  defineMangoAVChapters
4
- } from "./chunk-EHIKFRFV.js";
5
- import "./chunk-C4H46HDS.js";
6
- import "./chunk-4NOWGMI5.js";
4
+ } from "./chunk-BMMQFCD3.js";
5
+ import "./chunk-JXZR5DL2.js";
6
+ import "./chunk-53ZN4Y4L.js";
7
7
 
8
8
  // src/chapters.ts
9
9
  if (typeof window !== "undefined" && "customElements" in window) defineMangoAVChapters();
@@ -15,17 +15,13 @@ var defaultStrings = {
15
15
  mediaSource: "Media source",
16
16
  mediaInformation: "Media information",
17
17
  download: "Download",
18
- downloadMediaFile: "Download media file",
19
- searchTranscript: "Search transcript",
20
- transcriptSearchMatches: "Transcript search matches",
21
- searchMatchAt: "Search match at {time}",
22
- resumePrompt: "Resume from {time}?",
23
- resume: "Resume",
24
- startOver: "Start over",
18
+ searchTranscript: "Find in transcript",
19
+ captions: "Captions",
20
+ position: "Position",
21
+ recording: "Recording",
25
22
  loadingManifest: "Loading IIIF manifest\u2026",
26
23
  loadingTranscript: "Loading transcript\u2026",
27
24
  unableToLoadPlayer: "Unable to load the player.",
28
- unableToLoadMedia: "Unable to load media.",
29
25
  noMediaLoaded: "No media loaded",
30
26
  noPlayableMedia: "No playable audio or video source",
31
27
  noManifest: "No manifest is loaded.",
@@ -70,27 +66,23 @@ var defaultConfig = {
70
66
  search: true,
71
67
  download: true,
72
68
  showNotes: false,
73
- showMetadata: false,
74
- showSearchMarkers: true
69
+ showMetadata: false
75
70
  },
76
71
  audioArt: {
77
72
  title: "",
78
- transcript: false,
79
- visualizer: false
73
+ captions: false
80
74
  },
81
75
  resume: {
82
76
  enabled: false,
83
77
  maxItems: 200,
84
- ttlDays: 30,
85
- prompt: true
78
+ ttlDays: 30
86
79
  },
87
80
  strings: defaultStrings,
88
81
  preferredLanguages: ["en", "none"],
89
82
  autoAdvance: false,
90
83
  repeat: false,
91
- activePanel: "chapters",
92
- crossorigin: "",
93
- preload: "metadata",
84
+ activePanel: "transcript",
85
+ withCredentials: false,
94
86
  playbackRates: [0.5, 0.75, 1, 1.25, 1.5, 2]
95
87
  };
96
88
 
@@ -369,7 +361,7 @@ var annotationFrom = (value, canvasId, pageId, languages, index) => {
369
361
  if (pageId) result.sourcePageId = pageId;
370
362
  return result;
371
363
  };
372
- var classifySupplement = (body, annotation, canvasId, languages, index, ui) => {
364
+ var classifySupplement = (body, canvasId, languages, index, ui) => {
373
365
  const id = readId(body);
374
366
  if (!id) return {};
375
367
  const format = readFormat(body);
@@ -417,7 +409,7 @@ var collectSupplementing = (pages, canvasId, languages, ui) => {
417
409
  for (const body of bodyItems(annotation.body ?? annotation.resource)) {
418
410
  const bodyId = readId(body);
419
411
  if (bodyId && (motivations.includes("supplementing") || motivations.includes("transcribing") || motivations.length === 0)) {
420
- const supplement = classifySupplement(body, annotation, canvasId, languages, index, ui);
412
+ const supplement = classifySupplement(body, canvasId, languages, index, ui);
421
413
  if (supplement.track) tracks.push(supplement.track);
422
414
  if (supplement.transcript) transcripts.push(supplement.transcript);
423
415
  }
@@ -670,11 +662,12 @@ var normalizeManifest = async (input, options = {}) => {
670
662
  if (homepage) result.homepage = homepage;
671
663
  const logo = readId(asArray(source.logo ?? source.provider)[0]);
672
664
  if (logo) result.logo = logo;
665
+ const thumbnail = readId(asArray(source.thumbnail)[0]);
666
+ if (thumbnail) result.thumbnail = thumbnail;
673
667
  return result;
674
668
  };
675
669
 
676
670
  // src/controller.ts
677
- var positionPrefix = "@mango-iiif/av:position:";
678
671
  var AVPlayerController = class extends EventTarget {
679
672
  #config;
680
673
  #state = {
@@ -693,11 +686,11 @@ var AVPlayerController = class extends EventTarget {
693
686
  #lastTime = 0;
694
687
  #timelineScale = 1;
695
688
  #pendingTime;
689
+ /** Playback to start once the next Canvas or source has media attached. */
690
+ #pendingPlay = false;
696
691
  #boundListeners = /* @__PURE__ */ new Map();
697
- #crossoriginExplicit;
698
692
  constructor(config = {}) {
699
693
  super();
700
- this.#crossoriginExplicit = Object.hasOwn(config, "crossorigin");
701
694
  this.#config = mergeConfig(defaultConfig, config);
702
695
  this.#state.autoAdvance = this.#config.autoAdvance;
703
696
  this.#state.repeat = this.#config.repeat;
@@ -720,17 +713,12 @@ var AVPlayerController = class extends EventTarget {
720
713
  get media() {
721
714
  return this.#media;
722
715
  }
723
- /** Whether the host explicitly selected the media element CORS policy. */
724
- get crossoriginExplicit() {
725
- return this.#crossoriginExplicit;
726
- }
727
716
  on(type, listener) {
728
717
  const eventListener = listener;
729
718
  this.addEventListener(type, eventListener);
730
719
  return () => this.removeEventListener(type, eventListener);
731
720
  }
732
721
  configure(config) {
733
- if (Object.hasOwn(config, "crossorigin")) this.#crossoriginExplicit = true;
734
722
  this.#config = mergeConfig(
735
723
  this.#config,
736
724
  config
@@ -804,6 +792,11 @@ var AVPlayerController = class extends EventTarget {
804
792
  this.#state.duration = this.canvas?.duration ?? target?.end ?? (Number.isFinite(media.duration) ? this.#toCanvasTime(media.duration) : 0);
805
793
  this.#emitState();
806
794
  },
795
+ canplay: () => {
796
+ if (!this.#pendingPlay) return;
797
+ this.#pendingPlay = false;
798
+ void media.play().catch(() => void 0);
799
+ },
807
800
  play: () => {
808
801
  this.#state.status = "playing";
809
802
  this.#emit("av-play", { canvasId: this.canvas?.id ?? "", time: this.#toCanvasTime(media.currentTime) });
@@ -811,7 +804,6 @@ var AVPlayerController = class extends EventTarget {
811
804
  },
812
805
  pause: () => {
813
806
  if (!media.ended) this.#state.status = "paused";
814
- this.#savePosition();
815
807
  this.#emit("av-pause", { canvasId: this.canvas?.id ?? "", time: this.#toCanvasTime(media.currentTime) });
816
808
  this.#emitState();
817
809
  },
@@ -851,7 +843,6 @@ var AVPlayerController = class extends EventTarget {
851
843
  ended: () => {
852
844
  this.#state.status = "ended";
853
845
  this.#emit("av-ended", { canvasId: this.canvas?.id ?? "" });
854
- this.#clearPosition();
855
846
  this.#emitState();
856
847
  void this.#advanceAfterEnd();
857
848
  },
@@ -867,6 +858,9 @@ var AVPlayerController = class extends EventTarget {
867
858
  media.addEventListener(type, listener);
868
859
  this.#boundListeners.set(type, listener);
869
860
  }
861
+ if (media.readyState >= HTMLMediaElement.HAVE_METADATA) listeners.loadedmetadata?.(new Event("loadedmetadata"));
862
+ if (media.readyState >= HTMLMediaElement.HAVE_FUTURE_DATA) listeners.canplay?.(new Event("canplay"));
863
+ if (!media.paused) listeners.play?.(new Event("play"));
870
864
  }
871
865
  detachMedia() {
872
866
  if (!this.#media) return;
@@ -915,9 +909,10 @@ var AVPlayerController = class extends EventTarget {
915
909
  this.#state.currentTime = time;
916
910
  this.#state.duration = canvas?.duration ?? 0;
917
911
  this.#pendingTime = this.#state.currentTime;
912
+ this.#pendingPlay = wasPlaying;
913
+ this.detachMedia();
918
914
  if (canvas) this.#emit("av-canvaschange", { canvas, canvasIndex: index });
919
915
  this.#emitState();
920
- if (wasPlaying) queueMicrotask(() => void this.play());
921
916
  return canvas;
922
917
  }
923
918
  next(options = {}) {
@@ -939,9 +934,10 @@ var AVPlayerController = class extends EventTarget {
939
934
  this.#state.sourceIndex = index;
940
935
  this.#timelineScale = 1;
941
936
  this.#pendingTime = currentTime;
937
+ this.#pendingPlay = wasPlaying;
938
+ this.detachMedia();
942
939
  this.#emit("av-sourcechange", { source, sourceIndex: index });
943
940
  this.#emitState();
944
- if (wasPlaying) queueMicrotask(() => void this.play());
945
941
  return source;
946
942
  }
947
943
  async createAnnotation(draft) {
@@ -991,24 +987,7 @@ var AVPlayerController = class extends EventTarget {
991
987
  this.#state.repeat = enabled;
992
988
  this.#emitState();
993
989
  }
994
- getSavedPosition() {
995
- if (!this.#config.resume.enabled || !this.canvas || typeof localStorage === "undefined") return void 0;
996
- const raw = localStorage.getItem(`${positionPrefix}${this.#state.manifest?.id}:${this.canvas.id}`);
997
- if (!raw) return void 0;
998
- try {
999
- const saved = JSON.parse(raw);
1000
- const ttl = (this.#config.resume.ttlDays ?? 30) * 864e5;
1001
- if (Date.now() - saved.savedAt > ttl) {
1002
- this.#clearPosition();
1003
- return void 0;
1004
- }
1005
- return saved.time;
1006
- } catch {
1007
- return void 0;
1008
- }
1009
- }
1010
990
  destroy() {
1011
- this.#savePosition();
1012
991
  this.detachMedia();
1013
992
  }
1014
993
  async resolvedSourceUrl() {
@@ -1025,23 +1004,6 @@ var AVPlayerController = class extends EventTarget {
1025
1004
  const next = this.next({ autoplay: true });
1026
1005
  if (!next) this.#state.status = "ended";
1027
1006
  }
1028
- #savePosition() {
1029
- if (!this.#config.resume.enabled || !this.canvas || typeof localStorage === "undefined") return;
1030
- const time = this.#state.currentTime;
1031
- if (time <= 0 || this.#state.duration > 0 && time >= this.#state.duration - 2) return;
1032
- const key = `${positionPrefix}${this.#state.manifest?.id}:${this.canvas.id}`;
1033
- localStorage.setItem(key, JSON.stringify({ time, savedAt: Date.now() }));
1034
- const keys = Object.keys(localStorage).filter((item) => item.startsWith(positionPrefix));
1035
- const maxItems = this.#config.resume.maxItems ?? 200;
1036
- if (keys.length > maxItems) {
1037
- const oldest = keys.map((item) => ({ item, value: JSON.parse(localStorage.getItem(item) ?? "{}") })).sort((a, b) => (a.value.savedAt ?? 0) - (b.value.savedAt ?? 0))[0];
1038
- if (oldest) localStorage.removeItem(oldest.item);
1039
- }
1040
- }
1041
- #clearPosition() {
1042
- if (!this.canvas || typeof localStorage === "undefined") return;
1043
- localStorage.removeItem(`${positionPrefix}${this.#state.manifest?.id}:${this.canvas.id}`);
1044
- }
1045
1007
  #emit(type, detail) {
1046
1008
  this.dispatchEvent(new CustomEvent(type, { detail, bubbles: false, composed: false }));
1047
1009
  }
@@ -1087,4 +1049,4 @@ export {
1087
1049
  normalizeManifest,
1088
1050
  AVPlayerController
1089
1051
  };
1090
- //# sourceMappingURL=chunk-4NOWGMI5.js.map
1052
+ //# sourceMappingURL=chunk-53ZN4Y4L.js.map