aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--client/src/app/shared/misc/utils.ts5
-rw-r--r--client/src/app/videos/+video-edit/shared/video-edit.component.ts2
-rw-r--r--client/src/assets/player/peertube-chunk-store.ts59
-rw-r--r--server/controllers/api/videos/index.ts4
-rw-r--r--server/helpers/custom-validators/misc.ts4
-rw-r--r--server/helpers/custom-validators/videos.ts8
-rw-r--r--server/middlewares/validators/videos.ts16
-rw-r--r--server/models/video/tag.ts2
8 files changed, 61 insertions, 39 deletions
diff --git a/client/src/app/shared/misc/utils.ts b/client/src/app/shared/misc/utils.ts
index b5bf99be2..b9aa223cf 100644
--- a/client/src/app/shared/misc/utils.ts
+++ b/client/src/app/shared/misc/utils.ts
@@ -66,6 +66,11 @@ function objectToFormData (obj: any, form?: FormData, namespace?: string) {
66 66
67 if (obj[key] === undefined) continue 67 if (obj[key] === undefined) continue
68 68
69 if (Array.isArray(obj[key]) && obj[key].length === 0) {
70 fd.append(key, null)
71 continue
72 }
73
69 if (obj[key] !== null && typeof obj[ key ] === 'object' && !(obj[ key ] instanceof File)) { 74 if (obj[key] !== null && typeof obj[ key ] === 'object' && !(obj[ key ] instanceof File)) {
70 objectToFormData(obj[ key ], fd, key) 75 objectToFormData(obj[ key ], fd, key)
71 } else { 76 } else {
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 af4438bd2..d4567e26c 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
@@ -80,7 +80,7 @@ export class VideoEditComponent implements OnInit {
80 this.form.addControl('licence', new FormControl('', VIDEO_LICENCE.VALIDATORS)) 80 this.form.addControl('licence', new FormControl('', VIDEO_LICENCE.VALIDATORS))
81 this.form.addControl('language', new FormControl('', VIDEO_LANGUAGE.VALIDATORS)) 81 this.form.addControl('language', new FormControl('', VIDEO_LANGUAGE.VALIDATORS))
82 this.form.addControl('description', new FormControl('', VIDEO_DESCRIPTION.VALIDATORS)) 82 this.form.addControl('description', new FormControl('', VIDEO_DESCRIPTION.VALIDATORS))
83 this.form.addControl('tags', new FormControl('')) 83 this.form.addControl('tags', new FormControl([]))
84 this.form.addControl('thumbnailfile', new FormControl('')) 84 this.form.addControl('thumbnailfile', new FormControl(''))
85 this.form.addControl('previewfile', new FormControl('')) 85 this.form.addControl('previewfile', new FormControl(''))
86 this.form.addControl('support', new FormControl('', VIDEO_SUPPORT.VALIDATORS)) 86 this.form.addControl('support', new FormControl('', VIDEO_SUPPORT.VALIDATORS))
diff --git a/client/src/assets/player/peertube-chunk-store.ts b/client/src/assets/player/peertube-chunk-store.ts
index 84fbaf146..e14e31c04 100644
--- a/client/src/assets/player/peertube-chunk-store.ts
+++ b/client/src/assets/player/peertube-chunk-store.ts
@@ -155,17 +155,17 @@ export class PeertubeChunkStore extends EventEmitter {
155 this.cleanerInterval = null 155 this.cleanerInterval = null
156 } 156 }
157 157
158 if (this.db) {
159 await this.db.close()
160
161 await this.dropDatabase(this.databaseName)
162 }
163
158 if (this.expirationDB) { 164 if (this.expirationDB) {
159 await this.expirationDB.close() 165 await this.expirationDB.close()
160 this.expirationDB = null 166 this.expirationDB = null
161 } 167 }
162 168
163 if (this.db) {
164 console.log('Destroying IndexDB database %s.', this.databaseName)
165 await this.db.close()
166 await this.db.delete()
167 }
168
169 return cb() 169 return cb()
170 } catch (err) { 170 } catch (err) {
171 console.error('Cannot destroy peertube chunk store.', err) 171 console.error('Cannot destroy peertube chunk store.', err)
@@ -181,31 +181,42 @@ export class PeertubeChunkStore extends EventEmitter {
181 }, PeertubeChunkStore.CLEANER_INTERVAL_MS) 181 }, PeertubeChunkStore.CLEANER_INTERVAL_MS)
182 } 182 }
183 183
184 private checkExpiration () { 184 private async checkExpiration () {
185 this.expirationDB.transaction('rw', this.expirationDB.databases, async () => { 185 let databasesToDeleteInfo: { name: string }[] = []
186 // Update our database expiration since we are alive
187 await this.expirationDB.databases.put({
188 name: this.databaseName,
189 expiration: new Date().getTime() + PeertubeChunkStore.CLEANER_EXPIRATION_MS
190 })
191 186
192 const now = new Date().getTime() 187 try {
193 const databasesToDeleteInfo = await this.expirationDB.databases.where('expiration').below(now).toArray() 188 await this.expirationDB.transaction('rw', this.expirationDB.databases, async () => {
189 // Update our database expiration since we are alive
190 await this.expirationDB.databases.put({
191 name: this.databaseName,
192 expiration: new Date().getTime() + PeertubeChunkStore.CLEANER_EXPIRATION_MS
193 })
194 194
195 for (const databaseToDeleteInfo of databasesToDeleteInfo) { 195 const now = new Date().getTime()
196 await this.dropDatabase(databaseToDeleteInfo.name) 196 databasesToDeleteInfo = await this.expirationDB.databases.where('expiration').below(now).toArray()
197 })
198 } catch (err) {
199 console.error('Cannot update expiration of fetch expired databases.', err)
200 }
197 201
198 await this.expirationDB.databases.where({ name: databaseToDeleteInfo.name }).delete() 202 for (const databaseToDeleteInfo of databasesToDeleteInfo) {
199 } 203 await this.dropDatabase(databaseToDeleteInfo.name)
200 }).catch(err => console.error('Cannot check expiration.', err)) 204 }
201 } 205 }
202 206
203 private dropDatabase (databaseName: string) { 207 private async dropDatabase (databaseName: string) {
204 const dbToDelete = new ChunkDatabase(databaseName) 208 const dbToDelete = new ChunkDatabase(databaseName)
209 console.log('Destroying IndexDB database %s.', databaseName)
205 210
206 console.log('Deleting %s.', databaseName) 211 try {
207 return dbToDelete.delete() 212 await dbToDelete.delete()
208 .catch(err => console.error('Cannot delete %s.', databaseName)) 213
214 await this.expirationDB.transaction('rw', this.expirationDB.databases, () => {
215 return this.expirationDB.databases.where({ name: databaseName }).delete()
216 })
217 } catch (err) {
218 console.error('Cannot delete %s.', databaseName, err)
219 }
209 } 220 }
210 221
211 private nextTick (cb, err, val?) { 222 private nextTick (cb, err, val?) {
diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts
index c07430e6c..bcf1eaee6 100644
--- a/server/controllers/api/videos/index.ts
+++ b/server/controllers/api/videos/index.ts
@@ -244,7 +244,7 @@ async function addVideo (req: express.Request, res: express.Response, videoPhysi
244 244
245 video.VideoFiles = [ videoFile ] 245 video.VideoFiles = [ videoFile ]
246 246
247 if (videoInfo.tags) { 247 if (videoInfo.tags !== undefined) {
248 const tagInstances = await TagModel.findOrCreateTags(videoInfo.tags, t) 248 const tagInstances = await TagModel.findOrCreateTags(videoInfo.tags, t)
249 249
250 await video.$set('Tags', tagInstances, sequelizeOptions) 250 await video.$set('Tags', tagInstances, sequelizeOptions)
@@ -332,7 +332,7 @@ async function updateVideo (req: express.Request, res: express.Response) {
332 const videoInstanceUpdated = await videoInstance.save(sequelizeOptions) 332 const videoInstanceUpdated = await videoInstance.save(sequelizeOptions)
333 333
334 // Video tags update? 334 // Video tags update?
335 if (videoInfoToUpdate.tags) { 335 if (videoInfoToUpdate.tags !== undefined) {
336 const tagInstances = await TagModel.findOrCreateTags(videoInfoToUpdate.tags, t) 336 const tagInstances = await TagModel.findOrCreateTags(videoInfoToUpdate.tags, t)
337 337
338 await videoInstanceUpdated.$set('Tags', tagInstances, sequelizeOptions) 338 await videoInstanceUpdated.$set('Tags', tagInstances, sequelizeOptions)
diff --git a/server/helpers/custom-validators/misc.ts b/server/helpers/custom-validators/misc.ts
index 275482fa1..254b4db6c 100644
--- a/server/helpers/custom-validators/misc.ts
+++ b/server/helpers/custom-validators/misc.ts
@@ -35,7 +35,7 @@ function toIntOrNull (value: string) {
35 return validator.toInt(value) 35 return validator.toInt(value)
36} 36}
37 37
38function toStringOrNull (value: string) { 38function toValueOrNull (value: string) {
39 if (value === 'null') return null 39 if (value === 'null') return null
40 40
41 return value 41 return value
@@ -73,7 +73,7 @@ export {
73 isUUIDValid, 73 isUUIDValid,
74 isIdOrUUIDValid, 74 isIdOrUUIDValid,
75 isDateValid, 75 isDateValid,
76 toStringOrNull, 76 toValueOrNull,
77 isBooleanValid, 77 isBooleanValid,
78 toIntOrNull, 78 toIntOrNull,
79 isFileValid 79 isFileValid
diff --git a/server/helpers/custom-validators/videos.ts b/server/helpers/custom-validators/videos.ts
index c35db49ac..002324fe0 100644
--- a/server/helpers/custom-validators/videos.ts
+++ b/server/helpers/custom-validators/videos.ts
@@ -57,9 +57,11 @@ function isVideoTagValid (tag: string) {
57} 57}
58 58
59function isVideoTagsValid (tags: string[]) { 59function isVideoTagsValid (tags: string[]) {
60 return isArray(tags) && 60 return tags === null || (
61 validator.isInt(tags.length.toString(), VIDEOS_CONSTRAINTS_FIELDS.TAGS) && 61 isArray(tags) &&
62 tags.every(tag => isVideoTagValid(tag)) 62 validator.isInt(tags.length.toString(), VIDEOS_CONSTRAINTS_FIELDS.TAGS) &&
63 tags.every(tag => isVideoTagValid(tag))
64 )
63} 65}
64 66
65function isVideoAbuseReasonValid (value: string) { 67function isVideoAbuseReasonValid (value: string) {
diff --git a/server/middlewares/validators/videos.ts b/server/middlewares/validators/videos.ts
index aa2afb068..dd0246a63 100644
--- a/server/middlewares/validators/videos.ts
+++ b/server/middlewares/validators/videos.ts
@@ -2,7 +2,7 @@ import * as express from 'express'
2import 'express-validator' 2import 'express-validator'
3import { body, param, query } from 'express-validator/check' 3import { body, param, query } from 'express-validator/check'
4import { UserRight, VideoPrivacy } from '../../../shared' 4import { UserRight, VideoPrivacy } from '../../../shared'
5import { isBooleanValid, isIdOrUUIDValid, isIdValid, isUUIDValid, toIntOrNull, toStringOrNull } from '../../helpers/custom-validators/misc' 5import { isBooleanValid, isIdOrUUIDValid, isIdValid, isUUIDValid, toIntOrNull, toValueOrNull } from '../../helpers/custom-validators/misc'
6import { 6import {
7 isVideoAbuseReasonValid, 7 isVideoAbuseReasonValid,
8 isVideoCategoryValid, 8 isVideoCategoryValid,
@@ -52,21 +52,22 @@ const videosAddValidator = [
52 .custom(isVideoLicenceValid).withMessage('Should have a valid licence'), 52 .custom(isVideoLicenceValid).withMessage('Should have a valid licence'),
53 body('language') 53 body('language')
54 .optional() 54 .optional()
55 .customSanitizer(toStringOrNull) 55 .customSanitizer(toValueOrNull)
56 .custom(isVideoLanguageValid).withMessage('Should have a valid language'), 56 .custom(isVideoLanguageValid).withMessage('Should have a valid language'),
57 body('nsfw') 57 body('nsfw')
58 .toBoolean() 58 .toBoolean()
59 .custom(isBooleanValid).withMessage('Should have a valid NSFW attribute'), 59 .custom(isBooleanValid).withMessage('Should have a valid NSFW attribute'),
60 body('description') 60 body('description')
61 .optional() 61 .optional()
62 .customSanitizer(toStringOrNull) 62 .customSanitizer(toValueOrNull)
63 .custom(isVideoDescriptionValid).withMessage('Should have a valid description'), 63 .custom(isVideoDescriptionValid).withMessage('Should have a valid description'),
64 body('support') 64 body('support')
65 .optional() 65 .optional()
66 .customSanitizer(toStringOrNull) 66 .customSanitizer(toValueOrNull)
67 .custom(isVideoSupportValid).withMessage('Should have a valid support text'), 67 .custom(isVideoSupportValid).withMessage('Should have a valid support text'),
68 body('tags') 68 body('tags')
69 .optional() 69 .optional()
70 .customSanitizer(toValueOrNull)
70 .custom(isVideoTagsValid).withMessage('Should have correct tags'), 71 .custom(isVideoTagsValid).withMessage('Should have correct tags'),
71 body('commentsEnabled') 72 body('commentsEnabled')
72 .toBoolean() 73 .toBoolean()
@@ -142,7 +143,7 @@ const videosUpdateValidator = [
142 .custom(isVideoLicenceValid).withMessage('Should have a valid licence'), 143 .custom(isVideoLicenceValid).withMessage('Should have a valid licence'),
143 body('language') 144 body('language')
144 .optional() 145 .optional()
145 .customSanitizer(toStringOrNull) 146 .customSanitizer(toValueOrNull)
146 .custom(isVideoLanguageValid).withMessage('Should have a valid language'), 147 .custom(isVideoLanguageValid).withMessage('Should have a valid language'),
147 body('nsfw') 148 body('nsfw')
148 .optional() 149 .optional()
@@ -154,14 +155,15 @@ const videosUpdateValidator = [
154 .custom(isVideoPrivacyValid).withMessage('Should have correct video privacy'), 155 .custom(isVideoPrivacyValid).withMessage('Should have correct video privacy'),
155 body('description') 156 body('description')
156 .optional() 157 .optional()
157 .customSanitizer(toStringOrNull) 158 .customSanitizer(toValueOrNull)
158 .custom(isVideoDescriptionValid).withMessage('Should have a valid description'), 159 .custom(isVideoDescriptionValid).withMessage('Should have a valid description'),
159 body('support') 160 body('support')
160 .optional() 161 .optional()
161 .customSanitizer(toStringOrNull) 162 .customSanitizer(toValueOrNull)
162 .custom(isVideoSupportValid).withMessage('Should have a valid support text'), 163 .custom(isVideoSupportValid).withMessage('Should have a valid support text'),
163 body('tags') 164 body('tags')
164 .optional() 165 .optional()
166 .customSanitizer(toValueOrNull)
165 .custom(isVideoTagsValid).withMessage('Should have correct tags'), 167 .custom(isVideoTagsValid).withMessage('Should have correct tags'),
166 body('commentsEnabled') 168 body('commentsEnabled')
167 .optional() 169 .optional()
diff --git a/server/models/video/tag.ts b/server/models/video/tag.ts
index 0ae74d808..6d79a5575 100644
--- a/server/models/video/tag.ts
+++ b/server/models/video/tag.ts
@@ -37,6 +37,8 @@ export class TagModel extends Model<TagModel> {
37 Videos: VideoModel[] 37 Videos: VideoModel[]
38 38
39 static findOrCreateTags (tags: string[], transaction: Transaction) { 39 static findOrCreateTags (tags: string[], transaction: Transaction) {
40 if (tags === null) return []
41
40 const tasks: Bluebird<TagModel>[] = [] 42 const tasks: Bluebird<TagModel>[] = []
41 tags.forEach(tag => { 43 tags.forEach(tag => {
42 const query = { 44 const query = {