@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.
- package/CHANGELOG.md +33 -0
- package/README.md +24 -17
- package/dist/annotations.d.ts +2 -2
- package/dist/annotations.js +4 -4
- package/dist/captions.d.ts +23 -0
- package/dist/captions.js +16 -0
- package/dist/captions.js.map +1 -0
- package/dist/chapters.d.ts +2 -2
- package/dist/chapters.js +3 -3
- package/dist/{chunk-4NOWGMI5.js → chunk-53ZN4Y4L.js} +28 -66
- package/dist/chunk-53ZN4Y4L.js.map +1 -0
- package/dist/chunk-AWLDINQR.js +73 -0
- package/dist/chunk-AWLDINQR.js.map +1 -0
- package/dist/chunk-BB7RE7EO.js +407 -0
- package/dist/chunk-BB7RE7EO.js.map +1 -0
- package/dist/chunk-BMMQFCD3.js +86 -0
- package/dist/chunk-BMMQFCD3.js.map +1 -0
- package/dist/chunk-IZYEGSGT.js +148 -0
- package/dist/chunk-IZYEGSGT.js.map +1 -0
- package/dist/chunk-JXZR5DL2.js +160 -0
- package/dist/chunk-JXZR5DL2.js.map +1 -0
- package/dist/{chunk-CIAQZ2EB.js → chunk-LMZIFVHN.js} +8 -4
- package/dist/chunk-LMZIFVHN.js.map +1 -0
- package/dist/chunk-TQNEVWUH.js +39 -0
- package/dist/chunk-TQNEVWUH.js.map +1 -0
- package/dist/chunk-XXM4G3K5.js +296 -0
- package/dist/chunk-XXM4G3K5.js.map +1 -0
- package/dist/chunk-YVI7J43Q.js +29 -0
- package/dist/chunk-YVI7J43Q.js.map +1 -0
- package/dist/chunk-Z2OF3RKT.js +152 -0
- package/dist/chunk-Z2OF3RKT.js.map +1 -0
- package/dist/{controller-SkwGkVE8.d.ts → controller-Ag3nFtwP.d.ts} +16 -24
- package/dist/{controller-element-CkXIA6w2.d.ts → controller-element-Cn5cxKnD.d.ts} +4 -2
- package/dist/core.d.ts +2 -2
- package/dist/core.js +2 -2
- package/dist/index.d.ts +6 -3
- package/dist/index.js +21 -14
- package/dist/index.js.map +1 -1
- package/dist/media.d.ts +3 -3
- package/dist/media.js +11 -5
- package/dist/media.js.map +1 -1
- package/dist/player.d.ts +8 -2
- package/dist/player.js +3 -5
- package/dist/player.js.map +1 -1
- package/dist/ramp-2XKALEGA.js +98 -0
- package/dist/ramp-2XKALEGA.js.map +1 -0
- package/dist/transcript-element.d.ts +2 -2
- package/dist/transcript-element.js +6 -5
- package/dist/transcript-element.js.map +1 -1
- package/docs/API.md +70 -45
- package/package.json +18 -11
- package/dist/chunk-4NOWGMI5.js.map +0 -1
- package/dist/chunk-BAE4FKBJ.js +0 -645
- package/dist/chunk-BAE4FKBJ.js.map +0 -1
- package/dist/chunk-C4H46HDS.js +0 -95
- package/dist/chunk-C4H46HDS.js.map +0 -1
- package/dist/chunk-CDLYT3KB.js +0 -17
- package/dist/chunk-CDLYT3KB.js.map +0 -1
- package/dist/chunk-CIAQZ2EB.js.map +0 -1
- package/dist/chunk-EHIKFRFV.js +0 -75
- package/dist/chunk-EHIKFRFV.js.map +0 -1
- package/dist/chunk-FVYEV2LO.js +0 -163
- package/dist/chunk-FVYEV2LO.js.map +0 -1
- package/dist/chunk-GQD675FA.js +0 -289
- package/dist/chunk-GQD675FA.js.map +0 -1
- package/dist/chunk-M5ZZRBGD.js +0 -357
- package/dist/chunk-M5ZZRBGD.js.map +0 -1
- package/dist/chunk-YX32ZQPR.js +0 -83
- 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
|
-
|
|
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>`
|
|
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
|
|
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-
|
|
169
|
+
--mango-av-list-height: 30rem;
|
|
173
170
|
}
|
|
174
171
|
```
|
|
175
172
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
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
|
-
|
|
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
|
|
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.
|
package/dist/annotations.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { A as AVControllerElement } from './controller-element-
|
|
2
|
-
import {
|
|
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;
|
package/dist/annotations.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
MangoAVAnnotationsElement,
|
|
3
3
|
defineMangoAVAnnotations
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import "./chunk-
|
|
6
|
-
import "./chunk-
|
|
7
|
-
import "./chunk-
|
|
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 };
|
package/dist/captions.js
ADDED
|
@@ -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":[]}
|
package/dist/chapters.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { A as AVControllerElement } from './controller-element-
|
|
2
|
-
import {
|
|
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-
|
|
5
|
-
import "./chunk-
|
|
6
|
-
import "./chunk-
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
|
|
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: "
|
|
92
|
-
|
|
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,
|
|
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,
|
|
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-
|
|
1052
|
+
//# sourceMappingURL=chunk-53ZN4Y4L.js.map
|