diff options
author | Rigel Kent <sendmemail@rigelk.eu> | 2019-12-17 16:49:33 +0100 |
---|---|---|
committer | Rigel Kent <sendmemail@rigelk.eu> | 2019-12-17 16:49:33 +0100 |
commit | 3bcb4fd74190aecb22c39a88772e80b223a87472 (patch) | |
tree | ea57f42187dd8e5106908bd9e5503d73fe3ddf64 /client/src/assets/player | |
parent | d6ed9ccc819eba813b3bf8e1f8829117d43382b2 (diff) | |
download | PeerTube-3bcb4fd74190aecb22c39a88772e80b223a87472.tar.gz PeerTube-3bcb4fd74190aecb22c39a88772e80b223a87472.tar.zst PeerTube-3bcb4fd74190aecb22c39a88772e80b223a87472.zip |
add 'up next' screen on autoplay
Diffstat (limited to 'client/src/assets/player')
-rw-r--r-- | client/src/assets/player/peertube-player-manager.ts | 1 | ||||
-rw-r--r-- | client/src/assets/player/upnext/upnext-plugin.ts | 169 |
2 files changed, 170 insertions, 0 deletions
diff --git a/client/src/assets/player/peertube-player-manager.ts b/client/src/assets/player/peertube-player-manager.ts index d10fb7a4a..2f4e0ac1a 100644 --- a/client/src/assets/player/peertube-player-manager.ts +++ b/client/src/assets/player/peertube-player-manager.ts | |||
@@ -5,6 +5,7 @@ import 'videojs-hotkeys' | |||
5 | import 'videojs-dock' | 5 | import 'videojs-dock' |
6 | import 'videojs-contextmenu-ui' | 6 | import 'videojs-contextmenu-ui' |
7 | import 'videojs-contrib-quality-levels' | 7 | import 'videojs-contrib-quality-levels' |
8 | import './upnext/upnext-plugin' | ||
8 | import './peertube-plugin' | 9 | import './peertube-plugin' |
9 | import './videojs-components/peertube-link-button' | 10 | import './videojs-components/peertube-link-button' |
10 | import './videojs-components/resolution-menu-button' | 11 | import './videojs-components/resolution-menu-button' |
diff --git a/client/src/assets/player/upnext/upnext-plugin.ts b/client/src/assets/player/upnext/upnext-plugin.ts new file mode 100644 index 000000000..1f0705481 --- /dev/null +++ b/client/src/assets/player/upnext/upnext-plugin.ts | |||
@@ -0,0 +1,169 @@ | |||
1 | // @ts-ignore | ||
2 | import * as videojs from 'video.js' | ||
3 | import { VideoJSComponentInterface } from '../peertube-videojs-typings' | ||
4 | |||
5 | function getMainTemplate (options: any) { | ||
6 | return ` | ||
7 | <div class="vjs-upnext-top"> | ||
8 | <span class="vjs-upnext-headtext">${options.headText}</span> | ||
9 | <div class="vjs-upnext-title"></div> | ||
10 | </div> | ||
11 | <div class="vjs-upnext-autoplay-icon"> | ||
12 | <svg height="100%" version="1.1" viewbox="0 0 98 98" width="100%"> | ||
13 | <circle class="vjs-upnext-svg-autoplay-circle" cx="49" cy="49" fill="#000" fill-opacity="0.8" r="48"></circle> | ||
14 | <circle class="vjs-upnext-svg-autoplay-ring" cx="-49" cy="49" fill-opacity="0" r="46.5" stroke="#FFFFFF" stroke-width="4" transform="rotate(-90)"></circle> | ||
15 | <polygon class="vjs-upnext-svg-autoplay-triangle" fill="#fff" points="32,27 72,49 32,71"></polygon></svg> | ||
16 | </div> | ||
17 | <span class="vjs-upnext-bottom"> | ||
18 | <span class="vjs-upnext-cancel"> | ||
19 | <button class="vjs-upnext-cancel-button" tabindex="0" aria-label="Cancel autoplay">${options.cancelText}</button> | ||
20 | </span> | ||
21 | </span> | ||
22 | ` | ||
23 | } | ||
24 | |||
25 | // @ts-ignore-start | ||
26 | const Component = videojs.getComponent('Component') | ||
27 | class EndCard extends Component { | ||
28 | options_: any | ||
29 | getTitle: Function | ||
30 | next: Function | ||
31 | condition: Function | ||
32 | dashOffsetTotal = 586 | ||
33 | dashOffsetStart = 293 | ||
34 | interval = 50 | ||
35 | upNextEvents = new videojs.EventTarget() | ||
36 | chunkSize: number | ||
37 | |||
38 | container: HTMLElement | ||
39 | title: HTMLElement | ||
40 | autoplayRing: HTMLElement | ||
41 | cancelButton: HTMLElement | ||
42 | nextButton: HTMLElement | ||
43 | |||
44 | constructor (player: videojs.Player, options: any) { | ||
45 | super(player, options) | ||
46 | this.options_ = options | ||
47 | |||
48 | this.getTitle = this.options_.getTitle | ||
49 | this.next = this.options_.next | ||
50 | this.condition = this.options_.condition | ||
51 | |||
52 | this.chunkSize = (this.dashOffsetTotal - this.dashOffsetStart) / (this.options_.timeout / this.interval) | ||
53 | |||
54 | player.on('ended', (_: any) => { | ||
55 | if (!this.condition()) return | ||
56 | |||
57 | player.addClass('vjs-upnext--showing') | ||
58 | this.showCard((canceled: boolean) => { | ||
59 | player.removeClass('vjs-upnext--showing') | ||
60 | this.container.style.display = 'none' | ||
61 | if (!canceled) { | ||
62 | this.next() | ||
63 | } | ||
64 | }) | ||
65 | }) | ||
66 | |||
67 | player.on('playing', () => { | ||
68 | this.upNextEvents.trigger('playing') | ||
69 | }) | ||
70 | } | ||
71 | |||
72 | createEl () { | ||
73 | const container = super.createEl('div', { | ||
74 | className: 'vjs-upnext-content', | ||
75 | innerHTML: getMainTemplate(this.options_) | ||
76 | }) | ||
77 | |||
78 | this.container = container | ||
79 | container.style.display = 'none' | ||
80 | |||
81 | this.autoplayRing = container.getElementsByClassName('vjs-upnext-svg-autoplay-ring')[0] | ||
82 | this.title = container.getElementsByClassName('vjs-upnext-title')[0] | ||
83 | this.cancelButton = container.getElementsByClassName('vjs-upnext-cancel-button')[0] | ||
84 | this.nextButton = container.getElementsByClassName('vjs-upnext-autoplay-icon')[0] | ||
85 | |||
86 | this.cancelButton.onclick = () => { | ||
87 | this.upNextEvents.trigger('cancel') | ||
88 | } | ||
89 | |||
90 | this.nextButton.onclick = () => { | ||
91 | this.upNextEvents.trigger('next') | ||
92 | } | ||
93 | |||
94 | return container | ||
95 | } | ||
96 | |||
97 | showCard (cb: Function) { | ||
98 | let timeout: any | ||
99 | let start: number | ||
100 | let now: number | ||
101 | let newOffset: number | ||
102 | |||
103 | this.autoplayRing.setAttribute('stroke-dasharray', this.dashOffsetStart) | ||
104 | this.autoplayRing.setAttribute('stroke-dashoffset', -this.dashOffsetStart) | ||
105 | |||
106 | this.title.innerHTML = this.getTitle() | ||
107 | |||
108 | this.upNextEvents.one('cancel', () => { | ||
109 | clearTimeout(timeout) | ||
110 | cb(true) | ||
111 | }) | ||
112 | |||
113 | this.upNextEvents.one('playing', () => { | ||
114 | clearTimeout(timeout) | ||
115 | cb(true) | ||
116 | }) | ||
117 | |||
118 | this.upNextEvents.one('next', () => { | ||
119 | clearTimeout(timeout) | ||
120 | cb(false) | ||
121 | }) | ||
122 | |||
123 | const update = () => { | ||
124 | now = this.options_.timeout - (new Date().getTime() - start) | ||
125 | |||
126 | if (now <= 0) { | ||
127 | clearTimeout(timeout) | ||
128 | cb(false) | ||
129 | } else { | ||
130 | newOffset = Math.max(-this.dashOffsetTotal, this.autoplayRing.getAttribute('stroke-dashoffset') - this.chunkSize) | ||
131 | this.autoplayRing.setAttribute('stroke-dashoffset', newOffset) | ||
132 | timeout = setTimeout(update.bind(this), this.interval) | ||
133 | } | ||
134 | |||
135 | } | ||
136 | |||
137 | this.container.style.display = 'block' | ||
138 | start = new Date().getTime() | ||
139 | timeout = setTimeout(update.bind(this), this.interval) | ||
140 | } | ||
141 | } | ||
142 | // @ts-ignore-end | ||
143 | |||
144 | videojs.registerComponent('EndCard', EndCard) | ||
145 | |||
146 | const Plugin: VideoJSComponentInterface = videojs.getPlugin('plugin') | ||
147 | class UpNextPlugin extends Plugin { | ||
148 | constructor (player: videojs.Player, options: any = {}) { | ||
149 | const settings = { | ||
150 | next: options.next, | ||
151 | getTitle: options.getTitle, | ||
152 | timeout: options.timeout || 5000, | ||
153 | cancelText: options.cancelText || 'Cancel', | ||
154 | headText: options.headText || 'Up Next', | ||
155 | condition: options.condition | ||
156 | } | ||
157 | |||
158 | super(player, settings) | ||
159 | |||
160 | this.player.ready(() => { | ||
161 | player.addClass('vjs-upnext') | ||
162 | }) | ||
163 | |||
164 | player.addChild('EndCard', settings) | ||
165 | } | ||
166 | } | ||
167 | |||
168 | videojs.registerPlugin('upnext', UpNextPlugin) | ||
169 | export { UpNextPlugin } | ||