aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/assets
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2023-08-28 10:55:04 +0200
committerChocobozzz <me@florianbigard.com>2023-08-28 16:17:31 +0200
commit77b70702d2193d78bf6fbd07f0fc7335e34957f8 (patch)
tree1a0aed540054286c9a8b10c4890cc0f718e00458 /client/src/assets
parent7113f32a87bd6b2868154fed20bde1a1633c190e (diff)
downloadPeerTube-77b70702d2193d78bf6fbd07f0fc7335e34957f8.tar.gz
PeerTube-77b70702d2193d78bf6fbd07f0fc7335e34957f8.tar.zst
PeerTube-77b70702d2193d78bf6fbd07f0fc7335e34957f8.zip
Add video chapters support
Diffstat (limited to 'client/src/assets')
-rw-r--r--client/src/assets/player/peertube-player.ts7
-rw-r--r--client/src/assets/player/shared/control-bar/chapters-plugin.ts64
-rw-r--r--client/src/assets/player/shared/control-bar/index.ts2
-rw-r--r--client/src/assets/player/shared/control-bar/progress-bar-marker-component.ts24
-rw-r--r--client/src/assets/player/shared/control-bar/storyboard-plugin.ts4
-rw-r--r--client/src/assets/player/shared/control-bar/time-tooltip.ts20
-rw-r--r--client/src/assets/player/types/peertube-player-options.ts3
-rw-r--r--client/src/assets/player/types/peertube-videojs-typings.ts15
8 files changed, 136 insertions, 3 deletions
diff --git a/client/src/assets/player/peertube-player.ts b/client/src/assets/player/peertube-player.ts
index 111b4645b..192b2e124 100644
--- a/client/src/assets/player/peertube-player.ts
+++ b/client/src/assets/player/peertube-player.ts
@@ -7,6 +7,8 @@ import './shared/bezels/bezels-plugin'
7import './shared/peertube/peertube-plugin' 7import './shared/peertube/peertube-plugin'
8import './shared/resolutions/peertube-resolutions-plugin' 8import './shared/resolutions/peertube-resolutions-plugin'
9import './shared/control-bar/storyboard-plugin' 9import './shared/control-bar/storyboard-plugin'
10import './shared/control-bar/chapters-plugin'
11import './shared/control-bar/time-tooltip'
10import './shared/control-bar/next-previous-video-button' 12import './shared/control-bar/next-previous-video-button'
11import './shared/control-bar/p2p-info-button' 13import './shared/control-bar/p2p-info-button'
12import './shared/control-bar/peertube-link-button' 14import './shared/control-bar/peertube-link-button'
@@ -227,6 +229,7 @@ export class PeerTubePlayer {
227 if (this.player.usingPlugin('upnext')) this.player.upnext().dispose() 229 if (this.player.usingPlugin('upnext')) this.player.upnext().dispose()
228 if (this.player.usingPlugin('stats')) this.player.stats().dispose() 230 if (this.player.usingPlugin('stats')) this.player.stats().dispose()
229 if (this.player.usingPlugin('storyboard')) this.player.storyboard().dispose() 231 if (this.player.usingPlugin('storyboard')) this.player.storyboard().dispose()
232 if (this.player.usingPlugin('chapters')) this.player.chapters().dispose()
230 233
231 if (this.player.usingPlugin('peertubeDock')) this.player.peertubeDock().dispose() 234 if (this.player.usingPlugin('peertubeDock')) this.player.peertubeDock().dispose()
232 235
@@ -273,6 +276,10 @@ export class PeerTubePlayer {
273 this.player.storyboard(this.currentLoadOptions.storyboard) 276 this.player.storyboard(this.currentLoadOptions.storyboard)
274 } 277 }
275 278
279 if (this.currentLoadOptions.videoChapters) {
280 this.player.chapters({ chapters: this.currentLoadOptions.videoChapters })
281 }
282
276 if (this.currentLoadOptions.dock) { 283 if (this.currentLoadOptions.dock) {
277 this.player.peertubeDock(this.currentLoadOptions.dock) 284 this.player.peertubeDock(this.currentLoadOptions.dock)
278 } 285 }
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 @@
1import videojs from 'video.js'
2import { ChaptersOptions } from '../../types'
3import { VideoChapter } from '@peertube/peertube-models'
4import { ProgressBarMarkerComponent } from './progress-bar-marker-component'
5
6const Plugin = videojs.getPlugin('plugin')
7
8class 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
62videojs.registerPlugin('chapters', ChaptersPlugin)
63
64export { 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 @@
1export * from './chapters-plugin'
1export * from './next-previous-video-button' 2export * from './next-previous-video-button'
2export * from './p2p-info-button' 3export * from './p2p-info-button'
3export * from './peertube-link-button' 4export * from './peertube-link-button'
4export * from './peertube-live-display' 5export * from './peertube-live-display'
5export * from './storyboard-plugin' 6export * from './storyboard-plugin'
6export * from './theater-button' 7export * from './theater-button'
8export * 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 @@
1import videojs from 'video.js'
2import { ProgressBarMarkerComponentOptions } from '../../types'
3
4const Component = videojs.getComponent('Component')
5
6export 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
24videojs.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 @@
1import { timeToInt } from '@peertube/peertube-core-utils'
2import videojs, { VideoJsPlayer } from 'video.js'
3
4const TimeToolTip = videojs.getComponent('TimeTooltip') as any // FIXME: typings don't have write method
5
6class 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
20videojs.registerComponent('TimeTooltip', TimeTooltip)
diff --git a/client/src/assets/player/types/peertube-player-options.ts b/client/src/assets/player/types/peertube-player-options.ts
index 6fb2f7913..32f26fa9e 100644
--- a/client/src/assets/player/types/peertube-player-options.ts
+++ b/client/src/assets/player/types/peertube-player-options.ts
@@ -1,4 +1,4 @@
1import { LiveVideoLatencyModeType, VideoFile } from '@peertube/peertube-models' 1import { LiveVideoLatencyModeType, VideoChapter, VideoFile } from '@peertube/peertube-models'
2import { PluginsManager } from '@root-helpers/plugins-manager' 2import { PluginsManager } from '@root-helpers/plugins-manager'
3import { PeerTubeDockPluginOptions } from '../shared/dock/peertube-dock-plugin' 3import { PeerTubeDockPluginOptions } from '../shared/dock/peertube-dock-plugin'
4import { PlaylistPluginOptions, VideoJSCaption, VideoJSStoryboard } from './peertube-videojs-typings' 4import { PlaylistPluginOptions, VideoJSCaption, VideoJSStoryboard } from './peertube-videojs-typings'
@@ -68,6 +68,7 @@ export type PeerTubePlayerLoadOptions = {
68 } 68 }
69 69
70 videoCaptions: VideoJSCaption[] 70 videoCaptions: VideoJSCaption[]
71 videoChapters: VideoChapter[]
71 storyboard: VideoJSStoryboard 72 storyboard: VideoJSStoryboard
72 73
73 videoUUID: string 74 videoUUID: string
diff --git a/client/src/assets/player/types/peertube-videojs-typings.ts b/client/src/assets/player/types/peertube-videojs-typings.ts
index 27fbda31d..6293404ab 100644
--- a/client/src/assets/player/types/peertube-videojs-typings.ts
+++ b/client/src/assets/player/types/peertube-videojs-typings.ts
@@ -1,7 +1,7 @@
1import { HlsConfig, Level } from 'hls.js' 1import { HlsConfig, Level } from 'hls.js'
2import videojs from 'video.js' 2import videojs from 'video.js'
3import { Engine } from '@peertube/p2p-media-loader-hlsjs' 3import { Engine } from '@peertube/p2p-media-loader-hlsjs'
4import { VideoFile, VideoPlaylist, VideoPlaylistElement } from '@peertube/peertube-models' 4import { VideoChapter, VideoFile, VideoPlaylist, VideoPlaylistElement } from '@peertube/peertube-models'
5import { BezelsPlugin } from '../shared/bezels/bezels-plugin' 5import { BezelsPlugin } from '../shared/bezels/bezels-plugin'
6import { StoryboardPlugin } from '../shared/control-bar/storyboard-plugin' 6import { StoryboardPlugin } from '../shared/control-bar/storyboard-plugin'
7import { PeerTubeDockPlugin, PeerTubeDockPluginOptions } from '../shared/dock/peertube-dock-plugin' 7import { PeerTubeDockPlugin, PeerTubeDockPluginOptions } from '../shared/dock/peertube-dock-plugin'
@@ -19,6 +19,7 @@ import { UpNextPlugin } from '../shared/upnext/upnext-plugin'
19import { WebVideoPlugin } from '../shared/web-video/web-video-plugin' 19import { WebVideoPlugin } from '../shared/web-video/web-video-plugin'
20import { PlayerMode } from './peertube-player-options' 20import { PlayerMode } from './peertube-player-options'
21import { SegmentValidator } from '../shared/p2p-media-loader/segment-validator' 21import { SegmentValidator } from '../shared/p2p-media-loader/segment-validator'
22import { ChaptersPlugin } from '../shared/control-bar/chapters-plugin'
22 23
23declare module 'video.js' { 24declare module 'video.js' {
24 25
@@ -62,6 +63,8 @@ declare module 'video.js' {
62 63
63 peertubeDock (options?: PeerTubeDockPluginOptions): PeerTubeDockPlugin 64 peertubeDock (options?: PeerTubeDockPluginOptions): PeerTubeDockPlugin
64 65
66 chapters (options?: ChaptersOptions): ChaptersPlugin
67
65 upnext (options?: UpNextPluginOptions): UpNextPlugin 68 upnext (options?: UpNextPluginOptions): UpNextPlugin
66 69
67 playlist (options?: PlaylistPluginOptions): PlaylistPlugin 70 playlist (options?: PlaylistPluginOptions): PlaylistPlugin
@@ -142,6 +145,10 @@ type StoryboardOptions = {
142 interval: number 145 interval: number
143} 146}
144 147
148type ChaptersOptions = {
149 chapters: VideoChapter[]
150}
151
145type PlaylistPluginOptions = { 152type PlaylistPluginOptions = {
146 elements: VideoPlaylistElement[] 153 elements: VideoPlaylistElement[]
147 154
@@ -161,6 +168,10 @@ type UpNextPluginOptions = {
161 isSuspended: () => boolean 168 isSuspended: () => boolean
162} 169}
163 170
171type ProgressBarMarkerComponentOptions = {
172 timecode: number
173}
174
164type NextPreviousVideoButtonOptions = { 175type NextPreviousVideoButtonOptions = {
165 type: 'next' | 'previous' 176 type: 'next' | 'previous'
166 handler?: () => void 177 handler?: () => void
@@ -273,6 +284,7 @@ export {
273 NextPreviousVideoButtonOptions, 284 NextPreviousVideoButtonOptions,
274 ResolutionUpdateData, 285 ResolutionUpdateData,
275 AutoResolutionUpdateData, 286 AutoResolutionUpdateData,
287 ProgressBarMarkerComponentOptions,
276 PlaylistPluginOptions, 288 PlaylistPluginOptions,
277 MetricsPluginOptions, 289 MetricsPluginOptions,
278 VideoJSCaption, 290 VideoJSCaption,
@@ -284,5 +296,6 @@ export {
284 UpNextPluginOptions, 296 UpNextPluginOptions,
285 LoadedQualityData, 297 LoadedQualityData,
286 StoryboardOptions, 298 StoryboardOptions,
299 ChaptersOptions,
287 PeerTubeLinkButtonOptions 300 PeerTubeLinkButtonOptions
288} 301}