private initializePlayer () {
this.buildQualities()
+ if (this.videoFiles.length === 0) {
+ this.player.addClass('disabled')
+ return
+ }
+
if (this.autoplay) {
this.player.posterImage.hide()
font-size: $font-size;
color: pvar(--embedForegroundColor);
+ &.disabled {
+ cursor: default;
+ pointer-events: none;
+
+ .vjs-big-play-button {
+ display: none !important;
+ }
+ }
+
.vjs-audio-button {
display: none;
}
width: 100%;
height: 100%;
background-position: 50% 50%;
+ background-repeat: no-repeat;
+}
+
+.player-information {
+ width: 100%;
+ color: #fff;
+ background: rgba(0, 0, 0, 0.6);
+ padding: 20px 0;
+ position: absolute;
+ bottom: 0;
+ text-align: center;
}
@media screen and (max-width: 300px) {
import videojs from 'video.js'
import { peertubeTranslate } from '../../../../shared/core-utils/i18n'
import { HTMLServerConfig, LiveVideo, ResultList, VideoDetails, VideoPlaylist, VideoPlaylistElement } from '../../../../shared/models'
+import { PeertubePlayerManager } from '../../assets/player'
import { TranslationsManager } from '../../assets/player/translations-manager'
import { getParamString } from '../../root-helpers'
import { PeerTubeEmbedApi } from './embed-api'
-import { AuthHTTP, PeerTubePlugin, PlayerManagerOptions, PlaylistFetcher, PlaylistTracker, VideoFetcher } from './shared'
+import { AuthHTTP, LiveManager, PeerTubePlugin, PlayerManagerOptions, PlaylistFetcher, PlaylistTracker, VideoFetcher } from './shared'
import { PlayerHTML } from './shared/player-html'
-import { PeertubePlayerManager } from '../../assets/player'
export class PeerTubeEmbed {
player: videojs.Player
private readonly peertubePlugin: PeerTubePlugin
private readonly playerHTML: PlayerHTML
private readonly playerManagerOptions: PlayerManagerOptions
+ private readonly liveManager: LiveManager
private playlistTracker: PlaylistTracker
this.peertubePlugin = new PeerTubePlugin(this.http)
this.playerHTML = new PlayerHTML(videoWrapperId)
this.playerManagerOptions = new PlayerManagerOptions(this.playerHTML, this.videoFetcher, this.peertubePlugin)
+ this.liveManager = new LiveManager(this.playerHTML)
try {
this.config = JSON.parse(window['PeerTubeServerConfig'])
}
this.peertubePlugin.getPluginsManager().runHook('action:embed.player.loaded', undefined, { player: this.player, videojs, video })
+
+ if (video.isLive) {
+ this.liveManager.displayInfoAndListenForChanges({
+ video,
+ translations,
+ onPublishedVideo: () => {
+ this.liveManager.stopListeningForChanges(video)
+ this.loadVideoAndBuildPlayer(video.uuid)
+ }
+ })
+ }
}
private resetPlayerElement () {
export * from './auth-http'
export * from './peertube-plugin'
+export * from './live-manager'
export * from './player-html'
export * from './player-manager-options'
export * from './playlist-fetcher'
--- /dev/null
+import { Socket } from 'socket.io-client'
+import { LiveVideoEventPayload, VideoDetails, VideoState } from '../../../../../shared/models'
+import { PlayerHTML } from './player-html'
+import { Translations } from './translations'
+
+export class LiveManager {
+ private liveSocket: Socket
+
+ constructor (
+ private readonly playerHTML: PlayerHTML
+ ) {
+
+ }
+
+ async displayInfoAndListenForChanges (options: {
+ video: VideoDetails
+ translations: Translations
+ onPublishedVideo: () => any
+ }) {
+ const { video, onPublishedVideo } = options
+
+ this.displayAppropriateInfo(options)
+
+ if (!this.liveSocket) {
+ const io = (await import('socket.io-client')).io
+ this.liveSocket = io(window.location.origin + '/live-videos')
+ }
+
+ this.liveSocket.on('state-change', (payload: LiveVideoEventPayload) => {
+ if (payload.state === VideoState.PUBLISHED) {
+ this.playerHTML.removeInformation()
+ onPublishedVideo()
+ return
+ }
+ })
+
+ this.liveSocket.emit('subscribe', { videoId: video.id })
+ }
+
+ stopListeningForChanges (video: VideoDetails) {
+ this.liveSocket.emit('unsubscribe', { videoId: video.id })
+ }
+
+ private displayAppropriateInfo (options: {
+ video: VideoDetails
+ translations: Translations
+ }) {
+ const { video, translations } = options
+
+ if (video.state.id === VideoState.WAITING_FOR_LIVE) {
+ this.displayWaitingForLiveInfo(translations)
+ return
+ }
+
+ if (video.state.id === VideoState.LIVE_ENDED) {
+ this.displayEndedLiveInfo(translations)
+ return
+ }
+ }
+
+ private displayWaitingForLiveInfo (translations: Translations) {
+ this.playerHTML.displayInformation('This live has not started yet.', translations)
+ }
+
+ private displayEndedLiveInfo (translations: Translations) {
+ this.playerHTML.displayInformation('This live has ended.', translations)
+
+ }
+}
private readonly wrapperElement: HTMLElement
private playerElement: HTMLVideoElement
+ private informationElement: HTMLDivElement
constructor (private readonly videoWrapperId: string) {
this.wrapperElement = document.getElementById(this.videoWrapperId)
placeholder.style.display = 'none'
}
+ displayInformation (text: string, translations: Translations) {
+ if (this.informationElement) this.removeInformation()
+
+ this.informationElement = document.createElement('div')
+ this.informationElement.className = 'player-information'
+ this.informationElement.innerText = peertubeTranslate(text, translations)
+
+ document.body.appendChild(this.informationElement)
+ }
+
+ removeInformation () {
+ this.removeElement(this.informationElement)
+ }
+
private getPlaceholderElement () {
return document.getElementById('placeholder-preview')
}
import { getActivityStreamDuration } from './activitypub/activity'
import { getBiggestActorImage } from './actor-image'
import { ServerConfigManager } from './server-config-manager'
+import { isTestInstance } from '@server/helpers/core-utils'
type Tags = {
ogType: string
static async getEmbedHTML () {
const path = ClientHtml.getEmbedPath()
- if (ClientHtml.htmlCache[path]) return ClientHtml.htmlCache[path]
+ // Disable HTML cache in dev mode because webpack can regenerate JS files
+ if (!isTestInstance() && ClientHtml.htmlCache[path]) {
+ return ClientHtml.htmlCache[path]
+ }
const buffer = await readFile(path)
const serverConfig = await ServerConfigManager.Instance.getHTMLServerConfig()