aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.html17
-rw-r--r--client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.scss12
-rw-r--r--client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.ts28
-rw-r--r--client/src/app/+videos/+video-edit/video-add-components/video-send.scss3
-rw-r--r--client/src/app/shared/form-validators/video-validators.ts2
-rw-r--r--client/src/app/shared/shared-forms/select/select-shared.component.scss4
-rw-r--r--client/src/app/shared/shared-video-live/live-stream-information.component.html2
-rw-r--r--client/src/sass/include/_mixins.scss2
-rw-r--r--server/lib/activitypub/videos/shared/abstract-builder.ts2
-rw-r--r--server/lib/activitypub/videos/shared/object-to-model-attributes.ts6
10 files changed, 67 insertions, 11 deletions
diff --git a/client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.html b/client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.html
index fddb08e0b..fce5a1ee5 100644
--- a/client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.html
+++ b/client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.html
@@ -16,7 +16,24 @@
16 ></my-select-options> 16 ></my-select-options>
17 </div> 17 </div>
18 18
19 <div class="form-group live-type">
20 <div class="peertube-radio-container">
21 <input type="radio" id="permanentLiveFalse" [(ngModel)]="firstStepPermanentLive" [value]="false">
22 <label i18n for="permanentLiveFalse" class="radio">Normal live</label>
23
24 <span class="form-group-description">{{ getNormalLiveDescription() }}</span>
25 </div>
26
27 <div class="peertube-radio-container">
28 <input type="radio" id="permanentLiveTrue" [(ngModel)]="firstStepPermanentLive" [value]="true">
29 <label i18n for="permanentLiveTrue" class="radio">Permanent/recurring live</label>
30
31 <span class="form-group-description" i18n>{{ getPermanentLiveDescription() }}</span>
32 </div>
33 </div>
34
19 <input 35 <input
36 [disabled]="firstStepPermanentLive !== true && firstStepPermanentLive !== false"
20 type="button" i18n-value value="Go Live" (click)="goLive()" 37 type="button" i18n-value value="Go Live" (click)="goLive()"
21 /> 38 />
22 </div> 39 </div>
diff --git a/client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.scss b/client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.scss
new file mode 100644
index 000000000..09b71018f
--- /dev/null
+++ b/client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.scss
@@ -0,0 +1,12 @@
1.live-type {
2 margin-top: 15px;
3}
4
5.peertube-radio-container {
6 width: 250px;
7
8 .form-group-description {
9 white-space: nowrap;
10 }
11}
12
diff --git a/client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.ts b/client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.ts
index ee7011b4c..01eabb0d7 100644
--- a/client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.ts
+++ b/client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.ts
@@ -1,4 +1,3 @@
1
2import { forkJoin } from 'rxjs' 1import { forkJoin } from 'rxjs'
3import { AfterViewInit, Component, EventEmitter, OnInit, Output } from '@angular/core' 2import { AfterViewInit, Component, EventEmitter, OnInit, Output } from '@angular/core'
4import { Router } from '@angular/router' 3import { Router } from '@angular/router'
@@ -16,6 +15,7 @@ import { VideoSend } from './video-send'
16 templateUrl: './video-go-live.component.html', 15 templateUrl: './video-go-live.component.html',
17 styleUrls: [ 16 styleUrls: [
18 '../shared/video-edit.component.scss', 17 '../shared/video-edit.component.scss',
18 './video-go-live.component.scss',
19 './video-send.scss' 19 './video-send.scss'
20 ] 20 ]
21}) 21})
@@ -23,6 +23,8 @@ export class VideoGoLiveComponent extends VideoSend implements OnInit, AfterView
23 @Output() firstStepDone = new EventEmitter<string>() 23 @Output() firstStepDone = new EventEmitter<string>()
24 @Output() firstStepError = new EventEmitter<void>() 24 @Output() firstStepError = new EventEmitter<void>()
25 25
26 firstStepPermanentLive: boolean
27
26 isInUpdateForm = false 28 isInUpdateForm = false
27 29
28 liveVideo: LiveVideo 30 liveVideo: LiveVideo
@@ -70,8 +72,8 @@ export class VideoGoLiveComponent extends VideoSend implements OnInit, AfterView
70 waitTranscoding: true, 72 waitTranscoding: true,
71 commentsEnabled: true, 73 commentsEnabled: true,
72 downloadEnabled: true, 74 downloadEnabled: true,
73 permanentLive: false, 75 permanentLive: this.firstStepPermanentLive,
74 saveReplay: false, 76 saveReplay: this.firstStepPermanentLive === false && this.isReplayAllowed(),
75 channelId: this.firstStepChannelId 77 channelId: this.firstStepChannelId
76 } 78 }
77 79
@@ -154,6 +156,26 @@ export class VideoGoLiveComponent extends VideoSend implements OnInit, AfterView
154 return this.form.value['saveReplay'] === true 156 return this.form.value['saveReplay'] === true
155 } 157 }
156 158
159 getNormalLiveDescription () {
160 if (this.isReplayAllowed()) {
161 return $localize`Stream only once and save a replay of your live`
162 }
163
164 return $localize`Stream only once`
165 }
166
167 getPermanentLiveDescription () {
168 if (this.isReplayAllowed()) {
169 return $localize`Stream multiple times, replays can't be saved`
170 }
171
172 return $localize`Stream multiple times using the same URL`
173 }
174
175 private isReplayAllowed () {
176 return this.serverConfig.live.allowReplay
177 }
178
157 private fetchVideoLive () { 179 private fetchVideoLive () {
158 this.liveVideoService.getVideoLive(this.videoId) 180 this.liveVideoService.getVideoLive(this.videoId)
159 .subscribe({ 181 .subscribe({
diff --git a/client/src/app/+videos/+video-edit/video-add-components/video-send.scss b/client/src/app/+videos/+video-edit/video-add-components/video-send.scss
index e601c3dff..7284ad00f 100644
--- a/client/src/app/+videos/+video-edit/video-add-components/video-send.scss
+++ b/client/src/app/+videos/+video-edit/video-add-components/video-send.scss
@@ -1,7 +1,7 @@
1@use '_variables' as *; 1@use '_variables' as *;
2@use '_mixins' as *; 2@use '_mixins' as *;
3 3
4$width-size: 190px; 4$width-size: 250px;
5 5
6.alert.alert-danger { 6.alert.alert-danger {
7 text-align: center; 7 text-align: center;
@@ -29,6 +29,7 @@ $width-size: 190px;
29 my-select-options ::ng-deep ng-select, 29 my-select-options ::ng-deep ng-select,
30 my-select-channel ::ng-deep ng-select { 30 my-select-channel ::ng-deep ng-select {
31 width: $width-size; 31 width: $width-size;
32
32 @media screen and (max-width: $width-size) { 33 @media screen and (max-width: $width-size) {
33 width: 100%; 34 width: 100%;
34 } 35 }
diff --git a/client/src/app/shared/form-validators/video-validators.ts b/client/src/app/shared/form-validators/video-validators.ts
index f96189aa2..ab6f21a35 100644
--- a/client/src/app/shared/form-validators/video-validators.ts
+++ b/client/src/app/shared/form-validators/video-validators.ts
@@ -86,7 +86,7 @@ export const VIDEO_SUPPORT_VALIDATOR: BuildFormValidator = {
86} 86}
87 87
88export const VIDEO_SCHEDULE_PUBLICATION_AT_VALIDATOR: BuildFormValidator = { 88export const VIDEO_SCHEDULE_PUBLICATION_AT_VALIDATOR: BuildFormValidator = {
89 VALIDATORS: [ ], 89 VALIDATORS: [ ], // Required is set dynamically
90 MESSAGES: { 90 MESSAGES: {
91 required: $localize`A date is required to schedule video update.` 91 required: $localize`A date is required to schedule video update.`
92 } 92 }
diff --git a/client/src/app/shared/shared-forms/select/select-shared.component.scss b/client/src/app/shared/shared-forms/select/select-shared.component.scss
index 1e9b60fec..f7b001fe8 100644
--- a/client/src/app/shared/shared-forms/select/select-shared.component.scss
+++ b/client/src/app/shared/shared-forms/select/select-shared.component.scss
@@ -3,6 +3,10 @@
3 3
4$form-base-input-width: auto; 4$form-base-input-width: auto;
5 5
6.text-muted {
7 font-size: 90%;
8}
9
6ng-select { 10ng-select {
7 width: $form-base-input-width; 11 width: $form-base-input-width;
8 12
diff --git a/client/src/app/shared/shared-video-live/live-stream-information.component.html b/client/src/app/shared/shared-video-live/live-stream-information.component.html
index d6ee67ba9..7df1e5bc2 100644
--- a/client/src/app/shared/shared-video-live/live-stream-information.component.html
+++ b/client/src/app/shared/shared-video-live/live-stream-information.component.html
@@ -7,7 +7,7 @@
7 7
8 <div class="modal-body" *ngIf="live"> 8 <div class="modal-body" *ngIf="live">
9 <div> 9 <div>
10 <div class="badge badge-info" *ngIf="live.permanentLive" i18n>Permanent live</div> 10 <div class="badge badge-info" *ngIf="live.permanentLive" i18n>Permanent/Recurring live</div>
11 <div class="badge badge-info" *ngIf="live.saveReplay" i18n>Replay will be saved</div> 11 <div class="badge badge-info" *ngIf="live.saveReplay" i18n>Replay will be saved</div>
12 </div> 12 </div>
13 13
diff --git a/client/src/sass/include/_mixins.scss b/client/src/sass/include/_mixins.scss
index 0634f4677..9e510771e 100644
--- a/client/src/sass/include/_mixins.scss
+++ b/client/src/sass/include/_mixins.scss
@@ -478,7 +478,7 @@
478 478
479 .form-group-description { 479 .form-group-description {
480 display: block; 480 display: block;
481 margin-top: -10px; 481 margin-top: -7px;
482 margin-bottom: 10px; 482 margin-bottom: 10px;
483 margin-left: 29px; 483 margin-left: 29px;
484 } 484 }
diff --git a/server/lib/activitypub/videos/shared/abstract-builder.ts b/server/lib/activitypub/videos/shared/abstract-builder.ts
index f995fe637..788223b48 100644
--- a/server/lib/activitypub/videos/shared/abstract-builder.ts
+++ b/server/lib/activitypub/videos/shared/abstract-builder.ts
@@ -119,7 +119,7 @@ export abstract class APVideoAbstractBuilder {
119 } 119 }
120 120
121 protected async setStreamingPlaylists (video: MVideoFullLight, t: Transaction) { 121 protected async setStreamingPlaylists (video: MVideoFullLight, t: Transaction) {
122 const streamingPlaylistAttributes = getStreamingPlaylistAttributesFromObject(video, this.videoObject, video.VideoFiles || []) 122 const streamingPlaylistAttributes = getStreamingPlaylistAttributesFromObject(video, this.videoObject)
123 const newStreamingPlaylists = streamingPlaylistAttributes.map(a => new VideoStreamingPlaylistModel(a)) 123 const newStreamingPlaylists = streamingPlaylistAttributes.map(a => new VideoStreamingPlaylistModel(a))
124 124
125 // Remove video playlists that do not exist anymore 125 // Remove video playlists that do not exist anymore
diff --git a/server/lib/activitypub/videos/shared/object-to-model-attributes.ts b/server/lib/activitypub/videos/shared/object-to-model-attributes.ts
index 50ca4e673..1e1479869 100644
--- a/server/lib/activitypub/videos/shared/object-to-model-attributes.ts
+++ b/server/lib/activitypub/videos/shared/object-to-model-attributes.ts
@@ -11,7 +11,7 @@ import { VideoCaptionModel } from '@server/models/video/video-caption'
11import { VideoFileModel } from '@server/models/video/video-file' 11import { VideoFileModel } from '@server/models/video/video-file'
12import { VideoStreamingPlaylistModel } from '@server/models/video/video-streaming-playlist' 12import { VideoStreamingPlaylistModel } from '@server/models/video/video-streaming-playlist'
13import { FilteredModelAttributes } from '@server/types' 13import { FilteredModelAttributes } from '@server/types'
14import { isStreamingPlaylist, MChannelId, MStreamingPlaylistVideo, MVideo, MVideoFile, MVideoId } from '@server/types/models' 14import { isStreamingPlaylist, MChannelId, MStreamingPlaylistVideo, MVideo, MVideoId } from '@server/types/models'
15import { 15import {
16 ActivityHashTagObject, 16 ActivityHashTagObject,
17 ActivityMagnetUrlObject, 17 ActivityMagnetUrlObject,
@@ -110,7 +110,7 @@ function getFileAttributesFromUrl (
110 return attributes 110 return attributes
111} 111}
112 112
113function getStreamingPlaylistAttributesFromObject (video: MVideoId, videoObject: VideoObject, videoFiles: MVideoFile[]) { 113function getStreamingPlaylistAttributesFromObject (video: MVideoId, videoObject: VideoObject) {
114 const playlistUrls = videoObject.url.filter(u => isAPStreamingPlaylistUrlObject(u)) as ActivityPlaylistUrlObject[] 114 const playlistUrls = videoObject.url.filter(u => isAPStreamingPlaylistUrlObject(u)) as ActivityPlaylistUrlObject[]
115 if (playlistUrls.length === 0) return [] 115 if (playlistUrls.length === 0) return []
116 116
@@ -118,7 +118,7 @@ function getStreamingPlaylistAttributesFromObject (video: MVideoId, videoObject:
118 for (const playlistUrlObject of playlistUrls) { 118 for (const playlistUrlObject of playlistUrls) {
119 const segmentsSha256UrlObject = playlistUrlObject.tag.find(isAPPlaylistSegmentHashesUrlObject) 119 const segmentsSha256UrlObject = playlistUrlObject.tag.find(isAPPlaylistSegmentHashesUrlObject)
120 120
121 let files: unknown[] = playlistUrlObject.tag.filter(u => isAPVideoUrlObject(u)) as ActivityVideoUrlObject[] 121 const files: unknown[] = playlistUrlObject.tag.filter(u => isAPVideoUrlObject(u)) as ActivityVideoUrlObject[]
122 122
123 if (!segmentsSha256UrlObject) { 123 if (!segmentsSha256UrlObject) {
124 logger.warn('No segment sha256 URL found in AP playlist object.', { playlistUrl: playlistUrlObject }) 124 logger.warn('No segment sha256 URL found in AP playlist object.', { playlistUrl: playlistUrlObject })