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