aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--client/src/assets/player/shared/webtorrent/webtorrent-plugin.ts5
-rw-r--r--client/src/sass/player/peertube-skin.scss9
-rw-r--r--client/src/standalone/videos/embed.scss11
-rw-r--r--client/src/standalone/videos/embed.ts17
-rw-r--r--client/src/standalone/videos/shared/index.ts1
-rw-r--r--client/src/standalone/videos/shared/live-manager.ts69
-rw-r--r--client/src/standalone/videos/shared/player-html.ts15
-rw-r--r--server/lib/client-html.ts6
8 files changed, 130 insertions, 3 deletions
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 {
430 private initializePlayer () { 430 private initializePlayer () {
431 this.buildQualities() 431 this.buildQualities()
432 432
433 if (this.videoFiles.length === 0) {
434 this.player.addClass('disabled')
435 return
436 }
437
433 if (this.autoplay) { 438 if (this.autoplay) {
434 this.player.posterImage.hide() 439 this.player.posterImage.hide()
435 440
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 {
20 font-size: $font-size; 20 font-size: $font-size;
21 color: pvar(--embedForegroundColor); 21 color: pvar(--embedForegroundColor);
22 22
23 &.disabled {
24 cursor: default;
25 pointer-events: none;
26
27 .vjs-big-play-button {
28 display: none !important;
29 }
30 }
31
23 .vjs-audio-button { 32 .vjs-audio-button {
24 display: none; 33 display: none;
25 } 34 }
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 {
92 width: 100%; 92 width: 100%;
93 height: 100%; 93 height: 100%;
94 background-position: 50% 50%; 94 background-position: 50% 50%;
95 background-repeat: no-repeat;
96}
97
98.player-information {
99 width: 100%;
100 color: #fff;
101 background: rgba(0, 0, 0, 0.6);
102 padding: 20px 0;
103 position: absolute;
104 bottom: 0;
105 text-align: center;
95} 106}
96 107
97@media screen and (max-width: 300px) { 108@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'
4import videojs from 'video.js' 4import videojs from 'video.js'
5import { peertubeTranslate } from '../../../../shared/core-utils/i18n' 5import { peertubeTranslate } from '../../../../shared/core-utils/i18n'
6import { HTMLServerConfig, LiveVideo, ResultList, VideoDetails, VideoPlaylist, VideoPlaylistElement } from '../../../../shared/models' 6import { HTMLServerConfig, LiveVideo, ResultList, VideoDetails, VideoPlaylist, VideoPlaylistElement } from '../../../../shared/models'
7import { PeertubePlayerManager } from '../../assets/player'
7import { TranslationsManager } from '../../assets/player/translations-manager' 8import { TranslationsManager } from '../../assets/player/translations-manager'
8import { getParamString } from '../../root-helpers' 9import { getParamString } from '../../root-helpers'
9import { PeerTubeEmbedApi } from './embed-api' 10import { PeerTubeEmbedApi } from './embed-api'
10import { AuthHTTP, PeerTubePlugin, PlayerManagerOptions, PlaylistFetcher, PlaylistTracker, VideoFetcher } from './shared' 11import { AuthHTTP, LiveManager, PeerTubePlugin, PlayerManagerOptions, PlaylistFetcher, PlaylistTracker, VideoFetcher } from './shared'
11import { PlayerHTML } from './shared/player-html' 12import { PlayerHTML } from './shared/player-html'
12import { PeertubePlayerManager } from '../../assets/player'
13 13
14export class PeerTubeEmbed { 14export class PeerTubeEmbed {
15 player: videojs.Player 15 player: videojs.Player
@@ -26,6 +26,7 @@ export class PeerTubeEmbed {
26 private readonly peertubePlugin: PeerTubePlugin 26 private readonly peertubePlugin: PeerTubePlugin
27 private readonly playerHTML: PlayerHTML 27 private readonly playerHTML: PlayerHTML
28 private readonly playerManagerOptions: PlayerManagerOptions 28 private readonly playerManagerOptions: PlayerManagerOptions
29 private readonly liveManager: LiveManager
29 30
30 private playlistTracker: PlaylistTracker 31 private playlistTracker: PlaylistTracker
31 32
@@ -37,6 +38,7 @@ export class PeerTubeEmbed {
37 this.peertubePlugin = new PeerTubePlugin(this.http) 38 this.peertubePlugin = new PeerTubePlugin(this.http)
38 this.playerHTML = new PlayerHTML(videoWrapperId) 39 this.playerHTML = new PlayerHTML(videoWrapperId)
39 this.playerManagerOptions = new PlayerManagerOptions(this.playerHTML, this.videoFetcher, this.peertubePlugin) 40 this.playerManagerOptions = new PlayerManagerOptions(this.playerHTML, this.videoFetcher, this.peertubePlugin)
41 this.liveManager = new LiveManager(this.playerHTML)
40 42
41 try { 43 try {
42 this.config = JSON.parse(window['PeerTubeServerConfig']) 44 this.config = JSON.parse(window['PeerTubeServerConfig'])
@@ -235,6 +237,17 @@ export class PeerTubeEmbed {
235 } 237 }
236 238
237 this.peertubePlugin.getPluginsManager().runHook('action:embed.player.loaded', undefined, { player: this.player, videojs, video }) 239 this.peertubePlugin.getPluginsManager().runHook('action:embed.player.loaded', undefined, { player: this.player, videojs, video })
240
241 if (video.isLive) {
242 this.liveManager.displayInfoAndListenForChanges({
243 video,
244 translations,
245 onPublishedVideo: () => {
246 this.liveManager.stopListeningForChanges(video)
247 this.loadVideoAndBuildPlayer(video.uuid)
248 }
249 })
250 }
238 } 251 }
239 252
240 private resetPlayerElement () { 253 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 @@
1export * from './auth-http' 1export * from './auth-http'
2export * from './peertube-plugin' 2export * from './peertube-plugin'
3export * from './live-manager'
3export * from './player-html' 4export * from './player-html'
4export * from './player-manager-options' 5export * from './player-manager-options'
5export * from './playlist-fetcher' 6export * 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 @@
1import { Socket } from 'socket.io-client'
2import { LiveVideoEventPayload, VideoDetails, VideoState } from '../../../../../shared/models'
3import { PlayerHTML } from './player-html'
4import { Translations } from './translations'
5
6export class LiveManager {
7 private liveSocket: Socket
8
9 constructor (
10 private readonly playerHTML: PlayerHTML
11 ) {
12
13 }
14
15 async displayInfoAndListenForChanges (options: {
16 video: VideoDetails
17 translations: Translations
18 onPublishedVideo: () => any
19 }) {
20 const { video, onPublishedVideo } = options
21
22 this.displayAppropriateInfo(options)
23
24 if (!this.liveSocket) {
25 const io = (await import('socket.io-client')).io
26 this.liveSocket = io(window.location.origin + '/live-videos')
27 }
28
29 this.liveSocket.on('state-change', (payload: LiveVideoEventPayload) => {
30 if (payload.state === VideoState.PUBLISHED) {
31 this.playerHTML.removeInformation()
32 onPublishedVideo()
33 return
34 }
35 })
36
37 this.liveSocket.emit('subscribe', { videoId: video.id })
38 }
39
40 stopListeningForChanges (video: VideoDetails) {
41 this.liveSocket.emit('unsubscribe', { videoId: video.id })
42 }
43
44 private displayAppropriateInfo (options: {
45 video: VideoDetails
46 translations: Translations
47 }) {
48 const { video, translations } = options
49
50 if (video.state.id === VideoState.WAITING_FOR_LIVE) {
51 this.displayWaitingForLiveInfo(translations)
52 return
53 }
54
55 if (video.state.id === VideoState.LIVE_ENDED) {
56 this.displayEndedLiveInfo(translations)
57 return
58 }
59 }
60
61 private displayWaitingForLiveInfo (translations: Translations) {
62 this.playerHTML.displayInformation('This live has not started yet.', translations)
63 }
64
65 private displayEndedLiveInfo (translations: Translations) {
66 this.playerHTML.displayInformation('This live has ended.', translations)
67
68 }
69}
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 {
6 private readonly wrapperElement: HTMLElement 6 private readonly wrapperElement: HTMLElement
7 7
8 private playerElement: HTMLVideoElement 8 private playerElement: HTMLVideoElement
9 private informationElement: HTMLDivElement
9 10
10 constructor (private readonly videoWrapperId: string) { 11 constructor (private readonly videoWrapperId: string) {
11 this.wrapperElement = document.getElementById(this.videoWrapperId) 12 this.wrapperElement = document.getElementById(this.videoWrapperId)
@@ -66,6 +67,20 @@ export class PlayerHTML {
66 placeholder.style.display = 'none' 67 placeholder.style.display = 'none'
67 } 68 }
68 69
70 displayInformation (text: string, translations: Translations) {
71 if (this.informationElement) this.removeInformation()
72
73 this.informationElement = document.createElement('div')
74 this.informationElement.className = 'player-information'
75 this.informationElement.innerText = peertubeTranslate(text, translations)
76
77 document.body.appendChild(this.informationElement)
78 }
79
80 removeInformation () {
81 this.removeElement(this.informationElement)
82 }
83
69 private getPlaceholderElement () { 84 private getPlaceholderElement () {
70 return document.getElementById('placeholder-preview') 85 return document.getElementById('placeholder-preview')
71 } 86 }
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'
30import { getActivityStreamDuration } from './activitypub/activity' 30import { getActivityStreamDuration } from './activitypub/activity'
31import { getBiggestActorImage } from './actor-image' 31import { getBiggestActorImage } from './actor-image'
32import { ServerConfigManager } from './server-config-manager' 32import { ServerConfigManager } from './server-config-manager'
33import { isTestInstance } from '@server/helpers/core-utils'
33 34
34type Tags = { 35type Tags = {
35 ogType: string 36 ogType: string
@@ -232,7 +233,10 @@ class ClientHtml {
232 static async getEmbedHTML () { 233 static async getEmbedHTML () {
233 const path = ClientHtml.getEmbedPath() 234 const path = ClientHtml.getEmbedPath()
234 235
235 if (ClientHtml.htmlCache[path]) return ClientHtml.htmlCache[path] 236 // Disable HTML cache in dev mode because webpack can regenerate JS files
237 if (!isTestInstance() && ClientHtml.htmlCache[path]) {
238 return ClientHtml.htmlCache[path]
239 }
236 240
237 const buffer = await readFile(path) 241 const buffer = await readFile(path)
238 const serverConfig = await ServerConfigManager.Instance.getHTMLServerConfig() 242 const serverConfig = await ServerConfigManager.Instance.getHTMLServerConfig()