diff options
Diffstat (limited to 'client/src/app/videos/+video-edit/video-add.component.ts')
-rw-r--r-- | client/src/app/videos/+video-edit/video-add.component.ts | 252 |
1 files changed, 15 insertions, 237 deletions
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 651ee8dd2..64071b40c 100644 --- a/client/src/app/videos/+video-edit/video-add.component.ts +++ b/client/src/app/videos/+video-edit/video-add.component.ts | |||
@@ -1,251 +1,29 @@ | |||
1 | import { HttpEventType, HttpResponse } from '@angular/common/http' | 1 | import { Component, ViewChild } from '@angular/core' |
2 | import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core' | ||
3 | import { Router } from '@angular/router' | ||
4 | import { UserService } from '@app/shared' | ||
5 | import { CanComponentDeactivate } from '@app/shared/guards/can-deactivate-guard.service' | 2 | import { CanComponentDeactivate } from '@app/shared/guards/can-deactivate-guard.service' |
6 | import { LoadingBarService } from '@ngx-loading-bar/core' | 3 | import { VideoImportComponent } from '@app/videos/+video-edit/video-import.component' |
7 | import { NotificationsService } from 'angular2-notifications' | 4 | import { VideoUploadComponent } from '@app/videos/+video-edit/video-upload.component' |
8 | import { BytesPipe } from 'ngx-pipes' | ||
9 | import { Subscription } from 'rxjs' | ||
10 | import { VideoConstant, VideoPrivacy } from '../../../../../shared/models/videos' | ||
11 | import { AuthService, ServerService } from '../../core' | ||
12 | import { FormReactive } from '../../shared' | ||
13 | import { populateAsyncUserVideoChannels } from '../../shared/misc/utils' | ||
14 | import { VideoEdit } from '../../shared/video/video-edit.model' | ||
15 | import { VideoService } from '../../shared/video/video.service' | ||
16 | import { I18n } from '@ngx-translate/i18n-polyfill' | ||
17 | import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service' | ||
18 | import { switchMap } from 'rxjs/operators' | ||
19 | import { VideoCaptionService } from '@app/shared/video-caption' | ||
20 | import { VideoCaptionEdit } from '@app/shared/video-caption/video-caption-edit.model' | ||
21 | 5 | ||
22 | @Component({ | 6 | @Component({ |
23 | selector: 'my-videos-add', | 7 | selector: 'my-videos-add', |
24 | templateUrl: './video-add.component.html', | 8 | templateUrl: './video-add.component.html', |
25 | styleUrls: [ | 9 | styleUrls: [ './video-add.component.scss' ] |
26 | './shared/video-edit.component.scss', | ||
27 | './video-add.component.scss' | ||
28 | ] | ||
29 | }) | 10 | }) |
30 | export class VideoAddComponent extends FormReactive implements OnInit, OnDestroy, CanComponentDeactivate { | 11 | export class VideoAddComponent implements CanComponentDeactivate { |
31 | @ViewChild('videofileInput') videofileInput | 12 | @ViewChild('videoUpload') videoUpload: VideoUploadComponent |
13 | @ViewChild('videoImport') videoImport: VideoImportComponent | ||
32 | 14 | ||
33 | // So that it can be accessed in the template | 15 | secondStepType: 'upload' | 'import' |
34 | readonly SPECIAL_SCHEDULED_PRIVACY = VideoEdit.SPECIAL_SCHEDULED_PRIVACY | 16 | videoName: string |
35 | 17 | ||
36 | isUploadingVideo = false | 18 | onFirstStepDone (type: 'upload' | 'import', videoName: string) { |
37 | isUpdatingVideo = false | 19 | this.secondStepType = type |
38 | videoUploaded = false | 20 | this.videoName = videoName |
39 | videoUploadObservable: Subscription = null | ||
40 | videoUploadPercents = 0 | ||
41 | videoUploadedIds = { | ||
42 | id: 0, | ||
43 | uuid: '' | ||
44 | } | ||
45 | videoFileName: string | ||
46 | |||
47 | userVideoChannels: { id: number, label: string, support: string }[] = [] | ||
48 | userVideoQuotaUsed = 0 | ||
49 | videoPrivacies: VideoConstant<string>[] = [] | ||
50 | firstStepPrivacyId = 0 | ||
51 | firstStepChannelId = 0 | ||
52 | videoCaptions: VideoCaptionEdit[] = [] | ||
53 | |||
54 | constructor ( | ||
55 | protected formValidatorService: FormValidatorService, | ||
56 | private router: Router, | ||
57 | private notificationsService: NotificationsService, | ||
58 | private authService: AuthService, | ||
59 | private userService: UserService, | ||
60 | private serverService: ServerService, | ||
61 | private videoService: VideoService, | ||
62 | private loadingBar: LoadingBarService, | ||
63 | private i18n: I18n, | ||
64 | private videoCaptionService: VideoCaptionService | ||
65 | ) { | ||
66 | super() | ||
67 | } | ||
68 | |||
69 | get videoExtensions () { | ||
70 | return this.serverService.getConfig().video.file.extensions.join(',') | ||
71 | } | ||
72 | |||
73 | ngOnInit () { | ||
74 | this.buildForm({}) | ||
75 | |||
76 | populateAsyncUserVideoChannels(this.authService, this.userVideoChannels) | ||
77 | .then(() => this.firstStepChannelId = this.userVideoChannels[0].id) | ||
78 | |||
79 | this.userService.getMyVideoQuotaUsed() | ||
80 | .subscribe(data => this.userVideoQuotaUsed = data.videoQuotaUsed) | ||
81 | |||
82 | this.serverService.videoPrivaciesLoaded | ||
83 | .subscribe( | ||
84 | () => { | ||
85 | this.videoPrivacies = this.serverService.getVideoPrivacies() | ||
86 | |||
87 | // Public by default | ||
88 | this.firstStepPrivacyId = VideoPrivacy.PUBLIC | ||
89 | }) | ||
90 | } | ||
91 | |||
92 | ngOnDestroy () { | ||
93 | if (this.videoUploadObservable) { | ||
94 | this.videoUploadObservable.unsubscribe() | ||
95 | } | ||
96 | } | 21 | } |
97 | 22 | ||
98 | canDeactivate () { | 23 | canDeactivate () { |
99 | let text = '' | 24 | if (this.secondStepType === 'upload') return this.videoUpload.canDeactivate() |
100 | 25 | if (this.secondStepType === 'import') return this.videoImport.canDeactivate() | |
101 | if (this.videoUploaded === true) { | ||
102 | // FIXME: cannot concatenate strings inside i18n service :/ | ||
103 | text = this.i18n('Your video was uploaded in your account and is private.') + | ||
104 | this.i18n('But associated data (tags, description...) will be lost, are you sure you want to leave this page?') | ||
105 | } else { | ||
106 | text = this.i18n('Your video is not uploaded yet, are you sure you want to leave this page?') | ||
107 | } | ||
108 | |||
109 | return { | ||
110 | canDeactivate: !this.isUploadingVideo, | ||
111 | text | ||
112 | } | ||
113 | } | ||
114 | |||
115 | fileChange () { | ||
116 | this.uploadFirstStep() | ||
117 | } | ||
118 | |||
119 | checkForm () { | ||
120 | this.forceCheck() | ||
121 | |||
122 | return this.form.valid | ||
123 | } | ||
124 | |||
125 | cancelUpload () { | ||
126 | if (this.videoUploadObservable !== null) { | ||
127 | this.videoUploadObservable.unsubscribe() | ||
128 | this.isUploadingVideo = false | ||
129 | this.videoUploadPercents = 0 | ||
130 | this.videoUploadObservable = null | ||
131 | this.notificationsService.info(this.i18n('Info'), this.i18n('Upload cancelled')) | ||
132 | } | ||
133 | } | ||
134 | |||
135 | uploadFirstStep () { | ||
136 | const videofile = this.videofileInput.nativeElement.files[0] as File | ||
137 | if (!videofile) return | ||
138 | |||
139 | // Cannot upload videos > 8GB for now | ||
140 | if (videofile.size > 8 * 1024 * 1024 * 1024) { | ||
141 | this.notificationsService.error(this.i18n('Error'), this.i18n('We are sorry but PeerTube cannot handle videos > 8GB')) | ||
142 | return | ||
143 | } | ||
144 | |||
145 | const videoQuota = this.authService.getUser().videoQuota | ||
146 | if (videoQuota !== -1 && (this.userVideoQuotaUsed + videofile.size) > videoQuota) { | ||
147 | const bytePipes = new BytesPipe() | ||
148 | |||
149 | const msg = this.i18n( | ||
150 | 'Your video quota is exceeded with this video (video size: {{ videoSize }}, used: {{ videoQuotaUsed }}, quota: {{ videoQuota }})', | ||
151 | { | ||
152 | videoSize: bytePipes.transform(videofile.size, 0), | ||
153 | videoQuotaUsed: bytePipes.transform(this.userVideoQuotaUsed, 0), | ||
154 | videoQuota: bytePipes.transform(videoQuota, 0) | ||
155 | } | ||
156 | ) | ||
157 | this.notificationsService.error(this.i18n('Error'), msg) | ||
158 | return | ||
159 | } | ||
160 | |||
161 | this.videoFileName = videofile.name | ||
162 | |||
163 | const nameWithoutExtension = videofile.name.replace(/\.[^/.]+$/, '') | ||
164 | let name: string | ||
165 | |||
166 | // If the name of the file is very small, keep the extension | ||
167 | if (nameWithoutExtension.length < 3) name = videofile.name | ||
168 | else name = nameWithoutExtension | ||
169 | |||
170 | const privacy = this.firstStepPrivacyId.toString() | ||
171 | const nsfw = false | ||
172 | const waitTranscoding = true | ||
173 | const commentsEnabled = true | ||
174 | const channelId = this.firstStepChannelId.toString() | ||
175 | |||
176 | const formData = new FormData() | ||
177 | formData.append('name', name) | ||
178 | // Put the video "private" -> we are waiting the user validation of the second step | ||
179 | formData.append('privacy', VideoPrivacy.PRIVATE.toString()) | ||
180 | formData.append('nsfw', '' + nsfw) | ||
181 | formData.append('commentsEnabled', '' + commentsEnabled) | ||
182 | formData.append('waitTranscoding', '' + waitTranscoding) | ||
183 | formData.append('channelId', '' + channelId) | ||
184 | formData.append('videofile', videofile) | ||
185 | |||
186 | this.isUploadingVideo = true | ||
187 | this.form.patchValue({ | ||
188 | name, | ||
189 | privacy, | ||
190 | nsfw, | ||
191 | channelId | ||
192 | }) | ||
193 | |||
194 | this.videoUploadObservable = this.videoService.uploadVideo(formData).subscribe( | ||
195 | event => { | ||
196 | if (event.type === HttpEventType.UploadProgress) { | ||
197 | this.videoUploadPercents = Math.round(100 * event.loaded / event.total) | ||
198 | } else if (event instanceof HttpResponse) { | ||
199 | this.videoUploaded = true | ||
200 | |||
201 | this.videoUploadedIds = event.body.video | ||
202 | |||
203 | this.videoUploadObservable = null | ||
204 | } | ||
205 | }, | ||
206 | |||
207 | err => { | ||
208 | // Reset progress | ||
209 | this.isUploadingVideo = false | ||
210 | this.videoUploadPercents = 0 | ||
211 | this.videoUploadObservable = null | ||
212 | this.notificationsService.error(this.i18n('Error'), err.message) | ||
213 | } | ||
214 | ) | ||
215 | } | ||
216 | |||
217 | updateSecondStep () { | ||
218 | if (this.checkForm() === false) { | ||
219 | return | ||
220 | } | ||
221 | |||
222 | const video = new VideoEdit() | ||
223 | video.patch(this.form.value) | ||
224 | video.id = this.videoUploadedIds.id | ||
225 | video.uuid = this.videoUploadedIds.uuid | ||
226 | |||
227 | this.isUpdatingVideo = true | ||
228 | this.loadingBar.start() | ||
229 | this.videoService.updateVideo(video) | ||
230 | .pipe( | ||
231 | // Then update captions | ||
232 | switchMap(() => this.videoCaptionService.updateCaptions(video.id, this.videoCaptions)) | ||
233 | ) | ||
234 | .subscribe( | ||
235 | () => { | ||
236 | this.isUpdatingVideo = false | ||
237 | this.isUploadingVideo = false | ||
238 | this.loadingBar.complete() | ||
239 | |||
240 | this.notificationsService.success(this.i18n('Success'), this.i18n('Video published.')) | ||
241 | this.router.navigate([ '/videos/watch', video.uuid ]) | ||
242 | }, | ||
243 | 26 | ||
244 | err => { | 27 | return { canDeactivate: true } |
245 | this.isUpdatingVideo = false | ||
246 | this.notificationsService.error(this.i18n('Error'), err.message) | ||
247 | console.error(err) | ||
248 | } | ||
249 | ) | ||
250 | } | 28 | } |
251 | } | 29 | } |