aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app/+videos
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2020-09-17 09:20:52 +0200
committerChocobozzz <chocobozzz@cpy.re>2020-11-09 15:33:04 +0100
commitc6c0fa6cd8fe8f752463d8982c3dbcd448739c4e (patch)
tree79304b0152b0a38d33b26e65d4acdad0da4032a7 /client/src/app/+videos
parent110d463fece85e87a26aca48a6048ae0017a27b3 (diff)
downloadPeerTube-c6c0fa6cd8fe8f752463d8982c3dbcd448739c4e.tar.gz
PeerTube-c6c0fa6cd8fe8f752463d8982c3dbcd448739c4e.tar.zst
PeerTube-c6c0fa6cd8fe8f752463d8982c3dbcd448739c4e.zip
Live streaming implementation first step
Diffstat (limited to 'client/src/app/+videos')
-rw-r--r--client/src/app/+videos/+video-edit/shared/video-edit-utils.ts35
-rw-r--r--client/src/app/+videos/+video-edit/shared/video-edit.component.html23
-rw-r--r--client/src/app/+videos/+video-edit/shared/video-edit.component.ts16
-rw-r--r--client/src/app/+videos/+video-edit/shared/video-edit.type.ts1
-rw-r--r--client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.html47
-rw-r--r--client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.ts129
-rw-r--r--client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.ts8
-rw-r--r--client/src/app/+videos/+video-edit/video-add-components/video-import-url.component.ts29
-rw-r--r--client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts1
-rw-r--r--client/src/app/+videos/+video-edit/video-add.component.html12
-rw-r--r--client/src/app/+videos/+video-edit/video-add.component.ts13
-rw-r--r--client/src/app/+videos/+video-edit/video-add.module.ts4
-rw-r--r--client/src/app/+videos/+video-edit/video-update.component.html1
-rw-r--r--client/src/app/+videos/+video-edit/video-update.component.ts38
-rw-r--r--client/src/app/+videos/+video-edit/video-update.resolver.ts61
15 files changed, 318 insertions, 100 deletions
diff --git a/client/src/app/+videos/+video-edit/shared/video-edit-utils.ts b/client/src/app/+videos/+video-edit/shared/video-edit-utils.ts
new file mode 100644
index 000000000..3a7dbed36
--- /dev/null
+++ b/client/src/app/+videos/+video-edit/shared/video-edit-utils.ts
@@ -0,0 +1,35 @@
1import { FormGroup } from '@angular/forms'
2import { VideoEdit } from '@app/shared/shared-main'
3
4function hydrateFormFromVideo (formGroup: FormGroup, video: VideoEdit, thumbnailFiles: boolean) {
5 formGroup.patchValue(video.toFormPatch())
6
7 if (thumbnailFiles === false) return
8
9 const objects = [
10 {
11 url: 'thumbnailUrl',
12 name: 'thumbnailfile'
13 },
14 {
15 url: 'previewUrl',
16 name: 'previewfile'
17 }
18 ]
19
20 for (const obj of objects) {
21 if (!video[obj.url]) continue
22
23 fetch(video[obj.url])
24 .then(response => response.blob())
25 .then(data => {
26 formGroup.patchValue({
27 [ obj.name ]: data
28 })
29 })
30 }
31}
32
33export {
34 hydrateFormFromVideo
35}
diff --git a/client/src/app/+videos/+video-edit/shared/video-edit.component.html b/client/src/app/+videos/+video-edit/shared/video-edit.component.html
index 842997b20..c444dd8d3 100644
--- a/client/src/app/+videos/+video-edit/shared/video-edit.component.html
+++ b/client/src/app/+videos/+video-edit/shared/video-edit.component.html
@@ -195,6 +195,29 @@
195 </ng-template> 195 </ng-template>
196 </ng-container> 196 </ng-container>
197 197
198 <ng-container ngbNavItem *ngIf="videoLive">
199 <a ngbNavLink i18n>Live settings</a>
200
201 <ng-template ngbNavContent>
202 <div class="row live-settings">
203 <div class="col-md-12">
204
205 <div class="form-group">
206 <label for="videoLiveRTMPUrl" i18n>Live RTMP Url</label>
207 <my-input-readonly-copy id="videoLiveRTMPUrl" [value]="videoLive.rtmpUrl"></my-input-readonly-copy>
208 </div>
209
210 <div class="form-group">
211 <label for="videoLiveStreamKey" i18n>Live stream key</label>
212 <my-input-readonly-copy id="videoLiveStreamKey" [value]="videoLive.streamKey"></my-input-readonly-copy>
213 </div>
214 </div>
215 </div>
216 </ng-template>
217
218 </ng-container>
219
220
198 <ng-container ngbNavItem> 221 <ng-container ngbNavItem>
199 <a ngbNavLink i18n>Advanced settings</a> 222 <a ngbNavLink i18n>Advanced settings</a>
200 223
diff --git a/client/src/app/+videos/+video-edit/shared/video-edit.component.ts b/client/src/app/+videos/+video-edit/shared/video-edit.component.ts
index f04111e69..bee65184b 100644
--- a/client/src/app/+videos/+video-edit/shared/video-edit.component.ts
+++ b/client/src/app/+videos/+video-edit/shared/video-edit.component.ts
@@ -20,10 +20,11 @@ import {
20import { FormReactiveValidationMessages, FormValidatorService, SelectChannelItem } from '@app/shared/shared-forms' 20import { FormReactiveValidationMessages, FormValidatorService, SelectChannelItem } from '@app/shared/shared-forms'
21import { InstanceService } from '@app/shared/shared-instance' 21import { InstanceService } from '@app/shared/shared-instance'
22import { VideoCaptionEdit, VideoEdit, VideoService } from '@app/shared/shared-main' 22import { VideoCaptionEdit, VideoEdit, VideoService } from '@app/shared/shared-main'
23import { ServerConfig, VideoConstant, VideoPrivacy } from '@shared/models' 23import { ServerConfig, VideoConstant, VideoLive, VideoPrivacy } from '@shared/models'
24import { RegisterClientFormFieldOptions, RegisterClientVideoFieldOptions } from '@shared/models/plugins/register-client-form-field.model' 24import { RegisterClientFormFieldOptions, RegisterClientVideoFieldOptions } from '@shared/models/plugins/register-client-form-field.model'
25import { I18nPrimengCalendarService } from './i18n-primeng-calendar.service' 25import { I18nPrimengCalendarService } from './i18n-primeng-calendar.service'
26import { VideoCaptionAddModalComponent } from './video-caption-add-modal.component' 26import { VideoCaptionAddModalComponent } from './video-caption-add-modal.component'
27import { VideoEditType } from './video-edit.type'
27 28
28type VideoLanguages = VideoConstant<string> & { group?: string } 29type VideoLanguages = VideoConstant<string> & { group?: string }
29 30
@@ -40,7 +41,8 @@ export class VideoEditComponent implements OnInit, OnDestroy {
40 @Input() schedulePublicationPossible = true 41 @Input() schedulePublicationPossible = true
41 @Input() videoCaptions: (VideoCaptionEdit & { captionPath?: string })[] = [] 42 @Input() videoCaptions: (VideoCaptionEdit & { captionPath?: string })[] = []
42 @Input() waitTranscodingEnabled = true 43 @Input() waitTranscodingEnabled = true
43 @Input() type: 'import-url' | 'import-torrent' | 'upload' | 'update' 44 @Input() type: VideoEditType
45 @Input() videoLive: VideoLive
44 46
45 @ViewChild('videoCaptionAddModal', { static: true }) videoCaptionAddModal: VideoCaptionAddModalComponent 47 @ViewChild('videoCaptionAddModal', { static: true }) videoCaptionAddModal: VideoCaptionAddModalComponent
46 48
@@ -124,7 +126,8 @@ export class VideoEditComponent implements OnInit, OnDestroy {
124 previewfile: null, 126 previewfile: null,
125 support: VIDEO_SUPPORT_VALIDATOR, 127 support: VIDEO_SUPPORT_VALIDATOR,
126 schedulePublicationAt: VIDEO_SCHEDULE_PUBLICATION_AT_VALIDATOR, 128 schedulePublicationAt: VIDEO_SCHEDULE_PUBLICATION_AT_VALIDATOR,
127 originallyPublishedAt: VIDEO_ORIGINALLY_PUBLISHED_AT_VALIDATOR 129 originallyPublishedAt: VIDEO_ORIGINALLY_PUBLISHED_AT_VALIDATOR,
130 liveStreamKey: null
128 } 131 }
129 132
130 this.formValidatorService.updateForm( 133 this.formValidatorService.updateForm(
@@ -320,7 +323,12 @@ export class VideoEditComponent implements OnInit, OnDestroy {
320 const currentSupport = this.form.value[ 'support' ] 323 const currentSupport = this.form.value[ 'support' ]
321 324
322 // First time we set the channel? 325 // First time we set the channel?
323 if (isNaN(oldChannelId) && !currentSupport) return this.updateSupportField(newChannel.support) 326 if (isNaN(oldChannelId)) {
327 // Fill support if it's empty
328 if (!currentSupport) this.updateSupportField(newChannel.support)
329
330 return
331 }
324 332
325 const oldChannel = this.userVideoChannels.find(c => c.id === oldChannelId) 333 const oldChannel = this.userVideoChannels.find(c => c.id === oldChannelId)
326 if (!newChannel || !oldChannel) { 334 if (!newChannel || !oldChannel) {
diff --git a/client/src/app/+videos/+video-edit/shared/video-edit.type.ts b/client/src/app/+videos/+video-edit/shared/video-edit.type.ts
new file mode 100644
index 000000000..fdbe9505c
--- /dev/null
+++ b/client/src/app/+videos/+video-edit/shared/video-edit.type.ts
@@ -0,0 +1 @@
export type VideoEditType = 'update' | 'upload' | 'import-url' | 'import-torrent' | 'go-live'
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
new file mode 100644
index 000000000..6997f5388
--- /dev/null
+++ b/client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.html
@@ -0,0 +1,47 @@
1<div *ngIf="!isInUpdateForm" class="upload-video-container">
2 <div class="first-step-block">
3 <my-global-icon class="upload-icon" iconName="upload" aria-hidden="true"></my-global-icon>
4
5 <div class="form-group">
6 <label i18n for="first-step-channel">Channel</label>
7 <my-select-channel
8 labelForId="first-step-channel" [items]="userVideoChannels" [(ngModel)]="firstStepChannelId"
9 ></my-select-channel>
10 </div>
11
12 <div class="form-group">
13 <label i18n for="first-step-privacy">Privacy</label>
14 <my-select-options
15 labelForId="first-step-privacy" [items]="videoPrivacies" [(ngModel)]="firstStepPrivacyId"
16 ></my-select-options>
17 </div>
18
19 <input
20 type="button" i18n-value value="Go Live" (click)="goLive()"
21 />
22 </div>
23</div>
24
25<div *ngIf="error" class="alert alert-danger">
26 <div i18n>Sorry, but something went wrong</div>
27 {{ error }}
28</div>
29
30<!-- Hidden because we want to load the component -->
31<form [hidden]="!isInUpdateForm" novalidate [formGroup]="form">
32 <my-video-edit
33 [form]="form" [formErrors]="formErrors" [videoCaptions]="videoCaptions" [schedulePublicationPossible]="false"
34 [validationMessages]="validationMessages" [userVideoChannels]="userVideoChannels" [videoLive]="videoLive"
35 type="go-live"
36 ></my-video-edit>
37
38 <div class="submit-container">
39 <div class="submit-button"
40 (click)="updateSecondStep()"
41 [ngClass]="{ disabled: !form.valid }"
42 >
43 <my-global-icon iconName="circle-tick" aria-hidden="true"></my-global-icon>
44 <input type="button" i18n-value value="Update" />
45 </div>
46 </div>
47</form>
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
new file mode 100644
index 000000000..64fd4c4d4
--- /dev/null
+++ b/client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.ts
@@ -0,0 +1,129 @@
1
2import { Component, EventEmitter, OnInit, Output } from '@angular/core'
3import { Router } from '@angular/router'
4import { AuthService, CanComponentDeactivate, Notifier, ServerService } from '@app/core'
5import { scrollToTop } from '@app/helpers'
6import { FormValidatorService } from '@app/shared/shared-forms'
7import { VideoCaptionService, VideoEdit, VideoService, VideoLiveService } from '@app/shared/shared-main'
8import { LoadingBarService } from '@ngx-loading-bar/core'
9import { VideoCreate, VideoLive, VideoPrivacy } from '@shared/models'
10import { VideoSend } from './video-send'
11
12@Component({
13 selector: 'my-video-go-live',
14 templateUrl: './video-go-live.component.html',
15 styleUrls: [
16 '../shared/video-edit.component.scss',
17 './video-send.scss'
18 ]
19})
20export class VideoGoLiveComponent extends VideoSend implements OnInit, CanComponentDeactivate {
21 @Output() firstStepDone = new EventEmitter<string>()
22 @Output() firstStepError = new EventEmitter<void>()
23
24 isInUpdateForm = false
25
26 videoLive: VideoLive
27 videoId: number
28 videoUUID: string
29 error: string
30
31 protected readonly DEFAULT_VIDEO_PRIVACY = VideoPrivacy.PUBLIC
32
33 constructor (
34 protected formValidatorService: FormValidatorService,
35 protected loadingBar: LoadingBarService,
36 protected notifier: Notifier,
37 protected authService: AuthService,
38 protected serverService: ServerService,
39 protected videoService: VideoService,
40 protected videoCaptionService: VideoCaptionService,
41 private videoLiveService: VideoLiveService,
42 private router: Router
43 ) {
44 super()
45 }
46
47 ngOnInit () {
48 super.ngOnInit()
49 }
50
51 canDeactivate () {
52 return { canDeactivate: true }
53 }
54
55 goLive () {
56 const video: VideoCreate = {
57 name: 'Live',
58 privacy: VideoPrivacy.PRIVATE,
59 nsfw: this.serverConfig.instance.isNSFW,
60 waitTranscoding: true,
61 commentsEnabled: true,
62 downloadEnabled: true,
63 channelId: this.firstStepChannelId
64 }
65
66 this.firstStepDone.emit(name)
67
68 // Go live in private mode, but correctly fill the update form with the first user choice
69 const toPatch = Object.assign({}, video, { privacy: this.firstStepPrivacyId })
70 this.form.patchValue(toPatch)
71
72 this.videoLiveService.goLive(video).subscribe(
73 res => {
74 this.videoId = res.video.id
75 this.videoUUID = res.video.uuid
76 this.isInUpdateForm = true
77
78 this.fetchVideoLive()
79 },
80
81 err => {
82 this.firstStepError.emit()
83 this.notifier.error(err.message)
84 }
85 )
86 }
87
88 updateSecondStep () {
89 if (this.checkForm() === false) {
90 return
91 }
92
93 const video = new VideoEdit()
94 video.patch(this.form.value)
95 video.id = this.videoId
96 video.uuid = this.videoUUID
97
98 // Update the video
99 this.updateVideoAndCaptions(video)
100 .subscribe(
101 () => {
102 this.notifier.success($localize`Live published.`)
103
104 this.router.navigate([ '/videos/watch', video.uuid ])
105 },
106
107 err => {
108 this.error = err.message
109 scrollToTop()
110 console.error(err)
111 }
112 )
113
114 }
115
116 private fetchVideoLive () {
117 this.videoLiveService.getVideoLive(this.videoId)
118 .subscribe(
119 videoLive => {
120 this.videoLive = videoLive
121 },
122
123 err => {
124 this.firstStepError.emit()
125 this.notifier.error(err.message)
126 }
127 )
128 }
129}
diff --git a/client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.ts b/client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.ts
index e9ad8af7a..64e887987 100644
--- a/client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.ts
+++ b/client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.ts
@@ -6,6 +6,7 @@ import { FormValidatorService } from '@app/shared/shared-forms'
6import { VideoCaptionService, VideoEdit, VideoImportService, VideoService } from '@app/shared/shared-main' 6import { VideoCaptionService, VideoEdit, VideoImportService, VideoService } from '@app/shared/shared-main'
7import { LoadingBarService } from '@ngx-loading-bar/core' 7import { LoadingBarService } from '@ngx-loading-bar/core'
8import { VideoPrivacy, VideoUpdate } from '@shared/models' 8import { VideoPrivacy, VideoUpdate } from '@shared/models'
9import { hydrateFormFromVideo } from '../shared/video-edit-utils'
9import { VideoSend } from './video-send' 10import { VideoSend } from './video-send'
10 11
11@Component({ 12@Component({
@@ -99,7 +100,7 @@ export class VideoImportTorrentComponent extends VideoSend implements OnInit, Ca
99 previewUrl: null 100 previewUrl: null
100 })) 101 }))
101 102
102 this.hydrateFormFromVideo() 103 hydrateFormFromVideo(this.form, this.video, false)
103 }, 104 },
104 105
105 err => { 106 err => {
@@ -136,10 +137,5 @@ export class VideoImportTorrentComponent extends VideoSend implements OnInit, Ca
136 console.error(err) 137 console.error(err)
137 } 138 }
138 ) 139 )
139
140 }
141
142 private hydrateFormFromVideo () {
143 this.form.patchValue(this.video.toFormPatch())
144 } 140 }
145} 141}
diff --git a/client/src/app/+videos/+video-edit/video-add-components/video-import-url.component.ts b/client/src/app/+videos/+video-edit/video-add-components/video-import-url.component.ts
index 8bad81097..47f59a5d0 100644
--- a/client/src/app/+videos/+video-edit/video-add-components/video-import-url.component.ts
+++ b/client/src/app/+videos/+video-edit/video-add-components/video-import-url.component.ts
@@ -7,6 +7,7 @@ import { FormValidatorService } from '@app/shared/shared-forms'
7import { VideoCaptionService, VideoEdit, VideoImportService, VideoService } from '@app/shared/shared-main' 7import { VideoCaptionService, VideoEdit, VideoImportService, VideoService } from '@app/shared/shared-main'
8import { LoadingBarService } from '@ngx-loading-bar/core' 8import { LoadingBarService } from '@ngx-loading-bar/core'
9import { VideoPrivacy, VideoUpdate } from '@shared/models' 9import { VideoPrivacy, VideoUpdate } from '@shared/models'
10import { hydrateFormFromVideo } from '../shared/video-edit-utils'
10import { VideoSend } from './video-send' 11import { VideoSend } from './video-send'
11 12
12@Component({ 13@Component({
@@ -109,7 +110,7 @@ export class VideoImportUrlComponent extends VideoSend implements OnInit, CanCom
109 110
110 this.videoCaptions = videoCaptions 111 this.videoCaptions = videoCaptions
111 112
112 this.hydrateFormFromVideo() 113 hydrateFormFromVideo(this.form, this.video, true)
113 }, 114 },
114 115
115 err => { 116 err => {
@@ -146,31 +147,5 @@ export class VideoImportUrlComponent extends VideoSend implements OnInit, CanCom
146 console.error(err) 147 console.error(err)
147 } 148 }
148 ) 149 )
149
150 }
151
152 private hydrateFormFromVideo () {
153 this.form.patchValue(this.video.toFormPatch())
154
155 const objects = [
156 {
157 url: 'thumbnailUrl',
158 name: 'thumbnailfile'
159 },
160 {
161 url: 'previewUrl',
162 name: 'previewfile'
163 }
164 ]
165
166 for (const obj of objects) {
167 fetch(this.video[obj.url])
168 .then(response => response.blob())
169 .then(data => {
170 this.form.patchValue({
171 [ obj.name ]: data
172 })
173 })
174 }
175 } 150 }
176} 151}
diff --git a/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts b/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts
index 32a17052a..258f5c7a0 100644
--- a/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts
+++ b/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts
@@ -157,7 +157,6 @@ export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy
157 this.waitTranscodingEnabled = false 157 this.waitTranscodingEnabled = false
158 } 158 }
159 159
160 const privacy = this.firstStepPrivacyId.toString()
161 const nsfw = this.serverConfig.instance.isNSFW 160 const nsfw = this.serverConfig.instance.isNSFW
162 const waitTranscoding = true 161 const waitTranscoding = true
163 const commentsEnabled = true 162 const commentsEnabled = true
diff --git a/client/src/app/+videos/+video-edit/video-add.component.html b/client/src/app/+videos/+video-edit/video-add.component.html
index 14d41f95b..bf2cc9c83 100644
--- a/client/src/app/+videos/+video-edit/video-add.component.html
+++ b/client/src/app/+videos/+video-edit/video-add.component.html
@@ -50,7 +50,17 @@
50 <my-video-import-torrent #videoImportTorrent (firstStepDone)="onFirstStepDone('import-torrent', $event)" (firstStepError)="onError()"></my-video-import-torrent> 50 <my-video-import-torrent #videoImportTorrent (firstStepDone)="onFirstStepDone('import-torrent', $event)" (firstStepError)="onError()"></my-video-import-torrent>
51 </ng-template> 51 </ng-template>
52 </ng-container> 52 </ng-container>
53
54 <ng-container ngbNavItem *ngIf="isVideoLiveEnabled()">
55 <a ngbNavLink>
56 <span i18n>Go live</span>
57 </a>
58
59 <ng-template ngbNavContent>
60 <my-video-go-live #videoGoLive (firstStepDone)="onFirstStepDone('go-live', $event)" (firstStepError)="onError()"></my-video-go-live>
61 </ng-template>
62 </ng-container>
53 </div> 63 </div>
54 64
55 <div [ngbNavOutlet]="nav"></div> 65 <div [ngbNavOutlet]="nav"></div>
56</div> \ No newline at end of file 66</div>
diff --git a/client/src/app/+videos/+video-edit/video-add.component.ts b/client/src/app/+videos/+video-edit/video-add.component.ts
index 94e85efc1..441d5a3db 100644
--- a/client/src/app/+videos/+video-edit/video-add.component.ts
+++ b/client/src/app/+videos/+video-edit/video-add.component.ts
@@ -1,6 +1,8 @@
1import { Component, HostListener, OnInit, ViewChild } from '@angular/core' 1import { Component, HostListener, OnInit, ViewChild } from '@angular/core'
2import { AuthService, AuthUser, CanComponentDeactivate, ServerService } from '@app/core' 2import { AuthService, AuthUser, CanComponentDeactivate, ServerService } from '@app/core'
3import { ServerConfig } from '@shared/models' 3import { ServerConfig } from '@shared/models'
4import { VideoEditType } from './shared/video-edit.type'
5import { VideoGoLiveComponent } from './video-add-components/video-go-live.component'
4import { VideoImportTorrentComponent } from './video-add-components/video-import-torrent.component' 6import { VideoImportTorrentComponent } from './video-add-components/video-import-torrent.component'
5import { VideoImportUrlComponent } from './video-add-components/video-import-url.component' 7import { VideoImportUrlComponent } from './video-add-components/video-import-url.component'
6import { VideoUploadComponent } from './video-add-components/video-upload.component' 8import { VideoUploadComponent } from './video-add-components/video-upload.component'
@@ -14,10 +16,11 @@ export class VideoAddComponent implements OnInit, CanComponentDeactivate {
14 @ViewChild('videoUpload') videoUpload: VideoUploadComponent 16 @ViewChild('videoUpload') videoUpload: VideoUploadComponent
15 @ViewChild('videoImportUrl') videoImportUrl: VideoImportUrlComponent 17 @ViewChild('videoImportUrl') videoImportUrl: VideoImportUrlComponent
16 @ViewChild('videoImportTorrent') videoImportTorrent: VideoImportTorrentComponent 18 @ViewChild('videoImportTorrent') videoImportTorrent: VideoImportTorrentComponent
19 @ViewChild('videoGoLive') videoGoLive: VideoGoLiveComponent
17 20
18 user: AuthUser = null 21 user: AuthUser = null
19 22
20 secondStepType: 'upload' | 'import-url' | 'import-torrent' 23 secondStepType: VideoEditType
21 videoName: string 24 videoName: string
22 serverConfig: ServerConfig 25 serverConfig: ServerConfig
23 26
@@ -41,7 +44,7 @@ export class VideoAddComponent implements OnInit, CanComponentDeactivate {
41 this.user = this.auth.getUser() 44 this.user = this.auth.getUser()
42 } 45 }
43 46
44 onFirstStepDone (type: 'upload' | 'import-url' | 'import-torrent', videoName: string) { 47 onFirstStepDone (type: VideoEditType, videoName: string) {
45 this.secondStepType = type 48 this.secondStepType = type
46 this.videoName = videoName 49 this.videoName = videoName
47 } 50 }
@@ -62,9 +65,9 @@ export class VideoAddComponent implements OnInit, CanComponentDeactivate {
62 } 65 }
63 66
64 canDeactivate (): { canDeactivate: boolean, text?: string} { 67 canDeactivate (): { canDeactivate: boolean, text?: string} {
65 if (this.secondStepType === 'upload') return this.videoUpload.canDeactivate()
66 if (this.secondStepType === 'import-url') return this.videoImportUrl.canDeactivate() 68 if (this.secondStepType === 'import-url') return this.videoImportUrl.canDeactivate()
67 if (this.secondStepType === 'import-torrent') return this.videoImportTorrent.canDeactivate() 69 if (this.secondStepType === 'import-torrent') return this.videoImportTorrent.canDeactivate()
70 if (this.secondStepType === 'go-live') return this.videoGoLive.canDeactivate()
68 71
69 return { canDeactivate: true } 72 return { canDeactivate: true }
70 } 73 }
@@ -77,6 +80,10 @@ export class VideoAddComponent implements OnInit, CanComponentDeactivate {
77 return this.serverConfig.import.videos.torrent.enabled 80 return this.serverConfig.import.videos.torrent.enabled
78 } 81 }
79 82
83 isVideoLiveEnabled () {
84 return this.serverConfig.live.enabled
85 }
86
80 isInSecondStep () { 87 isInSecondStep () {
81 return !!this.secondStepType 88 return !!this.secondStepType
82 } 89 }
diff --git a/client/src/app/+videos/+video-edit/video-add.module.ts b/client/src/app/+videos/+video-edit/video-add.module.ts
index 477c1cf5e..da651119b 100644
--- a/client/src/app/+videos/+video-edit/video-add.module.ts
+++ b/client/src/app/+videos/+video-edit/video-add.module.ts
@@ -4,6 +4,7 @@ import { VideoEditModule } from './shared/video-edit.module'
4import { DragDropDirective } from './video-add-components/drag-drop.directive' 4import { DragDropDirective } from './video-add-components/drag-drop.directive'
5import { VideoImportTorrentComponent } from './video-add-components/video-import-torrent.component' 5import { VideoImportTorrentComponent } from './video-add-components/video-import-torrent.component'
6import { VideoImportUrlComponent } from './video-add-components/video-import-url.component' 6import { VideoImportUrlComponent } from './video-add-components/video-import-url.component'
7import { VideoGoLiveComponent } from './video-add-components/video-go-live.component'
7import { VideoUploadComponent } from './video-add-components/video-upload.component' 8import { VideoUploadComponent } from './video-add-components/video-upload.component'
8import { VideoAddRoutingModule } from './video-add-routing.module' 9import { VideoAddRoutingModule } from './video-add-routing.module'
9import { VideoAddComponent } from './video-add.component' 10import { VideoAddComponent } from './video-add.component'
@@ -20,7 +21,8 @@ import { VideoAddComponent } from './video-add.component'
20 VideoUploadComponent, 21 VideoUploadComponent,
21 VideoImportUrlComponent, 22 VideoImportUrlComponent,
22 VideoImportTorrentComponent, 23 VideoImportTorrentComponent,
23 DragDropDirective 24 DragDropDirective,
25 VideoGoLiveComponent
24 ], 26 ],
25 27
26 exports: [ ], 28 exports: [ ],
diff --git a/client/src/app/+videos/+video-edit/video-update.component.html b/client/src/app/+videos/+video-edit/video-update.component.html
index b37596399..5f50ddc74 100644
--- a/client/src/app/+videos/+video-edit/video-update.component.html
+++ b/client/src/app/+videos/+video-edit/video-update.component.html
@@ -11,6 +11,7 @@
11 [validationMessages]="validationMessages" [userVideoChannels]="userVideoChannels" 11 [validationMessages]="validationMessages" [userVideoChannels]="userVideoChannels"
12 [videoCaptions]="videoCaptions" [waitTranscodingEnabled]="waitTranscodingEnabled" 12 [videoCaptions]="videoCaptions" [waitTranscodingEnabled]="waitTranscodingEnabled"
13 type="update" (pluginFieldsAdded)="hydratePluginFieldsFromVideo()" 13 type="update" (pluginFieldsAdded)="hydratePluginFieldsFromVideo()"
14 [videoLive]="videoLive"
14 ></my-video-edit> 15 ></my-video-edit>
15 16
16 <div class="submit-container"> 17 <div class="submit-container">
diff --git a/client/src/app/+videos/+video-edit/video-update.component.ts b/client/src/app/+videos/+video-edit/video-update.component.ts
index 20438a2d3..c0f46acd2 100644
--- a/client/src/app/+videos/+video-edit/video-update.component.ts
+++ b/client/src/app/+videos/+video-edit/video-update.component.ts
@@ -5,7 +5,8 @@ import { Notifier } from '@app/core'
5import { FormReactive, FormValidatorService, SelectChannelItem } from '@app/shared/shared-forms' 5import { FormReactive, FormValidatorService, SelectChannelItem } from '@app/shared/shared-forms'
6import { VideoCaptionEdit, VideoCaptionService, VideoDetails, VideoEdit, VideoService } from '@app/shared/shared-main' 6import { VideoCaptionEdit, VideoCaptionService, VideoDetails, VideoEdit, VideoService } from '@app/shared/shared-main'
7import { LoadingBarService } from '@ngx-loading-bar/core' 7import { LoadingBarService } from '@ngx-loading-bar/core'
8import { VideoPrivacy } from '@shared/models' 8import { VideoPrivacy, VideoLive } from '@shared/models'
9import { hydrateFormFromVideo } from './shared/video-edit-utils'
9 10
10@Component({ 11@Component({
11 selector: 'my-videos-update', 12 selector: 'my-videos-update',
@@ -14,11 +15,12 @@ import { VideoPrivacy } from '@shared/models'
14}) 15})
15export class VideoUpdateComponent extends FormReactive implements OnInit { 16export class VideoUpdateComponent extends FormReactive implements OnInit {
16 video: VideoEdit 17 video: VideoEdit
18 userVideoChannels: SelectChannelItem[] = []
19 videoCaptions: VideoCaptionEdit[] = []
20 videoLive: VideoLive
17 21
18 isUpdatingVideo = false 22 isUpdatingVideo = false
19 userVideoChannels: SelectChannelItem[] = []
20 schedulePublicationPossible = false 23 schedulePublicationPossible = false
21 videoCaptions: VideoCaptionEdit[] = []
22 waitTranscodingEnabled = true 24 waitTranscodingEnabled = true
23 25
24 private updateDone = false 26 private updateDone = false
@@ -40,10 +42,11 @@ export class VideoUpdateComponent extends FormReactive implements OnInit {
40 42
41 this.route.data 43 this.route.data
42 .pipe(map(data => data.videoData)) 44 .pipe(map(data => data.videoData))
43 .subscribe(({ video, videoChannels, videoCaptions }) => { 45 .subscribe(({ video, videoChannels, videoCaptions, videoLive }) => {
44 this.video = new VideoEdit(video) 46 this.video = new VideoEdit(video)
45 this.userVideoChannels = videoChannels 47 this.userVideoChannels = videoChannels
46 this.videoCaptions = videoCaptions 48 this.videoCaptions = videoCaptions
49 this.videoLive = videoLive
47 50
48 this.schedulePublicationPossible = this.video.privacy === VideoPrivacy.PRIVATE 51 this.schedulePublicationPossible = this.video.privacy === VideoPrivacy.PRIVATE
49 52
@@ -53,7 +56,7 @@ export class VideoUpdateComponent extends FormReactive implements OnInit {
53 } 56 }
54 57
55 // FIXME: Angular does not detect the change inside this subscription, so use the patched setTimeout 58 // FIXME: Angular does not detect the change inside this subscription, so use the patched setTimeout
56 setTimeout(() => this.hydrateFormFromVideo()) 59 setTimeout(() => hydrateFormFromVideo(this.form, this.video, true))
57 }, 60 },
58 61
59 err => { 62 err => {
@@ -133,29 +136,4 @@ export class VideoUpdateComponent extends FormReactive implements OnInit {
133 pluginData: this.video.pluginData 136 pluginData: this.video.pluginData
134 }) 137 })
135 } 138 }
136
137 private hydrateFormFromVideo () {
138 this.form.patchValue(this.video.toFormPatch())
139
140 const objects = [
141 {
142 url: 'thumbnailUrl',
143 name: 'thumbnailfile'
144 },
145 {
146 url: 'previewUrl',
147 name: 'previewfile'
148 }
149 ]
150
151 for (const obj of objects) {
152 fetch(this.video[obj.url])
153 .then(response => response.blob())
154 .then(data => {
155 this.form.patchValue({
156 [ obj.name ]: data
157 })
158 })
159 }
160 }
161} 139}
diff --git a/client/src/app/+videos/+video-edit/video-update.resolver.ts b/client/src/app/+videos/+video-edit/video-update.resolver.ts
index a391913d8..3a82324c3 100644
--- a/client/src/app/+videos/+video-edit/video-update.resolver.ts
+++ b/client/src/app/+videos/+video-edit/video-update.resolver.ts
@@ -1,13 +1,14 @@
1import { forkJoin } from 'rxjs' 1import { forkJoin, of } from 'rxjs'
2import { map, switchMap } from 'rxjs/operators' 2import { map, switchMap } from 'rxjs/operators'
3import { Injectable } from '@angular/core' 3import { Injectable } from '@angular/core'
4import { ActivatedRouteSnapshot, Resolve } from '@angular/router' 4import { ActivatedRouteSnapshot, Resolve } from '@angular/router'
5import { VideoCaptionService, VideoChannelService, VideoService } from '@app/shared/shared-main' 5import { VideoCaptionService, VideoChannelService, VideoDetails, VideoLiveService, VideoService } from '@app/shared/shared-main'
6 6
7@Injectable() 7@Injectable()
8export class VideoUpdateResolver implements Resolve<any> { 8export class VideoUpdateResolver implements Resolve<any> {
9 constructor ( 9 constructor (
10 private videoService: VideoService, 10 private videoService: VideoService,
11 private videoLiveService: VideoLiveService,
11 private videoChannelService: VideoChannelService, 12 private videoChannelService: VideoChannelService,
12 private videoCaptionService: VideoCaptionService 13 private videoCaptionService: VideoCaptionService
13 ) { 14 ) {
@@ -18,32 +19,38 @@ export class VideoUpdateResolver implements Resolve<any> {
18 19
19 return this.videoService.getVideo({ videoId: uuid }) 20 return this.videoService.getVideo({ videoId: uuid })
20 .pipe( 21 .pipe(
21 switchMap(video => { 22 switchMap(video => forkJoin(this.buildVideoObservables(video))),
22 return forkJoin([ 23 map(([ video, videoChannels, videoCaptions, videoLive ]) => ({ video, videoChannels, videoCaptions, videoLive }))
23 this.videoService 24 )
24 .loadCompleteDescription(video.descriptionPath) 25 }
25 .pipe(map(description => Object.assign(video, { description }))),
26 26
27 this.videoChannelService 27 private buildVideoObservables (video: VideoDetails) {
28 .listAccountVideoChannels(video.account) 28 return [
29 .pipe( 29 this.videoService
30 map(result => result.data), 30 .loadCompleteDescription(video.descriptionPath)
31 map(videoChannels => videoChannels.map(c => ({ 31 .pipe(map(description => Object.assign(video, { description }))),
32 id: c.id,
33 label: c.displayName,
34 support: c.support,
35 avatarPath: c.avatar?.path
36 })))
37 ),
38 32
39 this.videoCaptionService 33 this.videoChannelService
40 .listCaptions(video.id) 34 .listAccountVideoChannels(video.account)
41 .pipe( 35 .pipe(
42 map(result => result.data) 36 map(result => result.data),
43 ) 37 map(videoChannels => videoChannels.map(c => ({
44 ]) 38 id: c.id,
45 }), 39 label: c.displayName,
46 map(([ video, videoChannels, videoCaptions ]) => ({ video, videoChannels, videoCaptions })) 40 support: c.support,
47 ) 41 avatarPath: c.avatar?.path
42 })))
43 ),
44
45 this.videoCaptionService
46 .listCaptions(video.id)
47 .pipe(
48 map(result => result.data)
49 ),
50
51 video.isLive
52 ? this.videoLiveService.getVideoLive(video.id)
53 : of(undefined)
54 ]
48 } 55 }
49} 56}