Add previous button

pull/3047/head
Chocobozzz 2020-08-05 11:02:14 +02:00 committed by Chocobozzz
parent 4572c3d0d9
commit a950e4c82b
7 changed files with 229 additions and 60 deletions

View File

@ -1,4 +1,59 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24px" height="24px" viewBox="0 0 36 36" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path fill="white" d="M 12,24 20.5,18 12,12 V 24 z M 22,12 v 12 h 2 V 12 h -2 z"></path>
</svg>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"
sodipodi:docname="next.svg"
id="svg4"
version="1.1"
viewBox="0 0 12 12"
height="8"
width="8">
<metadata
id="metadata10">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs8" />
<sodipodi:namedview
inkscape:current-layer="svg4"
inkscape:window-maximized="1"
inkscape:window-y="0"
inkscape:window-x="0"
inkscape:cy="-2.5620165"
inkscape:cx="-7.4038126"
inkscape:zoom="29.791667"
fit-margin-bottom="0"
fit-margin-right="0"
fit-margin-left="0"
fit-margin-top="0"
showgrid="false"
id="namedview6"
inkscape:window-height="1037"
inkscape:window-width="1916"
inkscape:pageshadow="2"
inkscape:pageopacity="0"
guidetolerance="10"
gridtolerance="10"
objecttolerance="10"
borderopacity="1"
bordercolor="#666666"
pagecolor="#ffffff" />
<path
id="path2"
d="M 0,12 8.5,6 0,0 Z M 10,0 v 12 h 2 V 0 Z"
fill="#ffffff" />
</svg>

Before

Width:  |  Height:  |  Size: 283 B

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -6,7 +6,7 @@ import './upnext/end-card'
import './upnext/upnext-plugin'
import './bezels/bezels-plugin'
import './peertube-plugin'
import './videojs-components/next-video-button'
import './videojs-components/next-previous-video-button'
import './videojs-components/p2p-info-button'
import './videojs-components/peertube-link-button'
import './videojs-components/peertube-load-progress-bar'
@ -27,6 +27,7 @@ import { segmentUrlBuilderFactory } from './p2p-media-loader/segment-url-builder
import { segmentValidatorFactory } from './p2p-media-loader/segment-validator'
import { getStoredP2PEnabled } from './peertube-player-local-storage'
import {
NextPreviousVideoButtonOptions,
P2PMediaLoaderPluginOptions,
PlaylistPluginOptions,
UserWatching,
@ -77,7 +78,12 @@ export interface CommonOptions extends CustomizationOptions {
onPlayerElementChange: (element: HTMLVideoElement) => void
autoplay: boolean
nextVideo?: Function
nextVideo?: () => void
hasNextVideo?: () => boolean
previousVideo?: () => void
hasPreviousVideo?: () => boolean
playlist?: PlaylistPluginOptions
@ -259,7 +265,12 @@ export class PeertubePlayerManager {
captions: commonOptions.captions,
peertubeLink: commonOptions.peertubeLink,
theaterButton: commonOptions.theaterButton,
nextVideo: commonOptions.nextVideo
nextVideo: commonOptions.nextVideo,
hasNextVideo: commonOptions.hasNextVideo,
previousVideo: commonOptions.previousVideo,
hasPreviousVideo: commonOptions.hasPreviousVideo
}) as any // FIXME: typings
}
}
@ -360,9 +371,14 @@ export class PeertubePlayerManager {
private static getControlBarChildren (mode: PlayerMode, options: {
peertubeLink: boolean
theaterButton: boolean,
captions: boolean,
theaterButton: boolean
captions: boolean
nextVideo?: Function
hasNextVideo?: () => boolean
previousVideo?: Function
hasPreviousVideo?: () => boolean
}) {
const settingEntries = []
const loadProgressBar = mode === 'webtorrent' ? 'peerTubeLoadProgressBar' : 'loadProgressBar'
@ -372,15 +388,39 @@ export class PeertubePlayerManager {
if (options.captions === true) settingEntries.push('captionsButton')
settingEntries.push('resolutionMenuButton')
const children = {
'playToggle': {}
const children = {}
if (options.previousVideo) {
const buttonOptions: NextPreviousVideoButtonOptions = {
type: 'previous',
handler: options.previousVideo,
isDisabled: () => {
if (!options.hasPreviousVideo) return false
return !options.hasPreviousVideo()
}
}
Object.assign(children, {
'previousVideoButton': buttonOptions
})
}
Object.assign(children, { playToggle: {} })
if (options.nextVideo) {
Object.assign(children, {
'nextVideoButton': {
handler: options.nextVideo
const buttonOptions: NextPreviousVideoButtonOptions = {
type: 'next',
handler: options.nextVideo,
isDisabled: () => {
if (!options.hasNextVideo) return false
return !options.hasNextVideo()
}
}
Object.assign(children, {
'nextVideoButton': buttonOptions
})
}

View File

@ -118,6 +118,12 @@ type PlaylistPluginOptions = {
onItemClicked: (element: VideoPlaylistElement) => void
}
type NextPreviousVideoButtonOptions = {
type: 'next' | 'previous'
handler: Function
isDisabled: () => boolean
}
type WebtorrentPluginOptions = {
playerElement: HTMLVideoElement
@ -194,6 +200,7 @@ type PlaylistItemOptions = {
export {
PlayerNetworkInfo,
PlaylistItemOptions,
NextPreviousVideoButtonOptions,
ResolutionUpdateData,
AutoResolutionUpdateData,
PlaylistPluginOptions,

View File

@ -0,0 +1,50 @@
import videojs from 'video.js'
import { NextPreviousVideoButtonOptions } from '../peertube-videojs-typings'
const Button = videojs.getComponent('Button')
class NextPreviousVideoButton extends Button {
private readonly nextPreviousVideoButtonOptions: NextPreviousVideoButtonOptions
constructor (player: videojs.Player, options?: NextPreviousVideoButtonOptions) {
super(player, options as any)
this.nextPreviousVideoButtonOptions = options
this.update()
}
createEl () {
const type = (this.options_ as NextPreviousVideoButtonOptions).type
const button = videojs.dom.createEl('button', {
className: 'vjs-' + type + '-video'
}) as HTMLButtonElement
const nextIcon = videojs.dom.createEl('span', {
className: 'icon icon-' + type
})
button.appendChild(nextIcon)
if (type === 'next') {
button.title = this.player_.localize('Next video')
} else {
button.title = this.player_.localize('Previous video')
}
return button
}
handleClick () {
this.nextPreviousVideoButtonOptions.handler()
}
update () {
const disabled = this.nextPreviousVideoButtonOptions.isDisabled()
if (disabled) this.addClass('vjs-disabled')
else this.removeClass('vjs-disabled')
}
}
videojs.registerComponent('NextVideoButton', NextPreviousVideoButton)
videojs.registerComponent('PreviousVideoButton', NextPreviousVideoButton)

View File

@ -1,37 +0,0 @@
import videojs from 'video.js'
const Button = videojs.getComponent('Button')
export interface NextVideoButtonOptions extends videojs.ComponentOptions {
handler: Function
}
class NextVideoButton extends Button {
private readonly nextVideoButtonOptions: NextVideoButtonOptions
constructor (player: videojs.Player, options?: NextVideoButtonOptions) {
super(player, options)
this.nextVideoButtonOptions = options
}
createEl () {
const button = videojs.dom.createEl('button', {
className: 'vjs-next-video'
}) as HTMLButtonElement
const nextIcon = videojs.dom.createEl('span', {
className: 'icon icon-next'
})
button.appendChild(nextIcon)
button.title = this.player_.localize('Next video')
return button
}
handleClick () {
this.nextVideoButtonOptions.handler()
}
}
videojs.registerComponent('NextVideoButton', NextVideoButton)

View File

@ -147,6 +147,10 @@ body {
box-shadow: 0 -15px 40px 10px rgba(0, 0, 0, 0.2);
text-shadow: 0 0 2px rgba(0, 0, 0, 0.5);
> button:first-child {
margin-left: 1em;
}
.vjs-progress-control,
.vjs-play-control,
.vjs-playback-rate,
@ -230,7 +234,6 @@ body {
cursor: pointer;
font-size: $font-size;
margin-left: 1em;
width: 3em;
}
@ -301,24 +304,32 @@ body {
}
}
.vjs-next-video {
.vjs-next-video,
.vjs-previous-video {
line-height: $control-bar-height;
text-align: right;
.icon {
&.icon-next {
&.icon-next,
&.icon-previous {
mask-image: url('#{$assets-path}/player/images/next.svg');
-webkit-mask-image: url('#{$assets-path}/player/images/next.svg');
background-color: white;
mask-size: cover;
-webkit-mask-size: cover;
transform: scale(2.2);
width: 11px;
height: 11px;
margin-top: -2px;
display: inline-block;
}
&.icon-previous {
transform: rotate(180deg);
}
}
}
.vjs-peertube,
.vjs-next-video {
.vjs-peertube {
.icon {
display: inline-block;
width: 15px;
@ -650,3 +661,13 @@ body {
display: block;
}
}
.vjs-no-next-in-playlist {
.vjs-next-video {
cursor: default;
.icon {
background-color: rgba(255, 255, 255, 0.5);
}
}
}

View File

@ -309,13 +309,13 @@ export class PeerTubeEmbed {
cancelText: peertubeTranslate('Cancel', translations),
suspendedText: peertubeTranslate('Autoplay is suspended', translations),
getTitle: () => this.nextVideoTitle(),
next: () => this.autoplayNext(),
next: () => this.playNextVideo(),
condition: () => !!this.getNextPlaylistElement(),
suspended: () => false
})
}
private async autoplayNext () {
private async playNextVideo () {
const next = this.getNextPlaylistElement()
if (!next) {
console.log('Next element not found in playlist.')
@ -327,6 +327,18 @@ export class PeerTubeEmbed {
return this.loadVideoAndBuildPlayer(this.currentPlaylistElement.video.uuid)
}
private async playPreviousVideo () {
const previous = this.getPreviousPlaylistElement()
if (!previous) {
console.log('Previous element not found in playlist.')
return
}
this.currentPlaylistElement = previous
return this.loadVideoAndBuildPlayer(this.currentPlaylistElement.video.uuid)
}
private async loadVideoAndBuildPlayer (uuid: string) {
const res = await this.loadVideo(uuid)
if (res === undefined) return
@ -357,6 +369,22 @@ export class PeerTubeEmbed {
return next
}
private getPreviousPlaylistElement (position?: number): VideoPlaylistElement {
if (!position) position = this.currentPlaylistElement.position -1
if (position < 1) {
return undefined
}
const prev = this.playlistElements.find(e => e.position === position)
if (!prev || !prev.video) {
return this.getNextPlaylistElement(position - 1)
}
return prev
}
private async buildVideoPlayer (videoResponse: Response, captionsPromise: Promise<Response>) {
let alreadyHadPlayer = false
@ -418,7 +446,12 @@ export class PeerTubeEmbed {
stopTime: this.stopTime,
subtitle: this.subtitle,
nextVideo: () => this.autoplayNext(),
nextVideo: this.playlist ? () => this.playNextVideo() : undefined,
hasNextVideo: this.playlist ? () => !!this.getNextPlaylistElement() : undefined,
previousVideo: this.playlist ? () => this.playPreviousVideo() : undefined,
hasPreviousVideo: this.playlist ? () => !!this.getPreviousPlaylistElement() : undefined,
playlist: playlistPlugin,
videoCaptions,