Add ability to set password from embed API

This commit is contained in:
Chocobozzz 2023-11-23 08:14:54 +01:00
parent 4c07200d64
commit b13460a10a
No known key found for this signature in database
GPG key ID: 583A612D890159BE
8 changed files with 127 additions and 45 deletions

View file

@ -49,20 +49,6 @@ function isMobile () {
return /iPhone|iPad|iPod|Android/i.test(navigator.userAgent)
}
function buildVideoOrPlaylistEmbed (embedUrl: string, embedTitle: string) {
const iframe = document.createElement('iframe')
iframe.title = embedTitle
iframe.width = '560'
iframe.height = '315'
iframe.src = embedUrl
iframe.frameBorder = '0'
iframe.allowFullscreen = true
iframe.sandbox.add('allow-same-origin', 'allow-scripts', 'allow-popups')
return iframe.outerHTML
}
function videoFileMaxByResolution (files: VideoFile[]) {
let max = files[0]
@ -106,7 +92,6 @@ export {
isWebRTCDisabled,
isP2PEnabled,
buildVideoOrPlaylistEmbed,
videoFileMaxByResolution,
videoFileMinByResolution,
isMobile,

View file

@ -195,6 +195,15 @@ export class PeerTubePlayer {
return this.sendMessage<undefined, number>('getCurrentPosition')
}
/**
* Set video password to so the user doesn't have to manually fill it
*
* @param password
*/
async setVideoPassword (password: string): Promise<void> {
await this.sendMessage('setVideoPassword', password)
}
private constructChannel () {
this.channel = Channel.build({
window: this.embedElement.contentWindow,

View file

@ -25,16 +25,15 @@ export class PeerTubeEmbedApi {
initialize () {
this.constructChannel()
this.setupStateTracking()
// We're ready!
this.notifyReady()
}
reInit () {
initWithVideo () {
this.disposeStateTracking()
this.setupStateTracking()
if (!this.isReady) {
this.notifyReady()
}
}
private get element () {
@ -44,6 +43,8 @@ export class PeerTubeEmbedApi {
private constructChannel () {
const channel = Channel.build({ window: window.parent, origin: '*', scope: this.embed.getScope() })
channel.bind('setVideoPassword', (txn, value) => this.embed.setVideoPasswordByAPI(value))
channel.bind('play', (txn, params) => this.embed.player.play())
channel.bind('pause', (txn, params) => this.embed.player.pause())
channel.bind('seek', (txn, time) => this.embed.player.currentTime(time))
@ -66,6 +67,7 @@ export class PeerTubeEmbedApi {
channel.bind('playNextVideo', (txn, params) => this.embed.playNextPlaylistVideo())
channel.bind('playPreviousVideo', (txn, params) => this.embed.playPreviousPlaylistVideo())
channel.bind('getCurrentPosition', (txn, params) => this.embed.getCurrentPlaylistPosition())
this.channel = channel
}

View file

@ -53,6 +53,8 @@ export class PeerTubeEmbed {
private alreadyPlayed = false
private videoPassword: string
private videoPasswordFromAPI: string
private onVideoPasswordFromAPIResolver: (value: string) => void
private requiresPassword: boolean
constructor (videoWrapperId: string) {
@ -142,17 +144,33 @@ export class PeerTubeEmbed {
}
private initializeApi () {
if (this.playerOptionsBuilder.hasAPIEnabled()) {
if (this.api) {
this.api.reInit()
return
}
if (!this.playerOptionsBuilder.hasAPIEnabled()) return
if (this.api) return
this.api = new PeerTubeEmbedApi(this)
this.api.initialize()
this.api = new PeerTubeEmbedApi(this)
this.api.initialize()
}
// ---------------------------------------------------------------------------
setVideoPasswordByAPI (password: string) {
logger.info('Setting password from API')
this.videoPasswordFromAPI = password
if (this.onVideoPasswordFromAPIResolver) {
this.onVideoPasswordFromAPIResolver(password)
}
}
private getPasswordByAPI () {
if (this.videoPasswordFromAPI) return Promise.resolve(this.videoPasswordFromAPI)
return new Promise<string>(res => {
this.onVideoPasswordFromAPIResolver = res
})
}
// ---------------------------------------------------------------------------
async playNextPlaylistVideo () {
@ -191,6 +209,9 @@ export class PeerTubeEmbed {
}) {
const { uuid, forceAutoplay } = options
this.playerOptionsBuilder.loadCommonParams()
this.initializeApi()
try {
const {
videoResponse,
@ -218,7 +239,7 @@ export class PeerTubeEmbed {
const videoInfoPromise = videoResponse.json()
.then(async (videoInfo: VideoDetails) => {
this.playerOptionsBuilder.loadParams(this.config, videoInfo)
this.playerOptionsBuilder.loadVideoParams(this.config, videoInfo)
const live = videoInfo.isLive
? await this.videoFetcher.loadLive(videoInfo)
@ -287,7 +308,8 @@ export class PeerTubeEmbed {
(window as any)['videojsPlayer'] = this.player
this.buildCSS()
this.initializeApi()
if (this.api) this.api.initWithVideo()
}
this.alreadyInitialized = true
@ -360,10 +382,30 @@ export class PeerTubeEmbed {
if (incorrectPassword === null) return false
this.requiresPassword = true
if (this.playerOptionsBuilder.mustWaitPasswordFromEmbedAPI()) {
logger.info('Waiting for password from Embed API')
const videoPasswordFromAPI = await this.getPasswordByAPI()
if (videoPasswordFromAPI && this.videoPassword !== videoPasswordFromAPI) {
logger.info('Using video password from API')
this.videoPassword = videoPasswordFromAPI
return true
}
logger.error('Password from embed API is not valid')
return false
}
this.videoPassword = await this.playerHTML.askVideoPassword({
incorrectPassword,
translations: await this.translationsPromise
})
return true
}

View file

@ -49,6 +49,8 @@ export class PlayerOptionsBuilder {
private bigPlayBackgroundColor: string
private foregroundColor: string
private waitPasswordFromEmbedAPI = false
private mode: PlayerMode
private scope = 'peertube'
@ -106,18 +108,16 @@ export class PlayerOptionsBuilder {
return this.scope
}
mustWaitPasswordFromEmbedAPI () {
return this.waitPasswordFromEmbedAPI
}
// ---------------------------------------------------------------------------
loadParams (config: HTMLServerConfig, video: VideoDetails) {
loadCommonParams () {
try {
const params = new URL(window.location.toString()).searchParams
this.autoplay = getParamToggle(params, 'autoplay', false)
// Disable auto play on live videos that are not streamed
if (video.state.id === VideoState.LIVE_ENDED || video.state.id === VideoState.WAITING_FOR_LIVE) {
this.autoplay = false
}
this.controls = getParamToggle(params, 'controls', true)
this.controlBar = getParamToggle(params, 'controlBar', true)
@ -125,9 +125,9 @@ export class PlayerOptionsBuilder {
this.loop = getParamToggle(params, 'loop', false)
this.title = getParamToggle(params, 'title', true)
this.enableApi = getParamToggle(params, 'api', this.enableApi)
this.waitPasswordFromEmbedAPI = getParamToggle(params, 'waitPasswordFromEmbedAPI', this.waitPasswordFromEmbedAPI)
this.warningTitle = getParamToggle(params, 'warningTitle', true)
this.peertubeLink = getParamToggle(params, 'peertubeLink', true)
this.p2pEnabled = getParamToggle(params, 'p2p', this.isP2PEnabled(config, video))
this.scope = getParamString(params, 'scope', this.scope)
this.subtitle = getParamString(params, 'subtitle')
@ -137,6 +137,22 @@ export class PlayerOptionsBuilder {
this.bigPlayBackgroundColor = getParamString(params, 'bigPlayBackgroundColor')
this.foregroundColor = getParamString(params, 'foregroundColor')
} catch (err) {
logger.error('Cannot get params from URL.', err)
}
}
loadVideoParams (config: HTMLServerConfig, video: VideoDetails) {
try {
const params = new URL(window.location.toString()).searchParams
this.autoplay = getParamToggle(params, 'autoplay', false)
// Disable auto play on live videos that are not streamed
if (video.state.id === VideoState.LIVE_ENDED || video.state.id === VideoState.WAITING_FOR_LIVE) {
this.autoplay = false
}
this.p2pEnabled = getParamToggle(params, 'p2p', this.isP2PEnabled(config, video))
const modeParam = getParamString(params, 'mode')

View file

@ -30,7 +30,7 @@ export class VideoFetcher {
}
if (videoResponse?.status === HttpStatusCode.FORBIDDEN_403) {
const res = await videoResponse.json()
throw new PeerTubeServerError(res.message, res.code)
throw new PeerTubeServerError(res.message || res.detail, res.code)
}
throw new Error('We cannot fetch the video. Please try again later.')
}

View file

@ -14,9 +14,9 @@ window.addEventListener('load', async () => {
const iframe = document.createElement('iframe')
iframe.src = isPlaylist
? `/video-playlists/embed/${elementId}?api=1`
: `/videos/embed/${elementId}?api=1`
: `/videos/embed/${elementId}?api=1&waitPasswordFromEmbedAPI=1`
iframe.sandbox.add('allow-same-origin', 'allow-scripts', 'allow-popups')
iframe.sandbox.add('allow-same-origin', 'allow-scripts', 'allow-popups', 'allow-forms')
const mainElement = document.querySelector('#host')
mainElement.appendChild(iframe)
@ -27,6 +27,8 @@ window.addEventListener('load', async () => {
(window as any)['player'] = player
logger.info('Awaiting player ready...')
await player.setVideoPassword('toto')
await player.ready
logger.info('Player is ready.')
@ -41,10 +43,12 @@ window.addEventListener('load', async () => {
player.addEventListener(e as PlayerEventType, (param) => logger.info(`PLAYER: event '${e}' received`, { param }))
logger.info(`PLAYER: now listening for event '${e}'`)
player.getCurrentPosition()
.then(position => {
document.getElementById('playlist-position').innerHTML = position + ''
})
if (isPlaylist) {
player.getCurrentPosition()
.then(position => {
document.getElementById('playlist-position').innerHTML = position + ''
})
}
})
let playbackRates: number[] = []

View file

@ -154,6 +154,21 @@ Enable embed JavaScript API (see methods below).
Value must be `0` or `1`.
### waitPasswordFromEmbedAPI
**PeerTube >= 6.0**
If the video requires a password, PeerTube will wait a password provided by `setVideoPassword` method before loading the video.
Until you provide a password, `player.ready` is not resolved.
## Embed attributes
### `ready: Promise<void>`
This promise is resolved when the video is loaded an the player is ready.
## Embed methods
@ -237,6 +252,15 @@ Play previous video in playlist.
Get current position in playlist (starts from 1).
### `setVideoPassword(): Promise<void>`
**PeerTube >= 6.0**
Set the video password so the user doesn't have to manually fill it.
`waitPasswordFromEmbedAPI=1` is required in embed URL.
## Embed events
You can subscribe to events by using `addEventListener()`. See above for details.