aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src
diff options
context:
space:
mode:
authorFelix Ableitner <me@nutomic.com>2018-08-28 02:01:35 -0500
committerChocobozzz <me@florianbigard.com>2018-08-28 09:01:35 +0200
commitbee0abffff73804d816b90c7fd599e0a51c09d61 (patch)
treefae6d58637f9c63a3800090277f8e130b43442dd /client/src
parentc907c2fa3fd7c0a741117a0204d0ebca675124bd (diff)
downloadPeerTube-bee0abffff73804d816b90c7fd599e0a51c09d61.tar.gz
PeerTube-bee0abffff73804d816b90c7fd599e0a51c09d61.tar.zst
PeerTube-bee0abffff73804d816b90c7fd599e0a51c09d61.zip
Implement daily upload limit (#956)
* Implement daily upload limit (ref #652) * remove duplicate code * review fixes * fix tests? * whitespace fixes, finish leftover todo * fix tests * added some new tests * use different config value for tests * remove todo
Diffstat (limited to 'client/src')
-rw-r--r--client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html14
-rw-r--r--client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts24
-rw-r--r--client/src/app/+admin/users/user-edit/user-edit.component.html9
-rw-r--r--client/src/app/+admin/users/user-edit/user-edit.ts17
-rw-r--r--client/src/app/+admin/users/user-edit/user-update.component.ts9
-rw-r--r--client/src/app/core/server/server.service.ts3
-rw-r--r--client/src/app/shared/forms/form-validators/user-validators.service.ts8
-rw-r--r--client/src/app/shared/users/user.model.ts3
-rw-r--r--client/src/app/videos/+video-edit/video-add-components/video-upload.component.ts22
9 files changed, 88 insertions, 21 deletions
diff --git a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html
index 49b89cef4..ca7890d84 100644
--- a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html
+++ b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html
@@ -142,6 +142,20 @@
142 {{ formErrors.userVideoQuota }} 142 {{ formErrors.userVideoQuota }}
143 </div> 143 </div>
144 </div> 144 </div>
145
146 <div class="form-group">
147 <label i18n for="userVideoQuotaDaily">User default daily upload limit</label>
148 <div class="peertube-select-container">
149 <select id="userVideoQuotaDaily" formControlName="userVideoQuotaDaily">
150 <option *ngFor="let videoQuotaDailyOption of videoQuotaDailyOptions" [value]="videoQuotaDailyOption.value">
151 {{ videoQuotaDailyOption.label }}
152 </option>
153 </select>
154 </div>
155 <div *ngIf="formErrors.userVideoQuotaDaily" class="form-error">
156 {{ formErrors.userVideoQuotaDaily }}
157 </div>
158 </div>
145 </ng-template> 159 </ng-template>
146 </ngb-tab> 160 </ngb-tab>
147 161
diff --git a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts
index fd6784415..3b6dabcb9 100644
--- a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts
+++ b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts
@@ -15,10 +15,7 @@ import { BuildFormDefaultValues, FormValidatorService } from '@app/shared/forms/
15 styleUrls: [ './edit-custom-config.component.scss' ] 15 styleUrls: [ './edit-custom-config.component.scss' ]
16}) 16})
17export class EditCustomConfigComponent extends FormReactive implements OnInit { 17export class EditCustomConfigComponent extends FormReactive implements OnInit {
18 customConfig: CustomConfig 18 static videoQuotaOptions = [
19 resolutions = [ '240p', '360p', '480p', '720p', '1080p' ]
20
21 videoQuotaOptions = [
22 { value: -1, label: 'Unlimited' }, 19 { value: -1, label: 'Unlimited' },
23 { value: 0, label: '0' }, 20 { value: 0, label: '0' },
24 { value: 100 * 1024 * 1024, label: '100MB' }, 21 { value: 100 * 1024 * 1024, label: '100MB' },
@@ -28,6 +25,20 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
28 { value: 20 * 1024 * 1024 * 1024, label: '20GB' }, 25 { value: 20 * 1024 * 1024 * 1024, label: '20GB' },
29 { value: 50 * 1024 * 1024 * 1024, label: '50GB' } 26 { value: 50 * 1024 * 1024 * 1024, label: '50GB' }
30 ] 27 ]
28 static videoQuotaDailyOptions = [
29 { value: -1, label: 'Unlimited' },
30 { value: 0, label: '0' },
31 { value: 10 * 1024 * 1024, label: '10MB' },
32 { value: 50 * 1024 * 1024, label: '50MB' },
33 { value: 100 * 1024 * 1024, label: '100MB' },
34 { value: 500 * 1024 * 1024, label: '500MB' },
35 { value: 2 * 1024 * 1024 * 1024, label: '2GB' },
36 { value: 5 * 1024 * 1024 * 1024, label: '5GB' }
37 ]
38
39 customConfig: CustomConfig
40 resolutions = [ '240p', '360p', '480p', '720p', '1080p' ]
41
31 transcodingThreadOptions = [ 42 transcodingThreadOptions = [
32 { value: 0, label: 'Auto (via ffmpeg)' }, 43 { value: 0, label: 'Auto (via ffmpeg)' },
33 { value: 1, label: '1' }, 44 { value: 1, label: '1' },
@@ -75,6 +86,7 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
75 importVideosTorrentEnabled: null, 86 importVideosTorrentEnabled: null,
76 adminEmail: this.customConfigValidatorsService.ADMIN_EMAIL, 87 adminEmail: this.customConfigValidatorsService.ADMIN_EMAIL,
77 userVideoQuota: this.userValidatorsService.USER_VIDEO_QUOTA, 88 userVideoQuota: this.userValidatorsService.USER_VIDEO_QUOTA,
89 userVideoQuotaDaily: this.userValidatorsService.USER_VIDEO_QUOTA_DAILY,
78 transcodingThreads: this.customConfigValidatorsService.TRANSCODING_THREADS, 90 transcodingThreads: this.customConfigValidatorsService.TRANSCODING_THREADS,
79 transcodingEnabled: null, 91 transcodingEnabled: null,
80 customizationJavascript: null, 92 customizationJavascript: null,
@@ -173,7 +185,8 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
173 email: this.form.value['adminEmail'] 185 email: this.form.value['adminEmail']
174 }, 186 },
175 user: { 187 user: {
176 videoQuota: this.form.value['userVideoQuota'] 188 videoQuota: this.form.value['userVideoQuota'],
189 videoQuotaDaily: this.form.value['userVideoQuotaDaily']
177 }, 190 },
178 transcoding: { 191 transcoding: {
179 enabled: this.form.value['transcodingEnabled'], 192 enabled: this.form.value['transcodingEnabled'],
@@ -231,6 +244,7 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
231 signupLimit: this.customConfig.signup.limit, 244 signupLimit: this.customConfig.signup.limit,
232 adminEmail: this.customConfig.admin.email, 245 adminEmail: this.customConfig.admin.email,
233 userVideoQuota: this.customConfig.user.videoQuota, 246 userVideoQuota: this.customConfig.user.videoQuota,
247 userVideoQuotaDaily: this.customConfig.user.videoQuotaDaily,
234 transcodingThreads: this.customConfig.transcoding.threads, 248 transcodingThreads: this.customConfig.transcoding.threads,
235 transcodingEnabled: this.customConfig.transcoding.enabled, 249 transcodingEnabled: this.customConfig.transcoding.enabled,
236 customizationJavascript: this.customConfig.instance.customizations.javascript, 250 customizationJavascript: this.customConfig.instance.customizations.javascript,
diff --git a/client/src/app/+admin/users/user-edit/user-edit.component.html b/client/src/app/+admin/users/user-edit/user-edit.component.html
index 4626a40c9..bb745d6aa 100644
--- a/client/src/app/+admin/users/user-edit/user-edit.component.html
+++ b/client/src/app/+admin/users/user-edit/user-edit.component.html
@@ -61,6 +61,15 @@
61 </option> 61 </option>
62 </select> 62 </select>
63 </div> 63 </div>
64
65 <label i18n for="videoQuotaDaily">Daily video quota</label>
66 <div class="peertube-select-container">
67 <select id="videoQuotaDaily" formControlName="videoQuotaDaily">
68 <option *ngFor="let videoQuotaDailyOption of videoQuotaDailyOptions" [value]="videoQuotaDailyOption.value">
69 {{ videoQuotaDailyOption.label }}
70 </option>
71 </select>
72 </div>
64 73
65 <div i18n class="transcoding-information" *ngIf="isTranscodingInformationDisplayed()"> 74 <div i18n class="transcoding-information" *ngIf="isTranscodingInformationDisplayed()">
66 Transcoding is enabled on server. The video quota only take in account <strong>original</strong> video. <br /> 75 Transcoding is enabled on server. The video quota only take in account <strong>original</strong> video. <br />
diff --git a/client/src/app/+admin/users/user-edit/user-edit.ts b/client/src/app/+admin/users/user-edit/user-edit.ts
index ea8c733c3..4e7ca8a1b 100644
--- a/client/src/app/+admin/users/user-edit/user-edit.ts
+++ b/client/src/app/+admin/users/user-edit/user-edit.ts
@@ -1,18 +1,15 @@
1import { ServerService } from '../../../core' 1import { ServerService } from '../../../core'
2import { FormReactive } from '../../../shared' 2import { FormReactive } from '../../../shared'
3import { USER_ROLE_LABELS, VideoResolution } from '../../../../../../shared' 3import { USER_ROLE_LABELS, VideoResolution } from '../../../../../../shared'
4import { EditCustomConfigComponent } from '../../../+admin/config/edit-custom-config/'
4 5
5export abstract class UserEdit extends FormReactive { 6export abstract class UserEdit extends FormReactive {
6 videoQuotaOptions = [ 7
7 { value: -1, label: 'Unlimited' }, 8 // These are used by a HTML select, so convert key into strings
8 { value: 0, label: '0' }, 9 videoQuotaOptions = EditCustomConfigComponent.videoQuotaOptions
9 { value: 100 * 1024 * 1024, label: '100MB' }, 10 .map(q => ({ value: q.value.toString(), label: q.label }))
10 { value: 500 * 1024 * 1024, label: '500MB' }, 11 videoQuotaDailyOptions = EditCustomConfigComponent.videoQuotaDailyOptions
11 { value: 1024 * 1024 * 1024, label: '1GB' }, 12 .map(q => ({ value: q.value.toString(), label: q.label }))
12 { value: 5 * 1024 * 1024 * 1024, label: '5GB' },
13 { value: 20 * 1024 * 1024 * 1024, label: '20GB' },
14 { value: 50 * 1024 * 1024 * 1024, label: '50GB' }
15 ].map(q => ({ value: q.value.toString(), label: q.label })) // Used by a HTML select, so convert key into strings
16 13
17 roles = Object.keys(USER_ROLE_LABELS).map(key => ({ value: key.toString(), label: USER_ROLE_LABELS[key] })) 14 roles = Object.keys(USER_ROLE_LABELS).map(key => ({ value: key.toString(), label: USER_ROLE_LABELS[key] }))
18 15
diff --git a/client/src/app/+admin/users/user-edit/user-update.component.ts b/client/src/app/+admin/users/user-edit/user-update.component.ts
index 06bde582e..5821229b3 100644
--- a/client/src/app/+admin/users/user-edit/user-update.component.ts
+++ b/client/src/app/+admin/users/user-edit/user-update.component.ts
@@ -36,11 +36,12 @@ export class UserUpdateComponent extends UserEdit implements OnInit, OnDestroy {
36 } 36 }
37 37
38 ngOnInit () { 38 ngOnInit () {
39 const defaultValues = { videoQuota: '-1' } 39 const defaultValues = { videoQuota: '-1', videoQuotaDaily: '-1' }
40 this.buildForm({ 40 this.buildForm({
41 email: this.userValidatorsService.USER_EMAIL, 41 email: this.userValidatorsService.USER_EMAIL,
42 role: this.userValidatorsService.USER_ROLE, 42 role: this.userValidatorsService.USER_ROLE,
43 videoQuota: this.userValidatorsService.USER_VIDEO_QUOTA 43 videoQuota: this.userValidatorsService.USER_VIDEO_QUOTA,
44 videoQuotaDaily: this.userValidatorsService.USER_VIDEO_QUOTA_DAILY
44 }, defaultValues) 45 }, defaultValues)
45 46
46 this.paramsSub = this.route.params.subscribe(routeParams => { 47 this.paramsSub = this.route.params.subscribe(routeParams => {
@@ -64,6 +65,7 @@ export class UserUpdateComponent extends UserEdit implements OnInit, OnDestroy {
64 65
65 // A select in HTML is always mapped as a string, we convert it to number 66 // A select in HTML is always mapped as a string, we convert it to number
66 userUpdate.videoQuota = parseInt(this.form.value['videoQuota'], 10) 67 userUpdate.videoQuota = parseInt(this.form.value['videoQuota'], 10)
68 userUpdate.videoQuotaDaily = parseInt(this.form.value['videoQuotaDaily'], 10)
67 69
68 this.userService.updateUser(this.userId, userUpdate).subscribe( 70 this.userService.updateUser(this.userId, userUpdate).subscribe(
69 () => { 71 () => {
@@ -93,7 +95,8 @@ export class UserUpdateComponent extends UserEdit implements OnInit, OnDestroy {
93 this.form.patchValue({ 95 this.form.patchValue({
94 email: userJson.email, 96 email: userJson.email,
95 role: userJson.role, 97 role: userJson.role,
96 videoQuota: userJson.videoQuota 98 videoQuota: userJson.videoQuota,
99 videoQuotaDaily: userJson.videoQuotaDaily
97 }) 100 })
98 } 101 }
99} 102}
diff --git a/client/src/app/core/server/server.service.ts b/client/src/app/core/server/server.service.ts
index 7823fa80e..a1ce12069 100644
--- a/client/src/app/core/server/server.service.ts
+++ b/client/src/app/core/server/server.service.ts
@@ -67,7 +67,8 @@ export class ServerService {
67 } 67 }
68 }, 68 },
69 user: { 69 user: {
70 videoQuota: -1 70 videoQuota: -1,
71 videoQuotaDaily: -1
71 }, 72 },
72 import: { 73 import: {
73 videos: { 74 videos: {
diff --git a/client/src/app/shared/forms/form-validators/user-validators.service.ts b/client/src/app/shared/forms/form-validators/user-validators.service.ts
index ec9566ef3..424553d74 100644
--- a/client/src/app/shared/forms/form-validators/user-validators.service.ts
+++ b/client/src/app/shared/forms/form-validators/user-validators.service.ts
@@ -9,6 +9,7 @@ export class UserValidatorsService {
9 readonly USER_EMAIL: BuildFormValidator 9 readonly USER_EMAIL: BuildFormValidator
10 readonly USER_PASSWORD: BuildFormValidator 10 readonly USER_PASSWORD: BuildFormValidator
11 readonly USER_VIDEO_QUOTA: BuildFormValidator 11 readonly USER_VIDEO_QUOTA: BuildFormValidator
12 readonly USER_VIDEO_QUOTA_DAILY: BuildFormValidator
12 readonly USER_ROLE: BuildFormValidator 13 readonly USER_ROLE: BuildFormValidator
13 readonly USER_DISPLAY_NAME: BuildFormValidator 14 readonly USER_DISPLAY_NAME: BuildFormValidator
14 readonly USER_DESCRIPTION: BuildFormValidator 15 readonly USER_DESCRIPTION: BuildFormValidator
@@ -61,6 +62,13 @@ export class UserValidatorsService {
61 'min': this.i18n('Quota must be greater than -1.') 62 'min': this.i18n('Quota must be greater than -1.')
62 } 63 }
63 } 64 }
65 this.USER_VIDEO_QUOTA_DAILY = {
66 VALIDATORS: [ Validators.required, Validators.min(-1) ],
67 MESSAGES: {
68 'required': this.i18n('Daily upload limit is required.'),
69 'min': this.i18n('Daily upload limit must be greater than -1.')
70 }
71 }
64 72
65 this.USER_ROLE = { 73 this.USER_ROLE = {
66 VALIDATORS: [ Validators.required ], 74 VALIDATORS: [ Validators.required ],
diff --git a/client/src/app/shared/users/user.model.ts b/client/src/app/shared/users/user.model.ts
index 2748001d0..877f1bf3a 100644
--- a/client/src/app/shared/users/user.model.ts
+++ b/client/src/app/shared/users/user.model.ts
@@ -16,6 +16,7 @@ export type UserConstructorHash = {
16 email: string, 16 email: string,
17 role: UserRole, 17 role: UserRole,
18 videoQuota?: number, 18 videoQuota?: number,
19 videoQuotaDaily?: number,
19 nsfwPolicy?: NSFWPolicyType, 20 nsfwPolicy?: NSFWPolicyType,
20 autoPlayVideo?: boolean, 21 autoPlayVideo?: boolean,
21 createdAt?: Date, 22 createdAt?: Date,
@@ -33,6 +34,7 @@ export class User implements UserServerModel {
33 nsfwPolicy: NSFWPolicyType 34 nsfwPolicy: NSFWPolicyType
34 autoPlayVideo: boolean 35 autoPlayVideo: boolean
35 videoQuota: number 36 videoQuota: number
37 videoQuotaDaily: number
36 account: Account 38 account: Account
37 videoChannels: VideoChannel[] 39 videoChannels: VideoChannel[]
38 createdAt: Date 40 createdAt: Date
@@ -48,6 +50,7 @@ export class User implements UserServerModel {
48 50
49 this.videoChannels = hash.videoChannels 51 this.videoChannels = hash.videoChannels
50 this.videoQuota = hash.videoQuota 52 this.videoQuota = hash.videoQuota
53 this.videoQuotaDaily = hash.videoQuotaDaily
51 this.nsfwPolicy = hash.nsfwPolicy 54 this.nsfwPolicy = hash.nsfwPolicy
52 this.autoPlayVideo = hash.autoPlayVideo 55 this.autoPlayVideo = hash.autoPlayVideo
53 this.createdAt = hash.createdAt 56 this.createdAt = hash.createdAt
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 3ec89ff62..c9ab35b1d 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
@@ -31,6 +31,7 @@ export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy
31 readonly SPECIAL_SCHEDULED_PRIVACY = VideoEdit.SPECIAL_SCHEDULED_PRIVACY 31 readonly SPECIAL_SCHEDULED_PRIVACY = VideoEdit.SPECIAL_SCHEDULED_PRIVACY
32 32
33 userVideoQuotaUsed = 0 33 userVideoQuotaUsed = 0
34 userVideoQuotaUsedDaily = 0
34 35
35 isUploadingVideo = false 36 isUploadingVideo = false
36 isUpdatingVideo = false 37 isUpdatingVideo = false
@@ -68,6 +69,9 @@ export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy
68 69
69 this.userService.getMyVideoQuotaUsed() 70 this.userService.getMyVideoQuotaUsed()
70 .subscribe(data => this.userVideoQuotaUsed = data.videoQuotaUsed) 71 .subscribe(data => this.userVideoQuotaUsed = data.videoQuotaUsed)
72
73 this.userService.getMyVideoQuotaUsed()
74 .subscribe(data => this.userVideoQuotaUsedDaily = data.videoQuotaUsedDaily)
71 } 75 }
72 76
73 ngOnDestroy () { 77 ngOnDestroy () {
@@ -115,10 +119,9 @@ export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy
115 return 119 return
116 } 120 }
117 121
122 const bytePipes = new BytesPipe()
118 const videoQuota = this.authService.getUser().videoQuota 123 const videoQuota = this.authService.getUser().videoQuota
119 if (videoQuota !== -1 && (this.userVideoQuotaUsed + videofile.size) > videoQuota) { 124 if (videoQuota !== -1 && (this.userVideoQuotaUsed + videofile.size) > videoQuota) {
120 const bytePipes = new BytesPipe()
121
122 const msg = this.i18n( 125 const msg = this.i18n(
123 'Your video quota is exceeded with this video (video size: {{ videoSize }}, used: {{ videoQuotaUsed }}, quota: {{ videoQuota }})', 126 'Your video quota is exceeded with this video (video size: {{ videoSize }}, used: {{ videoQuotaUsed }}, quota: {{ videoQuota }})',
124 { 127 {
@@ -131,6 +134,21 @@ export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy
131 return 134 return
132 } 135 }
133 136
137 const videoQuotaDaily = this.authService.getUser().videoQuotaDaily
138 if (videoQuotaDaily !== -1 && (this.userVideoQuotaUsedDaily + videofile.size) > videoQuotaDaily) {
139 const msg = this.i18n(
140 'Your daily video quota is exceeded with this video (video size: {{ videoSize }}, ' +
141 'used: {{ videoQuotaUsedDaily }}, quota: {{ videoQuotaDaily }})',
142 {
143 videoSize: bytePipes.transform(videofile.size, 0),
144 videoQuotaUsedDaily: bytePipes.transform(this.userVideoQuotaUsedDaily, 0),
145 videoQuotaDaily: bytePipes.transform(videoQuotaDaily, 0)
146 }
147 )
148 this.notificationsService.error(this.i18n('Error'), msg)
149 return
150 }
151
134 const nameWithoutExtension = videofile.name.replace(/\.[^/.]+$/, '') 152 const nameWithoutExtension = videofile.name.replace(/\.[^/.]+$/, '')
135 let name: string 153 let name: string
136 154