Add logic to handle playlist in embed
This commit is contained in:
parent
a4ff3100d3
commit
5abc96fca2
|
@ -163,6 +163,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
||||||
// Unsubscribe subscriptions
|
// Unsubscribe subscriptions
|
||||||
if (this.paramsSub) this.paramsSub.unsubscribe()
|
if (this.paramsSub) this.paramsSub.unsubscribe()
|
||||||
if (this.queryParamsSub) this.queryParamsSub.unsubscribe()
|
if (this.queryParamsSub) this.queryParamsSub.unsubscribe()
|
||||||
|
if (this.configSub) this.configSub.unsubscribe()
|
||||||
|
|
||||||
// Unbind hotkeys
|
// Unbind hotkeys
|
||||||
this.hotkeysService.remove(this.hotkeys)
|
this.hotkeysService.remove(this.hotkeys)
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
import { PeerTubePlugin } from './peertube-plugin'
|
|
||||||
import { WebTorrentPlugin } from './webtorrent/webtorrent-plugin'
|
|
||||||
import { P2pMediaLoaderPlugin } from './p2p-media-loader/p2p-media-loader-plugin'
|
|
||||||
import { PlayerMode } from './peertube-player-manager'
|
|
||||||
import { RedundancyUrlManager } from './p2p-media-loader/redundancy-url-manager'
|
|
||||||
import { VideoFile } from '@shared/models'
|
|
||||||
import videojs from 'video.js'
|
|
||||||
import { Config, Level } from 'hls.js'
|
import { Config, Level } from 'hls.js'
|
||||||
|
import videojs from 'video.js'
|
||||||
|
import { VideoFile } from '@shared/models'
|
||||||
|
import { P2pMediaLoaderPlugin } from './p2p-media-loader/p2p-media-loader-plugin'
|
||||||
|
import { RedundancyUrlManager } from './p2p-media-loader/redundancy-url-manager'
|
||||||
|
import { PlayerMode } from './peertube-player-manager'
|
||||||
|
import { PeerTubePlugin } from './peertube-plugin'
|
||||||
|
import { EndCardOptions } from './upnext/end-card'
|
||||||
|
import { WebTorrentPlugin } from './webtorrent/webtorrent-plugin'
|
||||||
|
|
||||||
declare module 'video.js' {
|
declare module 'video.js' {
|
||||||
|
|
||||||
|
@ -42,6 +43,8 @@ declare module 'video.js' {
|
||||||
}
|
}
|
||||||
|
|
||||||
dock (options: { title: string, description: string }): void
|
dock (options: { title: string, description: string }): void
|
||||||
|
|
||||||
|
upnext (options: Partial<EndCardOptions>): void
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { getCompleteLocale, getShortLocale, is18nLocale, isDefaultLocale } from
|
||||||
export class TranslationsManager {
|
export class TranslationsManager {
|
||||||
private static videojsLocaleCache: { [ path: string ]: any } = {}
|
private static videojsLocaleCache: { [ path: string ]: any } = {}
|
||||||
|
|
||||||
static getServerTranslations (serverUrl: string, locale: string) {
|
static getServerTranslations (serverUrl: string, locale: string): Promise<{ [id: string]: string }> {
|
||||||
const path = TranslationsManager.getLocalePath(serverUrl, locale)
|
const path = TranslationsManager.getLocalePath(serverUrl, locale)
|
||||||
// It is the default locale, nothing to translate
|
// It is the default locale, nothing to translate
|
||||||
if (!path) return Promise.resolve(undefined)
|
if (!path) return Promise.resolve(undefined)
|
||||||
|
|
|
@ -308,8 +308,10 @@ body {
|
||||||
.icon {
|
.icon {
|
||||||
&.icon-next {
|
&.icon-next {
|
||||||
mask-image: url('#{$assets-path}/player/images/next.svg');
|
mask-image: url('#{$assets-path}/player/images/next.svg');
|
||||||
|
-webkit-mask-image: url('#{$assets-path}/player/images/next.svg');
|
||||||
background-color: white;
|
background-color: white;
|
||||||
mask-size: cover;
|
mask-size: cover;
|
||||||
|
-webkit-mask-size: cover;
|
||||||
transform: scale(2.2);
|
transform: scale(2.2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@ export class PeerTubeEmbedApi {
|
||||||
}
|
}
|
||||||
|
|
||||||
private get element () {
|
private get element () {
|
||||||
return this.embed.videoElement
|
return this.embed.playerElement
|
||||||
}
|
}
|
||||||
|
|
||||||
private constructChannel () {
|
private constructChannel () {
|
||||||
|
@ -108,7 +108,6 @@ export class PeerTubeEmbedApi {
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
const position = this.element.currentTime
|
const position = this.element.currentTime
|
||||||
const volume = this.element.volume
|
const volume = this.element.volume
|
||||||
const duration = this.element.duration
|
|
||||||
|
|
||||||
this.channel.notify({
|
this.channel.notify({
|
||||||
method: 'playbackStatusUpdate',
|
method: 'playbackStatusUpdate',
|
||||||
|
|
|
@ -19,10 +19,9 @@
|
||||||
<div id="error-content"></div>
|
<div id="error-content"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<video playsinline="true" id="video-container" class="video-js vjs-peertube-skin">
|
<div id="video-wrapper"></div>
|
||||||
</video>
|
|
||||||
|
|
||||||
<div id="placeholder-preview" />
|
<div id="placeholder-preview"></div>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -27,6 +27,11 @@ html, body {
|
||||||
background-color: #000;
|
background-color: #000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#video-wrapper {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.video-js.vjs-peertube-skin {
|
.video-js.vjs-peertube-skin {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
|
@ -9,6 +9,8 @@ import {
|
||||||
UserRefreshToken,
|
UserRefreshToken,
|
||||||
VideoCaption,
|
VideoCaption,
|
||||||
VideoDetails,
|
VideoDetails,
|
||||||
|
VideoPlaylist,
|
||||||
|
VideoPlaylistElement,
|
||||||
VideoStreamingPlaylistType
|
VideoStreamingPlaylistType
|
||||||
} from '../../../../shared/models'
|
} from '../../../../shared/models'
|
||||||
import { P2PMediaLoaderOptions, PeertubePlayerManagerOptions, PlayerMode } from '../../assets/player/peertube-player-manager'
|
import { P2PMediaLoaderOptions, PeertubePlayerManagerOptions, PlayerMode } from '../../assets/player/peertube-player-manager'
|
||||||
|
@ -19,9 +21,10 @@ import { PeerTubeEmbedApi } from './embed-api'
|
||||||
type Translations = { [ id: string ]: string }
|
type Translations = { [ id: string ]: string }
|
||||||
|
|
||||||
export class PeerTubeEmbed {
|
export class PeerTubeEmbed {
|
||||||
videoElement: HTMLVideoElement
|
playerElement: HTMLVideoElement
|
||||||
player: videojs.Player
|
player: videojs.Player
|
||||||
api: PeerTubeEmbedApi = null
|
api: PeerTubeEmbedApi = null
|
||||||
|
|
||||||
autoplay: boolean
|
autoplay: boolean
|
||||||
controls: boolean
|
controls: boolean
|
||||||
muted: boolean
|
muted: boolean
|
||||||
|
@ -47,14 +50,24 @@ export class PeerTubeEmbed {
|
||||||
CLIENT_SECRET: 'client_secret'
|
CLIENT_SECRET: 'client_secret'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private translationsPromise: Promise<{ [id: string]: string }>
|
||||||
|
private configPromise: Promise<ServerConfig>
|
||||||
|
private PeertubePlayerManagerModulePromise: Promise<any>
|
||||||
|
|
||||||
|
private playlist: VideoPlaylist
|
||||||
|
private playlistElements: VideoPlaylistElement[]
|
||||||
|
private currentPlaylistElement: VideoPlaylistElement
|
||||||
|
|
||||||
|
private wrapperElement: HTMLElement
|
||||||
|
|
||||||
static async main () {
|
static async main () {
|
||||||
const videoContainerId = 'video-container'
|
const videoContainerId = 'video-wrapper'
|
||||||
const embed = new PeerTubeEmbed(videoContainerId)
|
const embed = new PeerTubeEmbed(videoContainerId)
|
||||||
await embed.init()
|
await embed.init()
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor (private videoContainerId: string) {
|
constructor (private videoWrapperId: string) {
|
||||||
this.videoElement = document.getElementById(videoContainerId) as HTMLVideoElement
|
this.wrapperElement = document.getElementById(this.videoWrapperId)
|
||||||
}
|
}
|
||||||
|
|
||||||
getVideoUrl (id: string) {
|
getVideoUrl (id: string) {
|
||||||
|
@ -114,6 +127,10 @@ export class PeerTubeEmbed {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getPlaylistUrl (id: string) {
|
||||||
|
return window.location.origin + '/api/v1/video-playlists/' + id
|
||||||
|
}
|
||||||
|
|
||||||
loadVideoInfo (videoId: string): Promise<Response> {
|
loadVideoInfo (videoId: string): Promise<Response> {
|
||||||
return this.refreshFetch(this.getVideoUrl(videoId), { headers: this.headers })
|
return this.refreshFetch(this.getVideoUrl(videoId), { headers: this.headers })
|
||||||
}
|
}
|
||||||
|
@ -122,8 +139,17 @@ export class PeerTubeEmbed {
|
||||||
return fetch(this.getVideoUrl(videoId) + '/captions')
|
return fetch(this.getVideoUrl(videoId) + '/captions')
|
||||||
}
|
}
|
||||||
|
|
||||||
loadConfig (): Promise<Response> {
|
loadPlaylistInfo (playlistId: string): Promise<Response> {
|
||||||
|
return fetch(this.getPlaylistUrl(playlistId))
|
||||||
|
}
|
||||||
|
|
||||||
|
loadPlaylistElements (playlistId: string): Promise<Response> {
|
||||||
|
return fetch(this.getPlaylistUrl(playlistId) + '/videos')
|
||||||
|
}
|
||||||
|
|
||||||
|
loadConfig (): Promise<ServerConfig> {
|
||||||
return fetch('/api/v1/config')
|
return fetch('/api/v1/config')
|
||||||
|
.then(res => res.json())
|
||||||
}
|
}
|
||||||
|
|
||||||
removeElement (element: HTMLElement) {
|
removeElement (element: HTMLElement) {
|
||||||
|
@ -132,7 +158,10 @@ export class PeerTubeEmbed {
|
||||||
|
|
||||||
displayError (text: string, translations?: Translations) {
|
displayError (text: string, translations?: Translations) {
|
||||||
// Remove video element
|
// Remove video element
|
||||||
if (this.videoElement) this.removeElement(this.videoElement)
|
if (this.playerElement) {
|
||||||
|
this.removeElement(this.playerElement)
|
||||||
|
this.playerElement = undefined
|
||||||
|
}
|
||||||
|
|
||||||
const translatedText = peertubeTranslate(text, translations)
|
const translatedText = peertubeTranslate(text, translations)
|
||||||
const translatedSorry = peertubeTranslate('Sorry', translations)
|
const translatedSorry = peertubeTranslate('Sorry', translations)
|
||||||
|
@ -159,6 +188,16 @@ export class PeerTubeEmbed {
|
||||||
this.displayError(text, translations)
|
this.displayError(text, translations)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
playlistNotFound (translations?: Translations) {
|
||||||
|
const text = 'This playlist does not exist.'
|
||||||
|
this.displayError(text, translations)
|
||||||
|
}
|
||||||
|
|
||||||
|
playlistFetchError (translations?: Translations) {
|
||||||
|
const text = 'We cannot fetch the playlist. Please try again later.'
|
||||||
|
this.displayError(text, translations)
|
||||||
|
}
|
||||||
|
|
||||||
getParamToggle (params: URLSearchParams, name: string, defaultValue?: boolean) {
|
getParamToggle (params: URLSearchParams, name: string, defaultValue?: boolean) {
|
||||||
return params.has(name) ? (params.get(name) === '1' || params.get(name) === 'true') : defaultValue
|
return params.has(name) ? (params.get(name) === '1' || params.get(name) === 'true') : defaultValue
|
||||||
}
|
}
|
||||||
|
@ -218,34 +257,129 @@ export class PeerTubeEmbed {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async initCore () {
|
private async loadPlaylist (playlistId: string) {
|
||||||
const urlParts = window.location.pathname.split('/')
|
const playlistPromise = this.loadPlaylistInfo(playlistId)
|
||||||
const videoId = urlParts[ urlParts.length - 1 ]
|
const playlistElementsPromise = this.loadPlaylistElements(playlistId)
|
||||||
|
|
||||||
if (this.userTokens) this.setHeadersFromTokens()
|
const playlistResponse = await playlistPromise
|
||||||
|
|
||||||
|
if (!playlistResponse.ok) {
|
||||||
|
const serverTranslations = await this.translationsPromise
|
||||||
|
|
||||||
|
if (playlistResponse.status === 404) {
|
||||||
|
this.playlistNotFound(serverTranslations)
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
this.playlistFetchError(serverTranslations)
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
return { playlistResponse, videosResponse: await playlistElementsPromise }
|
||||||
|
}
|
||||||
|
|
||||||
|
private async loadVideo (videoId: string) {
|
||||||
const videoPromise = this.loadVideoInfo(videoId)
|
const videoPromise = this.loadVideoInfo(videoId)
|
||||||
const captionsPromise = this.loadVideoCaptions(videoId)
|
|
||||||
const configPromise = this.loadConfig()
|
|
||||||
|
|
||||||
const translationsPromise = TranslationsManager.getServerTranslations(window.location.origin, navigator.language)
|
|
||||||
const videoResponse = await videoPromise
|
const videoResponse = await videoPromise
|
||||||
|
|
||||||
if (!videoResponse.ok) {
|
if (!videoResponse.ok) {
|
||||||
const serverTranslations = await translationsPromise
|
const serverTranslations = await this.translationsPromise
|
||||||
|
|
||||||
if (videoResponse.status === 404) return this.videoNotFound(serverTranslations)
|
if (videoResponse.status === 404) {
|
||||||
|
this.videoNotFound(serverTranslations)
|
||||||
return this.videoFetchError(serverTranslations)
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
const videoInfo: VideoDetails = await videoResponse.json()
|
this.videoFetchError(serverTranslations)
|
||||||
this.loadPlaceholder(videoInfo)
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
const PeertubePlayerManagerModulePromise = import('../../assets/player/peertube-player-manager')
|
const captionsPromise = this.loadVideoCaptions(videoId)
|
||||||
|
|
||||||
const promises = [ translationsPromise, captionsPromise, configPromise, PeertubePlayerManagerModulePromise ]
|
return { captionsPromise, videoResponse }
|
||||||
const [ serverTranslations, captionsResponse, configResponse, PeertubePlayerManagerModule ] = await Promise.all(promises)
|
}
|
||||||
|
|
||||||
|
private async buildPlaylistManager () {
|
||||||
|
const translations = await this.translationsPromise
|
||||||
|
|
||||||
|
this.player.upnext({
|
||||||
|
timeout: 10000, // 10s
|
||||||
|
headText: peertubeTranslate('Up Next', translations),
|
||||||
|
cancelText: peertubeTranslate('Cancel', translations),
|
||||||
|
suspendedText: peertubeTranslate('Autoplay is suspended', translations),
|
||||||
|
getTitle: () => this.nextVideoTitle(),
|
||||||
|
next: () => this.autoplayNext(),
|
||||||
|
condition: () => !!this.getNextPlaylistElement(),
|
||||||
|
suspended: () => false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private async autoplayNext () {
|
||||||
|
const next = this.getNextPlaylistElement()
|
||||||
|
if (!next) {
|
||||||
|
console.log('Next element not found in playlist.')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.currentPlaylistElement = next
|
||||||
|
|
||||||
|
const res = await this.loadVideo(this.currentPlaylistElement.video.uuid)
|
||||||
|
if (res === undefined) return
|
||||||
|
|
||||||
|
return this.buildVideoPlayer(res.videoResponse, res.captionsPromise)
|
||||||
|
}
|
||||||
|
|
||||||
|
private nextVideoTitle () {
|
||||||
|
const next = this.getNextPlaylistElement()
|
||||||
|
if (!next) return ''
|
||||||
|
|
||||||
|
return next.video.name
|
||||||
|
}
|
||||||
|
|
||||||
|
private getNextPlaylistElement (position?: number): VideoPlaylistElement {
|
||||||
|
if (!position) position = this.currentPlaylistElement.position + 1
|
||||||
|
|
||||||
|
if (position > this.playlist.videosLength) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
const next = this.playlistElements.find(e => e.position === position)
|
||||||
|
|
||||||
|
if (!next || !next.video) {
|
||||||
|
return this.getNextPlaylistElement(position + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return next
|
||||||
|
}
|
||||||
|
|
||||||
|
private async buildVideoPlayer (videoResponse: Response, captionsPromise: Promise<Response>) {
|
||||||
|
let alreadyHadPlayer = false
|
||||||
|
|
||||||
|
if (this.player) {
|
||||||
|
this.player.dispose()
|
||||||
|
alreadyHadPlayer = true
|
||||||
|
}
|
||||||
|
|
||||||
|
this.playerElement = document.createElement('video')
|
||||||
|
this.playerElement.className = 'video-js vjs-peertube-skin'
|
||||||
|
this.playerElement.setAttribute('playsinline', 'true')
|
||||||
|
this.wrapperElement.appendChild(this.playerElement)
|
||||||
|
|
||||||
|
const videoInfoPromise = videoResponse.json()
|
||||||
|
.then((videoInfo: VideoDetails) => {
|
||||||
|
if (!alreadyHadPlayer) this.loadPlaceholder(videoInfo)
|
||||||
|
|
||||||
|
return videoInfo
|
||||||
|
})
|
||||||
|
|
||||||
|
const [ videoInfo, serverTranslations, captionsResponse, config, PeertubePlayerManagerModule ] = await Promise.all([
|
||||||
|
videoInfoPromise,
|
||||||
|
this.translationsPromise,
|
||||||
|
captionsPromise,
|
||||||
|
this.configPromise,
|
||||||
|
this.PeertubePlayerManagerModulePromise
|
||||||
|
])
|
||||||
|
|
||||||
const PeertubePlayerManager = PeertubePlayerManagerModule.PeertubePlayerManager
|
const PeertubePlayerManager = PeertubePlayerManagerModule.PeertubePlayerManager
|
||||||
const videoCaptions = await this.buildCaptions(serverTranslations, captionsResponse)
|
const videoCaptions = await this.buildCaptions(serverTranslations, captionsResponse)
|
||||||
|
@ -254,7 +388,8 @@ export class PeerTubeEmbed {
|
||||||
|
|
||||||
const options: PeertubePlayerManagerOptions = {
|
const options: PeertubePlayerManagerOptions = {
|
||||||
common: {
|
common: {
|
||||||
autoplay: this.autoplay,
|
// Autoplay in playlist mode
|
||||||
|
autoplay: alreadyHadPlayer ? true : this.autoplay,
|
||||||
controls: this.controls,
|
controls: this.controls,
|
||||||
muted: this.muted,
|
muted: this.muted,
|
||||||
loop: this.loop,
|
loop: this.loop,
|
||||||
|
@ -263,12 +398,14 @@ export class PeerTubeEmbed {
|
||||||
stopTime: this.stopTime,
|
stopTime: this.stopTime,
|
||||||
subtitle: this.subtitle,
|
subtitle: this.subtitle,
|
||||||
|
|
||||||
|
nextVideo: () => this.autoplayNext(),
|
||||||
|
|
||||||
videoCaptions,
|
videoCaptions,
|
||||||
inactivityTimeout: 2500,
|
inactivityTimeout: 2500,
|
||||||
videoViewUrl: this.getVideoUrl(videoId) + '/views',
|
videoViewUrl: this.getVideoUrl(videoInfo.uuid) + '/views',
|
||||||
|
|
||||||
playerElement: this.videoElement,
|
playerElement: this.playerElement,
|
||||||
onPlayerElementChange: (element: HTMLVideoElement) => this.videoElement = element,
|
onPlayerElementChange: (element: HTMLVideoElement) => this.playerElement = element,
|
||||||
|
|
||||||
videoDuration: videoInfo.duration,
|
videoDuration: videoInfo.duration,
|
||||||
enableHotkeys: true,
|
enableHotkeys: true,
|
||||||
|
@ -307,23 +444,58 @@ export class PeerTubeEmbed {
|
||||||
|
|
||||||
this.buildCSS()
|
this.buildCSS()
|
||||||
|
|
||||||
await this.buildDock(videoInfo, configResponse)
|
await this.buildDock(videoInfo, config)
|
||||||
|
|
||||||
this.initializeApi()
|
this.initializeApi()
|
||||||
|
|
||||||
this.removePlaceholder()
|
this.removePlaceholder()
|
||||||
|
|
||||||
|
if (this.isPlaylistEmbed()) {
|
||||||
|
await this.buildPlaylistManager()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async initCore () {
|
||||||
|
if (this.userTokens) this.setHeadersFromTokens()
|
||||||
|
|
||||||
|
this.configPromise = this.loadConfig()
|
||||||
|
this.translationsPromise = TranslationsManager.getServerTranslations(window.location.origin, navigator.language)
|
||||||
|
this.PeertubePlayerManagerModulePromise = import('../../assets/player/peertube-player-manager')
|
||||||
|
|
||||||
|
let videoId: string
|
||||||
|
|
||||||
|
if (this.isPlaylistEmbed()) {
|
||||||
|
const playlistId = this.getResourceId()
|
||||||
|
const res = await this.loadPlaylist(playlistId)
|
||||||
|
if (!res) return undefined
|
||||||
|
|
||||||
|
this.playlist = await res.playlistResponse.json()
|
||||||
|
|
||||||
|
const playlistElementResult = await res.videosResponse.json()
|
||||||
|
this.playlistElements = playlistElementResult.data
|
||||||
|
|
||||||
|
this.currentPlaylistElement = this.playlistElements[0]
|
||||||
|
videoId = this.currentPlaylistElement.video.uuid
|
||||||
|
} else {
|
||||||
|
videoId = this.getResourceId()
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await this.loadVideo(videoId)
|
||||||
|
if (res === undefined) return
|
||||||
|
|
||||||
|
return this.buildVideoPlayer(res.videoResponse, res.captionsPromise)
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleError (err: Error, translations?: { [ id: string ]: string }) {
|
private handleError (err: Error, translations?: { [ id: string ]: string }) {
|
||||||
if (err.message.indexOf('from xs param') !== -1) {
|
if (err.message.indexOf('from xs param') !== -1) {
|
||||||
this.player.dispose()
|
this.player.dispose()
|
||||||
this.videoElement = null
|
this.playerElement = null
|
||||||
this.displayError('This video is not available because the remote instance is not responding.', translations)
|
this.displayError('This video is not available because the remote instance is not responding.', translations)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async buildDock (videoInfo: VideoDetails, configResponse: Response) {
|
private async buildDock (videoInfo: VideoDetails, config: ServerConfig) {
|
||||||
if (!this.controls) return
|
if (!this.controls) return
|
||||||
|
|
||||||
// On webtorrent fallback, player may have been disposed
|
// On webtorrent fallback, player may have been disposed
|
||||||
|
@ -331,7 +503,6 @@ export class PeerTubeEmbed {
|
||||||
|
|
||||||
const title = this.title ? videoInfo.name : undefined
|
const title = this.title ? videoInfo.name : undefined
|
||||||
|
|
||||||
const config: ServerConfig = await configResponse.json()
|
|
||||||
const description = config.tracker.enabled && this.warningTitle
|
const description = config.tracker.enabled && this.warningTitle
|
||||||
? '<span class="text">' + peertubeTranslate('Watching this video may reveal your IP address to others.') + '</span>'
|
? '<span class="text">' + peertubeTranslate('Watching this video may reveal your IP address to others.') + '</span>'
|
||||||
: undefined
|
: undefined
|
||||||
|
@ -373,11 +544,12 @@ export class PeerTubeEmbed {
|
||||||
|
|
||||||
const url = window.location.origin + video.previewPath
|
const url = window.location.origin + video.previewPath
|
||||||
placeholder.style.backgroundImage = `url("${url}")`
|
placeholder.style.backgroundImage = `url("${url}")`
|
||||||
|
placeholder.style.display = 'block'
|
||||||
}
|
}
|
||||||
|
|
||||||
private removePlaceholder () {
|
private removePlaceholder () {
|
||||||
const placeholder = this.getPlaceholderElement()
|
const placeholder = this.getPlaceholderElement()
|
||||||
placeholder.parentElement.removeChild(placeholder)
|
placeholder.style.display = 'none'
|
||||||
}
|
}
|
||||||
|
|
||||||
private getPlaceholderElement () {
|
private getPlaceholderElement () {
|
||||||
|
@ -387,6 +559,15 @@ export class PeerTubeEmbed {
|
||||||
private setHeadersFromTokens () {
|
private setHeadersFromTokens () {
|
||||||
this.headers.set('Authorization', `${this.userTokens.tokenType} ${this.userTokens.accessToken}`)
|
this.headers.set('Authorization', `${this.userTokens.tokenType} ${this.userTokens.accessToken}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getResourceId () {
|
||||||
|
const urlParts = window.location.pathname.split('/')
|
||||||
|
return urlParts[ urlParts.length - 1 ]
|
||||||
|
}
|
||||||
|
|
||||||
|
private isPlaylistEmbed () {
|
||||||
|
return window.location.pathname.split('/')[1] === 'video-playlists'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PeerTubeEmbed.main()
|
PeerTubeEmbed.main()
|
||||||
|
|
|
@ -48,7 +48,9 @@ values(VIDEO_CATEGORIES)
|
||||||
'This video does not exist.',
|
'This video does not exist.',
|
||||||
'We cannot fetch the video. Please try again later.',
|
'We cannot fetch the video. Please try again later.',
|
||||||
'Sorry',
|
'Sorry',
|
||||||
'This video is not available because the remote instance is not responding.'
|
'This video is not available because the remote instance is not responding.',
|
||||||
|
'This playlist does not exist',
|
||||||
|
'We cannot fetch the playlist. Please try again later.'
|
||||||
])
|
])
|
||||||
.forEach(v => { serverKeys[v] = v })
|
.forEach(v => { serverKeys[v] = v })
|
||||||
|
|
||||||
|
|
|
@ -22,19 +22,20 @@ clientsRouter.use('/videos/watch/:id', asyncMiddleware(generateWatchHtmlPage))
|
||||||
clientsRouter.use('/accounts/:nameWithHost', asyncMiddleware(generateAccountHtmlPage))
|
clientsRouter.use('/accounts/:nameWithHost', asyncMiddleware(generateAccountHtmlPage))
|
||||||
clientsRouter.use('/video-channels/:nameWithHost', asyncMiddleware(generateVideoChannelHtmlPage))
|
clientsRouter.use('/video-channels/:nameWithHost', asyncMiddleware(generateVideoChannelHtmlPage))
|
||||||
|
|
||||||
const embedCSPMiddleware = CONFIG.CSP.ENABLED
|
const embedMiddlewares = [
|
||||||
|
CONFIG.CSP.ENABLED
|
||||||
? embedCSP
|
? embedCSP
|
||||||
: (req: express.Request, res: express.Response, next: express.NextFunction) => next()
|
: (req: express.Request, res: express.Response, next: express.NextFunction) => next(),
|
||||||
|
|
||||||
clientsRouter.use(
|
|
||||||
'/videos/embed',
|
|
||||||
embedCSPMiddleware,
|
|
||||||
(req: express.Request, res: express.Response) => {
|
(req: express.Request, res: express.Response) => {
|
||||||
res.removeHeader('X-Frame-Options')
|
res.removeHeader('X-Frame-Options')
|
||||||
// Don't cache HTML file since it's an index to the immutable JS/CSS files
|
// Don't cache HTML file since it's an index to the immutable JS/CSS files
|
||||||
res.sendFile(embedPath, { maxAge: 0 })
|
res.sendFile(embedPath, { maxAge: 0 })
|
||||||
}
|
}
|
||||||
)
|
]
|
||||||
|
|
||||||
|
clientsRouter.use('/videos/embed', ...embedMiddlewares)
|
||||||
|
clientsRouter.use('/video-playlists/embed', ...embedMiddlewares)
|
||||||
clientsRouter.use(
|
clientsRouter.use(
|
||||||
'/videos/test-embed',
|
'/videos/test-embed',
|
||||||
(req: express.Request, res: express.Response) => res.sendFile(testEmbedPath)
|
(req: express.Request, res: express.Response) => res.sendFile(testEmbedPath)
|
||||||
|
|
Loading…
Reference in New Issue