From d3f4689bded2a6f5b589fe79c3f8b6082d553d9e Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 31 May 2022 14:18:41 +0200 Subject: [PATCH] Add live autostart/messages in embed --- .../shared/webtorrent/webtorrent-plugin.ts | 5 ++ client/src/sass/player/peertube-skin.scss | 9 +++ client/src/standalone/videos/embed.scss | 11 +++ client/src/standalone/videos/embed.ts | 17 ++++- client/src/standalone/videos/shared/index.ts | 1 + .../standalone/videos/shared/live-manager.ts | 69 +++++++++++++++++++ .../standalone/videos/shared/player-html.ts | 15 ++++ server/lib/client-html.ts | 6 +- 8 files changed, 130 insertions(+), 3 deletions(-) create mode 100644 client/src/standalone/videos/shared/live-manager.ts diff --git a/client/src/assets/player/shared/webtorrent/webtorrent-plugin.ts b/client/src/assets/player/shared/webtorrent/webtorrent-plugin.ts index b48203148..83b483d87 100644 --- a/client/src/assets/player/shared/webtorrent/webtorrent-plugin.ts +++ b/client/src/assets/player/shared/webtorrent/webtorrent-plugin.ts @@ -430,6 +430,11 @@ class WebTorrentPlugin extends Plugin { private initializePlayer () { this.buildQualities() + if (this.videoFiles.length === 0) { + this.player.addClass('disabled') + return + } + if (this.autoplay) { this.player.posterImage.hide() diff --git a/client/src/sass/player/peertube-skin.scss b/client/src/sass/player/peertube-skin.scss index c420e825e..43c144624 100644 --- a/client/src/sass/player/peertube-skin.scss +++ b/client/src/sass/player/peertube-skin.scss @@ -20,6 +20,15 @@ body { 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; } diff --git a/client/src/standalone/videos/embed.scss b/client/src/standalone/videos/embed.scss index 91ab822c8..8c20bae79 100644 --- a/client/src/standalone/videos/embed.scss +++ b/client/src/standalone/videos/embed.scss @@ -92,6 +92,17 @@ body { 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) { diff --git a/client/src/standalone/videos/embed.ts b/client/src/standalone/videos/embed.ts index c5d017d4a..0a2b0ccbd 100644 --- a/client/src/standalone/videos/embed.ts +++ b/client/src/standalone/videos/embed.ts @@ -4,12 +4,12 @@ import '../../assets/player/shared/dock/peertube-dock-plugin' 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 @@ -26,6 +26,7 @@ export class PeerTubeEmbed { private readonly peertubePlugin: PeerTubePlugin private readonly playerHTML: PlayerHTML private readonly playerManagerOptions: PlayerManagerOptions + private readonly liveManager: LiveManager private playlistTracker: PlaylistTracker @@ -37,6 +38,7 @@ export class PeerTubeEmbed { 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']) @@ -235,6 +237,17 @@ export class PeerTubeEmbed { } 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 () { diff --git a/client/src/standalone/videos/shared/index.ts b/client/src/standalone/videos/shared/index.ts index 4b4e05b7c..928b8e270 100644 --- a/client/src/standalone/videos/shared/index.ts +++ b/client/src/standalone/videos/shared/index.ts @@ -1,5 +1,6 @@ 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' diff --git a/client/src/standalone/videos/shared/live-manager.ts b/client/src/standalone/videos/shared/live-manager.ts new file mode 100644 index 000000000..422d39793 --- /dev/null +++ b/client/src/standalone/videos/shared/live-manager.ts @@ -0,0 +1,69 @@ +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) + + } +} diff --git a/client/src/standalone/videos/shared/player-html.ts b/client/src/standalone/videos/shared/player-html.ts index 110124417..eb6324ac7 100644 --- a/client/src/standalone/videos/shared/player-html.ts +++ b/client/src/standalone/videos/shared/player-html.ts @@ -6,6 +6,7 @@ export class PlayerHTML { private readonly wrapperElement: HTMLElement private playerElement: HTMLVideoElement + private informationElement: HTMLDivElement constructor (private readonly videoWrapperId: string) { this.wrapperElement = document.getElementById(this.videoWrapperId) @@ -66,6 +67,20 @@ export class PlayerHTML { 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') } diff --git a/server/lib/client-html.ts b/server/lib/client-html.ts index 337364ac9..1e8d03023 100644 --- a/server/lib/client-html.ts +++ b/server/lib/client-html.ts @@ -30,6 +30,7 @@ import { MAccountActor, MChannelActor } from '../types/models' 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 @@ -232,7 +233,10 @@ class ClientHtml { 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()