diff options
author | Chocobozzz <me@florianbigard.com> | 2023-08-28 10:55:04 +0200 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2023-08-28 16:17:31 +0200 |
commit | 77b70702d2193d78bf6fbd07f0fc7335e34957f8 (patch) | |
tree | 1a0aed540054286c9a8b10c4890cc0f718e00458 /client/src/assets/player/shared | |
parent | 7113f32a87bd6b2868154fed20bde1a1633c190e (diff) | |
download | PeerTube-77b70702d2193d78bf6fbd07f0fc7335e34957f8.tar.gz PeerTube-77b70702d2193d78bf6fbd07f0fc7335e34957f8.tar.zst PeerTube-77b70702d2193d78bf6fbd07f0fc7335e34957f8.zip |
Add video chapters support
Diffstat (limited to 'client/src/assets/player/shared')
5 files changed, 113 insertions, 1 deletions
diff --git a/client/src/assets/player/shared/control-bar/chapters-plugin.ts b/client/src/assets/player/shared/control-bar/chapters-plugin.ts new file mode 100644 index 000000000..5be081694 --- /dev/null +++ b/client/src/assets/player/shared/control-bar/chapters-plugin.ts | |||
@@ -0,0 +1,64 @@ | |||
1 | import videojs from 'video.js' | ||
2 | import { ChaptersOptions } from '../../types' | ||
3 | import { VideoChapter } from '@peertube/peertube-models' | ||
4 | import { ProgressBarMarkerComponent } from './progress-bar-marker-component' | ||
5 | |||
6 | const Plugin = videojs.getPlugin('plugin') | ||
7 | |||
8 | class ChaptersPlugin extends Plugin { | ||
9 | private chapters: VideoChapter[] = [] | ||
10 | private markers: ProgressBarMarkerComponent[] = [] | ||
11 | |||
12 | constructor (player: videojs.Player, options: videojs.ComponentOptions & ChaptersOptions) { | ||
13 | super(player, options) | ||
14 | |||
15 | this.chapters = options.chapters | ||
16 | |||
17 | this.player.ready(() => { | ||
18 | player.addClass('vjs-chapters') | ||
19 | |||
20 | this.player.one('durationchange', () => { | ||
21 | for (const chapter of this.chapters) { | ||
22 | if (chapter.timecode === 0) continue | ||
23 | |||
24 | const marker = new ProgressBarMarkerComponent(player, { timecode: chapter.timecode }) | ||
25 | |||
26 | this.markers.push(marker) | ||
27 | this.getSeekBar().addChild(marker) | ||
28 | } | ||
29 | }) | ||
30 | }) | ||
31 | } | ||
32 | |||
33 | dispose () { | ||
34 | for (const marker of this.markers) { | ||
35 | this.getSeekBar().removeChild(marker) | ||
36 | } | ||
37 | } | ||
38 | |||
39 | getChapter (timecode: number) { | ||
40 | if (this.chapters.length !== 0) { | ||
41 | for (let i = this.chapters.length - 1; i >= 0; i--) { | ||
42 | const chapter = this.chapters[i] | ||
43 | |||
44 | if (chapter.timecode <= timecode) { | ||
45 | this.player.addClass('has-chapter') | ||
46 | |||
47 | return chapter.title | ||
48 | } | ||
49 | } | ||
50 | } | ||
51 | |||
52 | this.player.removeClass('has-chapter') | ||
53 | |||
54 | return '' | ||
55 | } | ||
56 | |||
57 | private getSeekBar () { | ||
58 | return this.player.getDescendant('ControlBar', 'ProgressControl', 'SeekBar') | ||
59 | } | ||
60 | } | ||
61 | |||
62 | videojs.registerPlugin('chapters', ChaptersPlugin) | ||
63 | |||
64 | export { ChaptersPlugin } | ||
diff --git a/client/src/assets/player/shared/control-bar/index.ts b/client/src/assets/player/shared/control-bar/index.ts index 9307027f6..091e876e2 100644 --- a/client/src/assets/player/shared/control-bar/index.ts +++ b/client/src/assets/player/shared/control-bar/index.ts | |||
@@ -1,6 +1,8 @@ | |||
1 | export * from './chapters-plugin' | ||
1 | export * from './next-previous-video-button' | 2 | export * from './next-previous-video-button' |
2 | export * from './p2p-info-button' | 3 | export * from './p2p-info-button' |
3 | export * from './peertube-link-button' | 4 | export * from './peertube-link-button' |
4 | export * from './peertube-live-display' | 5 | export * from './peertube-live-display' |
5 | export * from './storyboard-plugin' | 6 | export * from './storyboard-plugin' |
6 | export * from './theater-button' | 7 | export * from './theater-button' |
8 | export * from './time-tooltip' | ||
diff --git a/client/src/assets/player/shared/control-bar/progress-bar-marker-component.ts b/client/src/assets/player/shared/control-bar/progress-bar-marker-component.ts new file mode 100644 index 000000000..50965ec71 --- /dev/null +++ b/client/src/assets/player/shared/control-bar/progress-bar-marker-component.ts | |||
@@ -0,0 +1,24 @@ | |||
1 | import videojs from 'video.js' | ||
2 | import { ProgressBarMarkerComponentOptions } from '../../types' | ||
3 | |||
4 | const Component = videojs.getComponent('Component') | ||
5 | |||
6 | export class ProgressBarMarkerComponent extends Component { | ||
7 | options_: ProgressBarMarkerComponentOptions & videojs.ComponentOptions | ||
8 | |||
9 | // eslint-disable-next-line @typescript-eslint/no-useless-constructor | ||
10 | constructor (player: videojs.Player, options?: ProgressBarMarkerComponentOptions & videojs.ComponentOptions) { | ||
11 | super(player, options) | ||
12 | } | ||
13 | |||
14 | createEl () { | ||
15 | const left = (this.options_.timecode / this.player().duration()) * 100 | ||
16 | |||
17 | return videojs.dom.createEl('span', { | ||
18 | className: 'vjs-marker', | ||
19 | style: `left: ${left}%` | ||
20 | }) as HTMLButtonElement | ||
21 | } | ||
22 | } | ||
23 | |||
24 | videojs.registerComponent('ProgressBarMarkerComponent', ProgressBarMarkerComponent) | ||
diff --git a/client/src/assets/player/shared/control-bar/storyboard-plugin.ts b/client/src/assets/player/shared/control-bar/storyboard-plugin.ts index 80c69b5f2..91d7f451e 100644 --- a/client/src/assets/player/shared/control-bar/storyboard-plugin.ts +++ b/client/src/assets/player/shared/control-bar/storyboard-plugin.ts | |||
@@ -141,7 +141,9 @@ class StoryboardPlugin extends Plugin { | |||
141 | const ctop = Math.floor(position / columns) * -scaledHeight | 141 | const ctop = Math.floor(position / columns) * -scaledHeight |
142 | 142 | ||
143 | const bgSize = `${imgWidth * scaleFactor}px ${imgHeight * scaleFactor}px` | 143 | const bgSize = `${imgWidth * scaleFactor}px ${imgHeight * scaleFactor}px` |
144 | const topOffset = -scaledHeight - 60 | 144 | |
145 | const timeTooltip = this.player.el().querySelector('.vjs-time-tooltip') | ||
146 | const topOffset = -scaledHeight + parseInt(getComputedStyle(timeTooltip).top.replace('px', '')) - 20 | ||
145 | 147 | ||
146 | const previewHalfSize = Math.round(scaledWidth / 2) | 148 | const previewHalfSize = Math.round(scaledWidth / 2) |
147 | let left = seekBarRect.width * seekBarX - previewHalfSize | 149 | let left = seekBarRect.width * seekBarX - previewHalfSize |
diff --git a/client/src/assets/player/shared/control-bar/time-tooltip.ts b/client/src/assets/player/shared/control-bar/time-tooltip.ts new file mode 100644 index 000000000..2ed4f9acd --- /dev/null +++ b/client/src/assets/player/shared/control-bar/time-tooltip.ts | |||
@@ -0,0 +1,20 @@ | |||
1 | import { timeToInt } from '@peertube/peertube-core-utils' | ||
2 | import videojs, { VideoJsPlayer } from 'video.js' | ||
3 | |||
4 | const TimeToolTip = videojs.getComponent('TimeTooltip') as any // FIXME: typings don't have write method | ||
5 | |||
6 | class TimeTooltip extends TimeToolTip { | ||
7 | |||
8 | write (timecode: string) { | ||
9 | const player: VideoJsPlayer = this.player() | ||
10 | |||
11 | if (player.usingPlugin('chapters')) { | ||
12 | const chapterTitle = player.chapters().getChapter(timeToInt(timecode)) | ||
13 | if (chapterTitle) return super.write(chapterTitle + '\r\n' + timecode) | ||
14 | } | ||
15 | |||
16 | return super.write(timecode) | ||
17 | } | ||
18 | } | ||
19 | |||
20 | videojs.registerComponent('TimeTooltip', TimeTooltip) | ||