@banou/media-player 0.10.0 → 0.10.1

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/README.md CHANGED
@@ -150,10 +150,39 @@ chrome at once.
150
150
 
151
151
  ## What it does
152
152
 
153
- Play and pause, seek with a preview thumbnail and a keyframe-accurate scrub, volume on a log curve,
154
- mute, playback speed, audio track selection, subtitle track selection, picture in picture, fullscreen,
155
- and keyboard shortcuts. Nothing is persisted: volume, speed and track choices start at their defaults
156
- every load.
153
+ Play and pause, seek with a preview thumbnail and a keyframe-accurate scrub, chapters on the seekbar,
154
+ an offer to skip an opening or ending, volume on a log curve, mute, playback speed, audio track
155
+ selection, subtitle track selection, picture in picture, fullscreen, and keyboard shortcuts. Nothing
156
+ is persisted: volume, speed and track choices start at their defaults every load.
157
+
158
+ ### Chapters
159
+
160
+ Chapters come from the container, and the seekbar draws them as segments with a break at each
161
+ boundary. Hovering one lifts it above its neighbours and names it beside the time in the preview.
162
+
163
+ Pass `chapters` to supply them yourself, on either arm, and they win over whatever the file declared:
164
+
165
+ ```tsx
166
+ <MediaPlayer {...source} chapters={[{ start: 0, end: 88, title: 'Opening' }]} />
167
+ ```
168
+
169
+ They are `{ start, end, title }` in seconds, ordered and non-overlapping, and they need not cover the
170
+ whole duration. A local file gets them from libav automatically, so this is for a source that knows
171
+ chapters the container does not.
172
+
173
+ ### Skip Opening and Skip Ending
174
+
175
+ When a chapter looks like an opening or an ending, a button offers to jump past it, appearing a
176
+ second before the chapter and staying six seconds. It never skips on its own, so a wrong guess costs
177
+ a button nobody presses rather than a jump out of the episode.
178
+
179
+ The guess is made from the chapter title, against the names releases actually use (`OP`, `Opening`,
180
+ `ED`, `Ending`, `Credits`, and the same names carrying a song), and it defers across the whole file:
181
+ `Intro` is the opening where nothing else claims to be, and the cold open where an `OP` follows it.
182
+ A file whose chapters carry no usable name at all falls back to shape, where a chapter of about
183
+ ninety seconds in each half of the runtime is the opening and the ending. Nothing shorter than
184
+ fifteen seconds is ever offered, and a disc that is mostly themes offers nothing, because there the
185
+ themes are what is being watched.
157
186
 
158
187
  ### Picture in picture keeps the subtitles
159
188
 
@@ -1,5 +1,5 @@
1
1
  export { startPlayback, terminateRemuxer, MediaElementError, isMediaElementError, DEFAULT_BUFFER_SIZE } from './playback';
2
- export type { PlaybackOptions, PlaybackController, MediaIndex, AudioStream } from './playback';
2
+ export type { PlaybackOptions, PlaybackController, MediaIndex, MediaChapter, AudioStream } from './playback';
3
3
  export { createSubtitleRenderer, SUBTITLES_OFF } from './subtitles';
4
4
  export type { SubtitleRenderer, SubtitleRendererOptions, SubtitleStream } from './subtitles';
5
5
  export { createThumbnailGenerator } from './thumbnails';
@@ -1,2 +1,2 @@
1
- import { a as e, c as t, d as n, f as r, i, l as a, n as o, o as s, r as c, s as l, t as u, u as d } from "../engine-YeMWShCv.js";
1
+ import { a as e, c as t, d as n, f as r, i, l as a, n as o, o as s, r as c, s as l, t as u, u as d } from "../engine-b5_5HscR.js";
2
2
  export { i as DEFAULT_BUFFER_SIZE, e as MediaElementError, a as SUBTITLES_OFF, u as createPictureInPicture, d as createSubtitleRenderer, c as createThumbnailGenerator, n as getTimeRanges, s as isMediaElementError, o as pictureInPictureMode, l as startPlayback, t as terminateRemuxer, r as updateSourceBuffer };
@@ -6,6 +6,21 @@ export type MediaIndex = {
6
6
  pos: number;
7
7
  timestamp: number;
8
8
  };
9
+ /**
10
+ * One named span of the timeline, in SECONDS.
11
+ *
12
+ * Chapters are not required to tile the duration: a container routinely declares a last chapter that
13
+ * ends fractionally before the file does, and nothing guarantees the first starts at zero. Anything
14
+ * drawing them has to treat the gaps as ordinary un-named time rather than assume full cover.
15
+ *
16
+ * libav also reports an `index`, dropped here the way `MediaIndex` drops it: array position already
17
+ * carries it, and a caller supplying their own chapters should not have to number them.
18
+ */
19
+ export type MediaChapter = {
20
+ start: number;
21
+ end: number;
22
+ title: string;
23
+ };
9
24
  export type PlaybackOptions = {
10
25
  videoElement: HTMLVideoElement;
11
26
  /**
@@ -49,6 +64,8 @@ export type PlaybackController = {
49
64
  selectSubtitleStream: (streamIndex: number | undefined) => void;
50
65
  /** Keyframe index of the input, which is what maps a downloaded byte range onto the timeline. */
51
66
  indexes: MediaIndex[];
67
+ /** Named spans the container declared, empty when it declared none. */
68
+ chapters: MediaChapter[];
52
69
  duration: number;
53
70
  videoMimeType: string;
54
71
  audioMimeType: string;
@@ -374,6 +374,7 @@ var i = (e) => Array(e.buffered.length).fill(void 0).map((t, n) => ({
374
374
  videoElement: n,
375
375
  selectSubtitleStream: (e) => s.selectStream(e),
376
376
  indexes: e.indexes ?? [],
377
+ chapters: e.chapters ?? [],
377
378
  duration: e.info.input.duration,
378
379
  videoMimeType: e.info.output.videoMimeType,
379
380
  audioMimeType: e.info.output.audioMimeType
package/dist/index.d.ts CHANGED
@@ -10,5 +10,5 @@ export { useSeekThumbnails } from './react/hooks/use-thumbnails';
10
10
  export { usePictureInPicture } from './react/hooks/use-picture-in-picture';
11
11
  export { inputToRemuxerInput } from './utils/source';
12
12
  export type { RemuxerInput } from './utils/source';
13
- export type { AudioStream, MediaIndex, PictureInPictureController, PlaybackController, PlaybackOptions, SubtitleStream, ThumbnailImage, } from './engine';
13
+ export type { AudioStream, MediaChapter, MediaIndex, PictureInPictureController, PlaybackController, PlaybackOptions, SubtitleStream, ThumbnailImage, } from './engine';
14
14
  export { startPlayback, createThumbnailGenerator, createSubtitleRenderer, createPictureInPicture, SUBTITLES_OFF, } from './engine';