diff options
Diffstat (limited to 'server/models')
-rw-r--r-- | server/models/pod.js | 3 | ||||
-rw-r--r-- | server/models/request.js | 6 | ||||
-rw-r--r-- | server/models/tag.js | 30 | ||||
-rw-r--r-- | server/models/video.js | 97 | ||||
-rw-r--r-- | server/models/videoTag.js | 9 |
5 files changed, 101 insertions, 44 deletions
diff --git a/server/models/pod.js b/server/models/pod.js index 2c1f56203..fff6970a7 100644 --- a/server/models/pod.js +++ b/server/models/pod.js | |||
@@ -19,7 +19,6 @@ module.exports = function (sequelize, DataTypes) { | |||
19 | type: DataTypes.INTEGER, | 19 | type: DataTypes.INTEGER, |
20 | defaultValue: constants.FRIEND_SCORE.BASE | 20 | defaultValue: constants.FRIEND_SCORE.BASE |
21 | } | 21 | } |
22 | // Check createdAt | ||
23 | }, | 22 | }, |
24 | { | 23 | { |
25 | classMethods: { | 24 | classMethods: { |
@@ -68,7 +67,7 @@ function associate (models) { | |||
68 | this.belongsToMany(models.Request, { | 67 | this.belongsToMany(models.Request, { |
69 | foreignKey: 'podId', | 68 | foreignKey: 'podId', |
70 | through: models.RequestToPod, | 69 | through: models.RequestToPod, |
71 | onDelete: 'CASCADE' | 70 | onDelete: 'cascade' |
72 | }) | 71 | }) |
73 | } | 72 | } |
74 | 73 | ||
diff --git a/server/models/request.js b/server/models/request.js index 882f747b7..70aa32610 100644 --- a/server/models/request.js +++ b/server/models/request.js | |||
@@ -79,9 +79,11 @@ function deactivate () { | |||
79 | timer = null | 79 | timer = null |
80 | } | 80 | } |
81 | 81 | ||
82 | function flush () { | 82 | function flush (callback) { |
83 | removeAll.call(this, function (err) { | 83 | removeAll.call(this, function (err) { |
84 | if (err) logger.error('Cannot flush the requests.', { error: err }) | 84 | if (err) logger.error('Cannot flush the requests.', { error: err }) |
85 | |||
86 | return callback(err) | ||
85 | }) | 87 | }) |
86 | } | 88 | } |
87 | 89 | ||
@@ -298,7 +300,7 @@ function listWithLimitAndRandom (limit, callback) { | |||
298 | 300 | ||
299 | function removeAll (callback) { | 301 | function removeAll (callback) { |
300 | // Delete all requests | 302 | // Delete all requests |
301 | this.destroy({ truncate: true }).asCallback(callback) | 303 | this.truncate({ cascade: true }).asCallback(callback) |
302 | } | 304 | } |
303 | 305 | ||
304 | function removeWithEmptyTo (callback) { | 306 | function removeWithEmptyTo (callback) { |
diff --git a/server/models/tag.js b/server/models/tag.js new file mode 100644 index 000000000..874e88842 --- /dev/null +++ b/server/models/tag.js | |||
@@ -0,0 +1,30 @@ | |||
1 | 'use strict' | ||
2 | |||
3 | // --------------------------------------------------------------------------- | ||
4 | |||
5 | module.exports = function (sequelize, DataTypes) { | ||
6 | const Tag = sequelize.define('Tag', | ||
7 | { | ||
8 | name: { | ||
9 | type: DataTypes.STRING | ||
10 | } | ||
11 | }, | ||
12 | { | ||
13 | classMethods: { | ||
14 | associate | ||
15 | } | ||
16 | } | ||
17 | ) | ||
18 | |||
19 | return Tag | ||
20 | } | ||
21 | |||
22 | // --------------------------------------------------------------------------- | ||
23 | |||
24 | function associate (models) { | ||
25 | this.belongsToMany(models.Video, { | ||
26 | foreignKey: 'tagId', | ||
27 | through: models.VideoTag, | ||
28 | onDelete: 'cascade' | ||
29 | }) | ||
30 | } | ||
diff --git a/server/models/video.js b/server/models/video.js index 8ef07c9e6..0023a24e1 100644 --- a/server/models/video.js +++ b/server/models/video.js | |||
@@ -4,6 +4,7 @@ const createTorrent = require('create-torrent') | |||
4 | const ffmpeg = require('fluent-ffmpeg') | 4 | const ffmpeg = require('fluent-ffmpeg') |
5 | const fs = require('fs') | 5 | const fs = require('fs') |
6 | const magnetUtil = require('magnet-uri') | 6 | const magnetUtil = require('magnet-uri') |
7 | const map = require('lodash/map') | ||
7 | const parallel = require('async/parallel') | 8 | const parallel = require('async/parallel') |
8 | const parseTorrent = require('parse-torrent') | 9 | const parseTorrent = require('parse-torrent') |
9 | const pathUtils = require('path') | 10 | const pathUtils = require('path') |
@@ -41,9 +42,6 @@ module.exports = function (sequelize, DataTypes) { | |||
41 | }, | 42 | }, |
42 | duration: { | 43 | duration: { |
43 | type: DataTypes.INTEGER | 44 | type: DataTypes.INTEGER |
44 | }, | ||
45 | tags: { | ||
46 | type: DataTypes.ARRAY(DataTypes.STRING) | ||
47 | } | 45 | } |
48 | }, | 46 | }, |
49 | { | 47 | { |
@@ -54,12 +52,12 @@ module.exports = function (sequelize, DataTypes) { | |||
54 | getDurationFromFile, | 52 | getDurationFromFile, |
55 | listForApi, | 53 | listForApi, |
56 | listByHostAndRemoteId, | 54 | listByHostAndRemoteId, |
57 | listOwnedAndPopulateAuthor, | 55 | listOwnedAndPopulateAuthorAndTags, |
58 | listOwnedByAuthor, | 56 | listOwnedByAuthor, |
59 | load, | 57 | load, |
60 | loadAndPopulateAuthor, | 58 | loadAndPopulateAuthor, |
61 | loadAndPopulateAuthorAndPod, | 59 | loadAndPopulateAuthorAndPodAndTags, |
62 | searchAndPopulateAuthorAndPod | 60 | searchAndPopulateAuthorAndPodAndTags |
63 | }, | 61 | }, |
64 | instanceMethods: { | 62 | instanceMethods: { |
65 | generateMagnetUri, | 63 | generateMagnetUri, |
@@ -170,6 +168,12 @@ function associate (models) { | |||
170 | }, | 168 | }, |
171 | onDelete: 'cascade' | 169 | onDelete: 'cascade' |
172 | }) | 170 | }) |
171 | |||
172 | this.belongsToMany(models.Tag, { | ||
173 | foreignKey: 'videoId', | ||
174 | through: models.VideoTag, | ||
175 | onDelete: 'cascade' | ||
176 | }) | ||
173 | } | 177 | } |
174 | 178 | ||
175 | function generateMagnetUri () { | 179 | function generateMagnetUri () { |
@@ -248,7 +252,7 @@ function toFormatedJSON () { | |||
248 | magnetUri: this.generateMagnetUri(), | 252 | magnetUri: this.generateMagnetUri(), |
249 | author: this.Author.name, | 253 | author: this.Author.name, |
250 | duration: this.duration, | 254 | duration: this.duration, |
251 | tags: this.tags, | 255 | tags: map(this.Tags, 'name'), |
252 | thumbnailPath: constants.STATIC_PATHS.THUMBNAILS + '/' + this.getThumbnailName(), | 256 | thumbnailPath: constants.STATIC_PATHS.THUMBNAILS + '/' + this.getThumbnailName(), |
253 | createdAt: this.createdAt | 257 | createdAt: this.createdAt |
254 | } | 258 | } |
@@ -275,7 +279,7 @@ function toRemoteJSON (callback) { | |||
275 | author: self.Author.name, | 279 | author: self.Author.name, |
276 | duration: self.duration, | 280 | duration: self.duration, |
277 | thumbnailBase64: new Buffer(thumbnailData).toString('base64'), | 281 | thumbnailBase64: new Buffer(thumbnailData).toString('base64'), |
278 | tags: self.tags, | 282 | tags: map(self.Tags, 'name'), |
279 | createdAt: self.createdAt, | 283 | createdAt: self.createdAt, |
280 | extname: self.extname | 284 | extname: self.extname |
281 | } | 285 | } |
@@ -310,12 +314,15 @@ function listForApi (start, count, sort, callback) { | |||
310 | const query = { | 314 | const query = { |
311 | offset: start, | 315 | offset: start, |
312 | limit: count, | 316 | limit: count, |
317 | distinct: true, // For the count, a video can have many tags | ||
313 | order: [ modelUtils.getSort(sort) ], | 318 | order: [ modelUtils.getSort(sort) ], |
314 | include: [ | 319 | include: [ |
315 | { | 320 | { |
316 | model: this.sequelize.models.Author, | 321 | model: this.sequelize.models.Author, |
317 | include: [ this.sequelize.models.Pod ] | 322 | include: [ { model: this.sequelize.models.Pod, required: false } ] |
318 | } | 323 | }, |
324 | |||
325 | this.sequelize.models.Tag | ||
319 | ] | 326 | ] |
320 | } | 327 | } |
321 | 328 | ||
@@ -337,6 +344,7 @@ function listByHostAndRemoteId (fromHost, remoteId, callback) { | |||
337 | include: [ | 344 | include: [ |
338 | { | 345 | { |
339 | model: this.sequelize.models.Pod, | 346 | model: this.sequelize.models.Pod, |
347 | required: true, | ||
340 | where: { | 348 | where: { |
341 | host: fromHost | 349 | host: fromHost |
342 | } | 350 | } |
@@ -349,13 +357,13 @@ function listByHostAndRemoteId (fromHost, remoteId, callback) { | |||
349 | return this.findAll(query).asCallback(callback) | 357 | return this.findAll(query).asCallback(callback) |
350 | } | 358 | } |
351 | 359 | ||
352 | function listOwnedAndPopulateAuthor (callback) { | 360 | function listOwnedAndPopulateAuthorAndTags (callback) { |
353 | // If remoteId is null this is *our* video | 361 | // If remoteId is null this is *our* video |
354 | const query = { | 362 | const query = { |
355 | where: { | 363 | where: { |
356 | remoteId: null | 364 | remoteId: null |
357 | }, | 365 | }, |
358 | include: [ this.sequelize.models.Author ] | 366 | include: [ this.sequelize.models.Author, this.sequelize.models.Tag ] |
359 | } | 367 | } |
360 | 368 | ||
361 | return this.findAll(query).asCallback(callback) | 369 | return this.findAll(query).asCallback(callback) |
@@ -391,23 +399,26 @@ function loadAndPopulateAuthor (id, callback) { | |||
391 | return this.findById(id, options).asCallback(callback) | 399 | return this.findById(id, options).asCallback(callback) |
392 | } | 400 | } |
393 | 401 | ||
394 | function loadAndPopulateAuthorAndPod (id, callback) { | 402 | function loadAndPopulateAuthorAndPodAndTags (id, callback) { |
395 | const options = { | 403 | const options = { |
396 | include: [ | 404 | include: [ |
397 | { | 405 | { |
398 | model: this.sequelize.models.Author, | 406 | model: this.sequelize.models.Author, |
399 | include: [ this.sequelize.models.Pod ] | 407 | include: [ { model: this.sequelize.models.Pod, required: false } ] |
400 | } | 408 | }, |
409 | this.sequelize.models.Tag | ||
401 | ] | 410 | ] |
402 | } | 411 | } |
403 | 412 | ||
404 | return this.findById(id, options).asCallback(callback) | 413 | return this.findById(id, options).asCallback(callback) |
405 | } | 414 | } |
406 | 415 | ||
407 | function searchAndPopulateAuthorAndPod (value, field, start, count, sort, callback) { | 416 | function searchAndPopulateAuthorAndPodAndTags (value, field, start, count, sort, callback) { |
408 | const podInclude = { | 417 | const podInclude = { |
409 | model: this.sequelize.models.Pod | 418 | model: this.sequelize.models.Pod, |
419 | required: false | ||
410 | } | 420 | } |
421 | |||
411 | const authorInclude = { | 422 | const authorInclude = { |
412 | model: this.sequelize.models.Author, | 423 | model: this.sequelize.models.Author, |
413 | include: [ | 424 | include: [ |
@@ -415,55 +426,61 @@ function searchAndPopulateAuthorAndPod (value, field, start, count, sort, callba | |||
415 | ] | 426 | ] |
416 | } | 427 | } |
417 | 428 | ||
429 | const tagInclude = { | ||
430 | model: this.sequelize.models.Tag | ||
431 | } | ||
432 | |||
418 | const query = { | 433 | const query = { |
419 | where: {}, | 434 | where: {}, |
420 | include: [ | ||
421 | authorInclude | ||
422 | ], | ||
423 | offset: start, | 435 | offset: start, |
424 | limit: count, | 436 | limit: count, |
437 | distinct: true, // For the count, a video can have many tags | ||
425 | order: [ modelUtils.getSort(sort) ] | 438 | order: [ modelUtils.getSort(sort) ] |
426 | } | 439 | } |
427 | 440 | ||
428 | // TODO: include our pod for podHost searches (we are not stored in the database) | ||
429 | // Make an exact search with the magnet | 441 | // Make an exact search with the magnet |
430 | if (field === 'magnetUri') { | 442 | if (field === 'magnetUri') { |
431 | const infoHash = magnetUtil.decode(value).infoHash | 443 | const infoHash = magnetUtil.decode(value).infoHash |
432 | query.where.infoHash = infoHash | 444 | query.where.infoHash = infoHash |
433 | } else if (field === 'tags') { | 445 | } else if (field === 'tags') { |
434 | query.where[field] = value | 446 | const escapedValue = this.sequelize.escape('%' + value + '%') |
435 | } else if (field === 'host') { | 447 | query.where = { |
436 | const whereQuery = { | 448 | id: { |
437 | '$Author.Pod.host$': { | 449 | $in: this.sequelize.literal( |
438 | $like: '%' + value + '%' | 450 | '(SELECT "VideoTags"."videoId" FROM "Tags" INNER JOIN "VideoTags" ON "Tags"."id" = "VideoTags"."tagId" WHERE name LIKE ' + escapedValue + ')' |
451 | ) | ||
439 | } | 452 | } |
440 | } | 453 | } |
441 | 454 | } else if (field === 'host') { | |
442 | // Include our pod? (not stored in the database) | 455 | // FIXME: Include our pod? (not stored in the database) |
443 | if (constants.CONFIG.WEBSERVER.HOST.indexOf(value) !== -1) { | 456 | podInclude.where = { |
444 | query.where = { | 457 | host: { |
445 | $or: [ | 458 | $like: '%' + value + '%' |
446 | whereQuery, | ||
447 | { | ||
448 | remoteId: null | ||
449 | } | ||
450 | ] | ||
451 | } | 459 | } |
452 | } else { | ||
453 | query.where = whereQuery | ||
454 | } | 460 | } |
461 | podInclude.required = true | ||
455 | } else if (field === 'author') { | 462 | } else if (field === 'author') { |
456 | query.where = { | 463 | authorInclude.where = { |
457 | '$Author.name$': { | 464 | name: { |
458 | $like: '%' + value + '%' | 465 | $like: '%' + value + '%' |
459 | } | 466 | } |
460 | } | 467 | } |
468 | |||
469 | // authorInclude.or = true | ||
461 | } else { | 470 | } else { |
462 | query.where[field] = { | 471 | query.where[field] = { |
463 | $like: '%' + value + '%' | 472 | $like: '%' + value + '%' |
464 | } | 473 | } |
465 | } | 474 | } |
466 | 475 | ||
476 | query.include = [ | ||
477 | authorInclude, tagInclude | ||
478 | ] | ||
479 | |||
480 | if (tagInclude.where) { | ||
481 | // query.include.push([ this.sequelize.models.Tag ]) | ||
482 | } | ||
483 | |||
467 | return this.findAndCountAll(query).asCallback(function (err, result) { | 484 | return this.findAndCountAll(query).asCallback(function (err, result) { |
468 | if (err) return callback(err) | 485 | if (err) return callback(err) |
469 | 486 | ||
diff --git a/server/models/videoTag.js b/server/models/videoTag.js new file mode 100644 index 000000000..0f2b20838 --- /dev/null +++ b/server/models/videoTag.js | |||
@@ -0,0 +1,9 @@ | |||
1 | 'use strict' | ||
2 | |||
3 | // --------------------------------------------------------------------------- | ||
4 | |||
5 | module.exports = function (sequelize, DataTypes) { | ||
6 | const VideoTag = sequelize.define('VideoTag', {}, {}) | ||
7 | |||
8 | return VideoTag | ||
9 | } | ||