diff options
author | Chocobozzz <me@florianbigard.com> | 2021-10-15 11:37:30 +0200 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2021-10-15 11:45:03 +0200 |
commit | 1e2fe802d1f82b81ac30fd9ca267da12f75032a2 (patch) | |
tree | c1d7142f825e277b56b5cd73f9de601ddcb20077 | |
parent | 3eb7ee658db32d270553046eeb77afc679a71466 (diff) | |
download | PeerTube-1e2fe802d1f82b81ac30fd9ca267da12f75032a2.tar.gz PeerTube-1e2fe802d1f82b81ac30fd9ca267da12f75032a2.tar.zst PeerTube-1e2fe802d1f82b81ac30fd9ca267da12f75032a2.zip |
Force live type specification in first step
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 | |||
2 | import { forkJoin } from 'rxjs' | 1 | import { forkJoin } from 'rxjs' |
3 | import { AfterViewInit, Component, EventEmitter, OnInit, Output } from '@angular/core' | 2 | import { AfterViewInit, Component, EventEmitter, OnInit, Output } from '@angular/core' |
4 | import { Router } from '@angular/router' | 3 | import { 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 | ||
88 | export const VIDEO_SCHEDULE_PUBLICATION_AT_VALIDATOR: BuildFormValidator = { | 88 | export 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 | |||
6 | ng-select { | 10 | ng-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' | |||
11 | import { VideoFileModel } from '@server/models/video/video-file' | 11 | import { VideoFileModel } from '@server/models/video/video-file' |
12 | import { VideoStreamingPlaylistModel } from '@server/models/video/video-streaming-playlist' | 12 | import { VideoStreamingPlaylistModel } from '@server/models/video/video-streaming-playlist' |
13 | import { FilteredModelAttributes } from '@server/types' | 13 | import { FilteredModelAttributes } from '@server/types' |
14 | import { isStreamingPlaylist, MChannelId, MStreamingPlaylistVideo, MVideo, MVideoFile, MVideoId } from '@server/types/models' | 14 | import { isStreamingPlaylist, MChannelId, MStreamingPlaylistVideo, MVideo, MVideoId } from '@server/types/models' |
15 | import { | 15 | import { |
16 | ActivityHashTagObject, | 16 | ActivityHashTagObject, |
17 | ActivityMagnetUrlObject, | 17 | ActivityMagnetUrlObject, |
@@ -110,7 +110,7 @@ function getFileAttributesFromUrl ( | |||
110 | return attributes | 110 | return attributes |
111 | } | 111 | } |
112 | 112 | ||
113 | function getStreamingPlaylistAttributesFromObject (video: MVideoId, videoObject: VideoObject, videoFiles: MVideoFile[]) { | 113 | function 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 }) |