diff options
Diffstat (limited to 'client/src/app/videos/+video-edit/video-add-components/video-upload.component.ts')
-rw-r--r-- | client/src/app/videos/+video-edit/video-add-components/video-upload.component.ts | 306 |
1 files changed, 0 insertions, 306 deletions
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 deleted file mode 100644 index eb7ac32ae..000000000 --- a/client/src/app/videos/+video-edit/video-add-components/video-upload.component.ts +++ /dev/null | |||
@@ -1,306 +0,0 @@ | |||
1 | import { BytesPipe } from 'ngx-pipes' | ||
2 | import { Subscription } from 'rxjs' | ||
3 | import { HttpEventType, HttpResponse } from '@angular/common/http' | ||
4 | import { Component, ElementRef, EventEmitter, OnDestroy, OnInit, Output, ViewChild } from '@angular/core' | ||
5 | import { Router } from '@angular/router' | ||
6 | import { AuthService, CanComponentDeactivate, Notifier, ServerService, UserService } from '@app/core' | ||
7 | import { scrollToTop } from '@app/helpers' | ||
8 | import { FormValidatorService } from '@app/shared/shared-forms' | ||
9 | import { VideoCaptionService, VideoEdit, VideoService } from '@app/shared/shared-main' | ||
10 | import { VideoSend } from '@app/videos/+video-edit/video-add-components/video-send' | ||
11 | import { LoadingBarService } from '@ngx-loading-bar/core' | ||
12 | import { I18n } from '@ngx-translate/i18n-polyfill' | ||
13 | import { VideoPrivacy } from '@shared/models' | ||
14 | |||
15 | @Component({ | ||
16 | selector: 'my-video-upload', | ||
17 | templateUrl: './video-upload.component.html', | ||
18 | styleUrls: [ | ||
19 | '../shared/video-edit.component.scss', | ||
20 | './video-upload.component.scss', | ||
21 | './video-send.scss' | ||
22 | ] | ||
23 | }) | ||
24 | export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy, CanComponentDeactivate { | ||
25 | @Output() firstStepDone = new EventEmitter<string>() | ||
26 | @Output() firstStepError = new EventEmitter<void>() | ||
27 | @ViewChild('videofileInput') videofileInput: ElementRef<HTMLInputElement> | ||
28 | |||
29 | // So that it can be accessed in the template | ||
30 | readonly SPECIAL_SCHEDULED_PRIVACY = VideoEdit.SPECIAL_SCHEDULED_PRIVACY | ||
31 | |||
32 | userVideoQuotaUsed = 0 | ||
33 | userVideoQuotaUsedDaily = 0 | ||
34 | |||
35 | isUploadingAudioFile = false | ||
36 | isUploadingVideo = false | ||
37 | isUpdatingVideo = false | ||
38 | |||
39 | videoUploaded = false | ||
40 | videoUploadObservable: Subscription = null | ||
41 | videoUploadPercents = 0 | ||
42 | videoUploadedIds = { | ||
43 | id: 0, | ||
44 | uuid: '' | ||
45 | } | ||
46 | |||
47 | waitTranscodingEnabled = true | ||
48 | previewfileUpload: File | ||
49 | |||
50 | error: string | ||
51 | |||
52 | protected readonly DEFAULT_VIDEO_PRIVACY = VideoPrivacy.PUBLIC | ||
53 | |||
54 | constructor ( | ||
55 | protected formValidatorService: FormValidatorService, | ||
56 | protected loadingBar: LoadingBarService, | ||
57 | protected notifier: Notifier, | ||
58 | protected authService: AuthService, | ||
59 | protected serverService: ServerService, | ||
60 | protected videoService: VideoService, | ||
61 | protected videoCaptionService: VideoCaptionService, | ||
62 | private userService: UserService, | ||
63 | private router: Router, | ||
64 | private i18n: I18n | ||
65 | ) { | ||
66 | super() | ||
67 | } | ||
68 | |||
69 | get videoExtensions () { | ||
70 | return this.serverConfig.video.file.extensions.join(', ') | ||
71 | } | ||
72 | |||
73 | ngOnInit () { | ||
74 | super.ngOnInit() | ||
75 | |||
76 | this.userService.getMyVideoQuotaUsed() | ||
77 | .subscribe(data => { | ||
78 | this.userVideoQuotaUsed = data.videoQuotaUsed | ||
79 | this.userVideoQuotaUsedDaily = data.videoQuotaUsedDaily | ||
80 | }) | ||
81 | } | ||
82 | |||
83 | ngOnDestroy () { | ||
84 | if (this.videoUploadObservable) this.videoUploadObservable.unsubscribe() | ||
85 | } | ||
86 | |||
87 | canDeactivate () { | ||
88 | let text = '' | ||
89 | |||
90 | if (this.videoUploaded === true) { | ||
91 | // FIXME: cannot concatenate strings inside i18n service :/ | ||
92 | text = this.i18n('Your video was uploaded to your account and is private.') + ' ' + | ||
93 | this.i18n('But associated data (tags, description...) will be lost, are you sure you want to leave this page?') | ||
94 | } else { | ||
95 | text = this.i18n('Your video is not uploaded yet, are you sure you want to leave this page?') | ||
96 | } | ||
97 | |||
98 | return { | ||
99 | canDeactivate: !this.isUploadingVideo, | ||
100 | text | ||
101 | } | ||
102 | } | ||
103 | |||
104 | getVideoFile () { | ||
105 | return this.videofileInput.nativeElement.files[0] | ||
106 | } | ||
107 | |||
108 | setVideoFile (files: FileList) { | ||
109 | this.videofileInput.nativeElement.files = files | ||
110 | this.fileChange() | ||
111 | } | ||
112 | |||
113 | getAudioUploadLabel () { | ||
114 | const videofile = this.getVideoFile() | ||
115 | if (!videofile) return this.i18n('Upload') | ||
116 | |||
117 | return this.i18n('Upload {{videofileName}}', { videofileName: videofile.name }) | ||
118 | } | ||
119 | |||
120 | fileChange () { | ||
121 | this.uploadFirstStep() | ||
122 | } | ||
123 | |||
124 | cancelUpload () { | ||
125 | if (this.videoUploadObservable !== null) { | ||
126 | this.videoUploadObservable.unsubscribe() | ||
127 | |||
128 | this.isUploadingVideo = false | ||
129 | this.videoUploadPercents = 0 | ||
130 | this.videoUploadObservable = null | ||
131 | |||
132 | this.firstStepError.emit() | ||
133 | |||
134 | this.notifier.info(this.i18n('Upload cancelled')) | ||
135 | } | ||
136 | } | ||
137 | |||
138 | uploadFirstStep (clickedOnButton = false) { | ||
139 | const videofile = this.getVideoFile() | ||
140 | if (!videofile) return | ||
141 | |||
142 | if (!this.checkGlobalUserQuota(videofile)) return | ||
143 | if (!this.checkDailyUserQuota(videofile)) return | ||
144 | |||
145 | if (clickedOnButton === false && this.isAudioFile(videofile.name)) { | ||
146 | this.isUploadingAudioFile = true | ||
147 | return | ||
148 | } | ||
149 | |||
150 | // Build name field | ||
151 | const nameWithoutExtension = videofile.name.replace(/\.[^/.]+$/, '') | ||
152 | let name: string | ||
153 | |||
154 | // If the name of the file is very small, keep the extension | ||
155 | if (nameWithoutExtension.length < 3) name = videofile.name | ||
156 | else name = nameWithoutExtension | ||
157 | |||
158 | // Force user to wait transcoding for unsupported video types in web browsers | ||
159 | if (!videofile.name.endsWith('.mp4') && !videofile.name.endsWith('.webm') && !videofile.name.endsWith('.ogv')) { | ||
160 | this.waitTranscodingEnabled = false | ||
161 | } | ||
162 | |||
163 | const privacy = this.firstStepPrivacyId.toString() | ||
164 | const nsfw = this.serverConfig.instance.isNSFW | ||
165 | const waitTranscoding = true | ||
166 | const commentsEnabled = true | ||
167 | const downloadEnabled = true | ||
168 | const channelId = this.firstStepChannelId.toString() | ||
169 | |||
170 | const formData = new FormData() | ||
171 | formData.append('name', name) | ||
172 | // Put the video "private" -> we are waiting the user validation of the second step | ||
173 | formData.append('privacy', VideoPrivacy.PRIVATE.toString()) | ||
174 | formData.append('nsfw', '' + nsfw) | ||
175 | formData.append('commentsEnabled', '' + commentsEnabled) | ||
176 | formData.append('downloadEnabled', '' + downloadEnabled) | ||
177 | formData.append('waitTranscoding', '' + waitTranscoding) | ||
178 | formData.append('channelId', '' + channelId) | ||
179 | formData.append('videofile', videofile) | ||
180 | |||
181 | if (this.previewfileUpload) { | ||
182 | formData.append('previewfile', this.previewfileUpload) | ||
183 | formData.append('thumbnailfile', this.previewfileUpload) | ||
184 | } | ||
185 | |||
186 | this.isUploadingVideo = true | ||
187 | this.firstStepDone.emit(name) | ||
188 | |||
189 | this.form.patchValue({ | ||
190 | name, | ||
191 | privacy, | ||
192 | nsfw, | ||
193 | channelId, | ||
194 | previewfile: this.previewfileUpload | ||
195 | }) | ||
196 | |||
197 | this.videoUploadObservable = this.videoService.uploadVideo(formData).subscribe( | ||
198 | event => { | ||
199 | if (event.type === HttpEventType.UploadProgress) { | ||
200 | this.videoUploadPercents = Math.round(100 * event.loaded / event.total) | ||
201 | } else if (event instanceof HttpResponse) { | ||
202 | this.videoUploaded = true | ||
203 | |||
204 | this.videoUploadedIds = event.body.video | ||
205 | |||
206 | this.videoUploadObservable = null | ||
207 | } | ||
208 | }, | ||
209 | |||
210 | err => { | ||
211 | // Reset progress | ||
212 | this.isUploadingVideo = false | ||
213 | this.videoUploadPercents = 0 | ||
214 | this.videoUploadObservable = null | ||
215 | this.firstStepError.emit() | ||
216 | this.notifier.error(err.message) | ||
217 | } | ||
218 | ) | ||
219 | } | ||
220 | |||
221 | isPublishingButtonDisabled () { | ||
222 | return !this.form.valid || | ||
223 | this.isUpdatingVideo === true || | ||
224 | this.videoUploaded !== true | ||
225 | } | ||
226 | |||
227 | updateSecondStep () { | ||
228 | if (this.checkForm() === false) { | ||
229 | return | ||
230 | } | ||
231 | |||
232 | const video = new VideoEdit() | ||
233 | video.patch(this.form.value) | ||
234 | video.id = this.videoUploadedIds.id | ||
235 | video.uuid = this.videoUploadedIds.uuid | ||
236 | |||
237 | this.isUpdatingVideo = true | ||
238 | |||
239 | this.updateVideoAndCaptions(video) | ||
240 | .subscribe( | ||
241 | () => { | ||
242 | this.isUpdatingVideo = false | ||
243 | this.isUploadingVideo = false | ||
244 | |||
245 | this.notifier.success(this.i18n('Video published.')) | ||
246 | this.router.navigate([ '/videos/watch', video.uuid ]) | ||
247 | }, | ||
248 | |||
249 | err => { | ||
250 | this.error = err.message | ||
251 | scrollToTop() | ||
252 | console.error(err) | ||
253 | } | ||
254 | ) | ||
255 | } | ||
256 | |||
257 | private checkGlobalUserQuota (videofile: File) { | ||
258 | const bytePipes = new BytesPipe() | ||
259 | |||
260 | // Check global user quota | ||
261 | const videoQuota = this.authService.getUser().videoQuota | ||
262 | if (videoQuota !== -1 && (this.userVideoQuotaUsed + videofile.size) > videoQuota) { | ||
263 | const msg = this.i18n( | ||
264 | 'Your video quota is exceeded with this video (video size: {{videoSize}}, used: {{videoQuotaUsed}}, quota: {{videoQuota}})', | ||
265 | { | ||
266 | videoSize: bytePipes.transform(videofile.size, 0), | ||
267 | videoQuotaUsed: bytePipes.transform(this.userVideoQuotaUsed, 0), | ||
268 | videoQuota: bytePipes.transform(videoQuota, 0) | ||
269 | } | ||
270 | ) | ||
271 | this.notifier.error(msg) | ||
272 | |||
273 | return false | ||
274 | } | ||
275 | |||
276 | return true | ||
277 | } | ||
278 | |||
279 | private checkDailyUserQuota (videofile: File) { | ||
280 | const bytePipes = new BytesPipe() | ||
281 | |||
282 | // Check daily user quota | ||
283 | const videoQuotaDaily = this.authService.getUser().videoQuotaDaily | ||
284 | if (videoQuotaDaily !== -1 && (this.userVideoQuotaUsedDaily + videofile.size) > videoQuotaDaily) { | ||
285 | const msg = this.i18n( | ||
286 | 'Your daily video quota is exceeded with this video (video size: {{videoSize}}, used: {{quotaUsedDaily}}, quota: {{quotaDaily}})', | ||
287 | { | ||
288 | videoSize: bytePipes.transform(videofile.size, 0), | ||
289 | quotaUsedDaily: bytePipes.transform(this.userVideoQuotaUsedDaily, 0), | ||
290 | quotaDaily: bytePipes.transform(videoQuotaDaily, 0) | ||
291 | } | ||
292 | ) | ||
293 | this.notifier.error(msg) | ||
294 | |||
295 | return false | ||
296 | } | ||
297 | |||
298 | return true | ||
299 | } | ||
300 | |||
301 | private isAudioFile (filename: string) { | ||
302 | const extensions = [ '.mp3', '.flac', '.ogg', '.wma', '.wav' ] | ||
303 | |||
304 | return extensions.some(e => filename.endsWith(e)) | ||
305 | } | ||
306 | } | ||