diff options
Diffstat (limited to 'server/models/video.ts')
-rw-r--r-- | server/models/video.ts | 379 |
1 files changed, 210 insertions, 169 deletions
diff --git a/server/models/video.ts b/server/models/video.ts index 1e29f1355..9284dfeba 100644 --- a/server/models/video.ts +++ b/server/models/video.ts | |||
@@ -8,8 +8,9 @@ import { map, values } from 'lodash' | |||
8 | import { parallel, series } from 'async' | 8 | import { parallel, series } from 'async' |
9 | import parseTorrent = require('parse-torrent') | 9 | import parseTorrent = require('parse-torrent') |
10 | import { join } from 'path' | 10 | import { join } from 'path' |
11 | import * as Sequelize from 'sequelize' | ||
11 | 12 | ||
12 | const db = require('../initializers/database') | 13 | import { database as db } from '../initializers/database' |
13 | import { | 14 | import { |
14 | logger, | 15 | logger, |
15 | isVideoNameValid, | 16 | isVideoNameValid, |
@@ -32,12 +33,42 @@ import { | |||
32 | THUMBNAILS_SIZE | 33 | THUMBNAILS_SIZE |
33 | } from '../initializers' | 34 | } from '../initializers' |
34 | import { JobScheduler, removeVideoToFriends } from '../lib' | 35 | import { JobScheduler, removeVideoToFriends } from '../lib' |
35 | import { getSort } from './utils' | ||
36 | 36 | ||
37 | // --------------------------------------------------------------------------- | 37 | import { addMethodsToModel, getSort } from './utils' |
38 | 38 | import { | |
39 | module.exports = function (sequelize, DataTypes) { | 39 | VideoClass, |
40 | const Video = sequelize.define('Video', | 40 | VideoInstance, |
41 | VideoAttributes, | ||
42 | |||
43 | VideoMethods | ||
44 | } from './video-interface' | ||
45 | |||
46 | let Video: Sequelize.Model<VideoInstance, VideoAttributes> | ||
47 | let generateMagnetUri: VideoMethods.GenerateMagnetUri | ||
48 | let getVideoFilename: VideoMethods.GetVideoFilename | ||
49 | let getThumbnailName: VideoMethods.GetThumbnailName | ||
50 | let getPreviewName: VideoMethods.GetPreviewName | ||
51 | let getTorrentName: VideoMethods.GetTorrentName | ||
52 | let isOwned: VideoMethods.IsOwned | ||
53 | let toFormatedJSON: VideoMethods.ToFormatedJSON | ||
54 | let toAddRemoteJSON: VideoMethods.ToAddRemoteJSON | ||
55 | let toUpdateRemoteJSON: VideoMethods.ToUpdateRemoteJSON | ||
56 | let transcodeVideofile: VideoMethods.TranscodeVideofile | ||
57 | |||
58 | let generateThumbnailFromData: VideoMethods.GenerateThumbnailFromData | ||
59 | let getDurationFromFile: VideoMethods.GetDurationFromFile | ||
60 | let list: VideoMethods.List | ||
61 | let listForApi: VideoMethods.ListForApi | ||
62 | let loadByHostAndRemoteId: VideoMethods.LoadByHostAndRemoteId | ||
63 | let listOwnedAndPopulateAuthorAndTags: VideoMethods.ListOwnedAndPopulateAuthorAndTags | ||
64 | let listOwnedByAuthor: VideoMethods.ListOwnedByAuthor | ||
65 | let load: VideoMethods.Load | ||
66 | let loadAndPopulateAuthor: VideoMethods.LoadAndPopulateAuthor | ||
67 | let loadAndPopulateAuthorAndPodAndTags: VideoMethods.LoadAndPopulateAuthorAndPodAndTags | ||
68 | let searchAndPopulateAuthorAndPodAndTags: VideoMethods.SearchAndPopulateAuthorAndPodAndTags | ||
69 | |||
70 | export default function (sequelize, DataTypes) { | ||
71 | Video = sequelize.define('Video', | ||
41 | { | 72 | { |
42 | id: { | 73 | id: { |
43 | type: DataTypes.UUID, | 74 | type: DataTypes.UUID, |
@@ -194,34 +225,6 @@ module.exports = function (sequelize, DataTypes) { | |||
194 | fields: [ 'likes' ] | 225 | fields: [ 'likes' ] |
195 | } | 226 | } |
196 | ], | 227 | ], |
197 | classMethods: { | ||
198 | associate, | ||
199 | |||
200 | generateThumbnailFromData, | ||
201 | getDurationFromFile, | ||
202 | list, | ||
203 | listForApi, | ||
204 | listOwnedAndPopulateAuthorAndTags, | ||
205 | listOwnedByAuthor, | ||
206 | load, | ||
207 | loadByHostAndRemoteId, | ||
208 | loadAndPopulateAuthor, | ||
209 | loadAndPopulateAuthorAndPodAndTags, | ||
210 | searchAndPopulateAuthorAndPodAndTags | ||
211 | }, | ||
212 | instanceMethods: { | ||
213 | generateMagnetUri, | ||
214 | getVideoFilename, | ||
215 | getThumbnailName, | ||
216 | getPreviewName, | ||
217 | getTorrentName, | ||
218 | isOwned, | ||
219 | toFormatedJSON, | ||
220 | toAddRemoteJSON, | ||
221 | toUpdateRemoteJSON, | ||
222 | transcodeVideofile, | ||
223 | removeFromBlacklist | ||
224 | }, | ||
225 | hooks: { | 228 | hooks: { |
226 | beforeValidate, | 229 | beforeValidate, |
227 | beforeCreate, | 230 | beforeCreate, |
@@ -230,99 +233,139 @@ module.exports = function (sequelize, DataTypes) { | |||
230 | } | 233 | } |
231 | ) | 234 | ) |
232 | 235 | ||
236 | const classMethods = [ | ||
237 | associate, | ||
238 | |||
239 | generateThumbnailFromData, | ||
240 | getDurationFromFile, | ||
241 | list, | ||
242 | listForApi, | ||
243 | listOwnedAndPopulateAuthorAndTags, | ||
244 | listOwnedByAuthor, | ||
245 | load, | ||
246 | loadByHostAndRemoteId, | ||
247 | loadAndPopulateAuthor, | ||
248 | loadAndPopulateAuthorAndPodAndTags, | ||
249 | searchAndPopulateAuthorAndPodAndTags | ||
250 | ] | ||
251 | const instanceMethods = [ | ||
252 | generateMagnetUri, | ||
253 | getVideoFilename, | ||
254 | getThumbnailName, | ||
255 | getPreviewName, | ||
256 | getTorrentName, | ||
257 | isOwned, | ||
258 | toFormatedJSON, | ||
259 | toAddRemoteJSON, | ||
260 | toUpdateRemoteJSON, | ||
261 | transcodeVideofile, | ||
262 | removeFromBlacklist | ||
263 | ] | ||
264 | addMethodsToModel(Video, classMethods, instanceMethods) | ||
265 | |||
233 | return Video | 266 | return Video |
234 | } | 267 | } |
235 | 268 | ||
236 | function beforeValidate (video, options, next) { | 269 | function beforeValidate (video, options) { |
237 | // Put a fake infoHash if it does not exists yet | 270 | // Put a fake infoHash if it does not exists yet |
238 | if (video.isOwned() && !video.infoHash) { | 271 | if (video.isOwned() && !video.infoHash) { |
239 | // 40 hexa length | 272 | // 40 hexa length |
240 | video.infoHash = '0123456789abcdef0123456789abcdef01234567' | 273 | video.infoHash = '0123456789abcdef0123456789abcdef01234567' |
241 | } | 274 | } |
242 | |||
243 | return next(null) | ||
244 | } | 275 | } |
245 | 276 | ||
246 | function beforeCreate (video, options, next) { | 277 | function beforeCreate (video, options) { |
247 | const tasks = [] | 278 | return new Promise(function (resolve, reject) { |
248 | 279 | const tasks = [] | |
249 | if (video.isOwned()) { | ||
250 | const videoPath = join(CONFIG.STORAGE.VIDEOS_DIR, video.getVideoFilename()) | ||
251 | |||
252 | tasks.push( | ||
253 | function createVideoTorrent (callback) { | ||
254 | createTorrentFromVideo(video, videoPath, callback) | ||
255 | }, | ||
256 | |||
257 | function createVideoThumbnail (callback) { | ||
258 | createThumbnail(video, videoPath, callback) | ||
259 | }, | ||
260 | 280 | ||
261 | function createVideoPreview (callback) { | 281 | if (video.isOwned()) { |
262 | createPreview(video, videoPath, callback) | 282 | const videoPath = join(CONFIG.STORAGE.VIDEOS_DIR, video.getVideoFilename()) |
263 | } | ||
264 | ) | ||
265 | 283 | ||
266 | if (CONFIG.TRANSCODING.ENABLED === true) { | ||
267 | tasks.push( | 284 | tasks.push( |
268 | function createVideoTranscoderJob (callback) { | 285 | function createVideoTorrent (callback) { |
269 | const dataInput = { | 286 | createTorrentFromVideo(video, videoPath, callback) |
270 | id: video.id | 287 | }, |
271 | } | 288 | |
289 | function createVideoThumbnail (callback) { | ||
290 | createThumbnail(video, videoPath, callback) | ||
291 | }, | ||
272 | 292 | ||
273 | JobScheduler.Instance.createJob(options.transaction, 'videoTranscoder', dataInput, callback) | 293 | function createVideoPreview (callback) { |
294 | createPreview(video, videoPath, callback) | ||
274 | } | 295 | } |
275 | ) | 296 | ) |
276 | } | ||
277 | 297 | ||
278 | return parallel(tasks, next) | 298 | if (CONFIG.TRANSCODING.ENABLED === true) { |
279 | } | 299 | tasks.push( |
300 | function createVideoTranscoderJob (callback) { | ||
301 | const dataInput = { | ||
302 | id: video.id | ||
303 | } | ||
280 | 304 | ||
281 | return next() | 305 | JobScheduler.Instance.createJob(options.transaction, 'videoTranscoder', dataInput, callback) |
282 | } | 306 | } |
307 | ) | ||
308 | } | ||
283 | 309 | ||
284 | function afterDestroy (video, options, next) { | 310 | return parallel(tasks, function (err) { |
285 | const tasks = [] | 311 | if (err) return reject(err) |
286 | 312 | ||
287 | tasks.push( | 313 | return resolve() |
288 | function (callback) { | 314 | }) |
289 | removeThumbnail(video, callback) | ||
290 | } | 315 | } |
291 | ) | ||
292 | 316 | ||
293 | if (video.isOwned()) { | 317 | return resolve() |
318 | }) | ||
319 | } | ||
320 | |||
321 | function afterDestroy (video, options) { | ||
322 | return new Promise(function (resolve, reject) { | ||
323 | const tasks = [] | ||
324 | |||
294 | tasks.push( | 325 | tasks.push( |
295 | function removeVideoFile (callback) { | 326 | function (callback) { |
296 | removeFile(video, callback) | 327 | removeThumbnail(video, callback) |
297 | }, | 328 | } |
329 | ) | ||
298 | 330 | ||
299 | function removeVideoTorrent (callback) { | 331 | if (video.isOwned()) { |
300 | removeTorrent(video, callback) | 332 | tasks.push( |
301 | }, | 333 | function removeVideoFile (callback) { |
334 | removeFile(video, callback) | ||
335 | }, | ||
302 | 336 | ||
303 | function removeVideoPreview (callback) { | 337 | function removeVideoTorrent (callback) { |
304 | removePreview(video, callback) | 338 | removeTorrent(video, callback) |
305 | }, | 339 | }, |
306 | 340 | ||
307 | function removeVideoToFriends (callback) { | 341 | function removeVideoPreview (callback) { |
308 | const params = { | 342 | removePreview(video, callback) |
309 | remoteId: video.id | 343 | }, |
310 | } | ||
311 | 344 | ||
312 | removeVideoToFriends(params) | 345 | function notifyFriends (callback) { |
346 | const params = { | ||
347 | remoteId: video.id | ||
348 | } | ||
313 | 349 | ||
314 | return callback() | 350 | removeVideoToFriends(params) |
315 | } | ||
316 | ) | ||
317 | } | ||
318 | 351 | ||
319 | parallel(tasks, next) | 352 | return callback() |
353 | } | ||
354 | ) | ||
355 | } | ||
356 | |||
357 | parallel(tasks, function (err) { | ||
358 | if (err) return reject(err) | ||
359 | |||
360 | return resolve() | ||
361 | }) | ||
362 | }) | ||
320 | } | 363 | } |
321 | 364 | ||
322 | // ------------------------------ METHODS ------------------------------ | 365 | // ------------------------------ METHODS ------------------------------ |
323 | 366 | ||
324 | function associate (models) { | 367 | function associate (models) { |
325 | this.belongsTo(models.Author, { | 368 | Video.belongsTo(models.Author, { |
326 | foreignKey: { | 369 | foreignKey: { |
327 | name: 'authorId', | 370 | name: 'authorId', |
328 | allowNull: false | 371 | allowNull: false |
@@ -330,13 +373,13 @@ function associate (models) { | |||
330 | onDelete: 'cascade' | 373 | onDelete: 'cascade' |
331 | }) | 374 | }) |
332 | 375 | ||
333 | this.belongsToMany(models.Tag, { | 376 | Video.belongsToMany(models.Tag, { |
334 | foreignKey: 'videoId', | 377 | foreignKey: 'videoId', |
335 | through: models.VideoTag, | 378 | through: models.VideoTag, |
336 | onDelete: 'cascade' | 379 | onDelete: 'cascade' |
337 | }) | 380 | }) |
338 | 381 | ||
339 | this.hasMany(models.VideoAbuse, { | 382 | Video.hasMany(models.VideoAbuse, { |
340 | foreignKey: { | 383 | foreignKey: { |
341 | name: 'videoId', | 384 | name: 'videoId', |
342 | allowNull: false | 385 | allowNull: false |
@@ -345,7 +388,7 @@ function associate (models) { | |||
345 | }) | 388 | }) |
346 | } | 389 | } |
347 | 390 | ||
348 | function generateMagnetUri () { | 391 | generateMagnetUri = function () { |
349 | let baseUrlHttp | 392 | let baseUrlHttp |
350 | let baseUrlWs | 393 | let baseUrlWs |
351 | 394 | ||
@@ -372,18 +415,18 @@ function generateMagnetUri () { | |||
372 | return magnetUtil.encode(magnetHash) | 415 | return magnetUtil.encode(magnetHash) |
373 | } | 416 | } |
374 | 417 | ||
375 | function getVideoFilename () { | 418 | getVideoFilename = function () { |
376 | if (this.isOwned()) return this.id + this.extname | 419 | if (this.isOwned()) return this.id + this.extname |
377 | 420 | ||
378 | return this.remoteId + this.extname | 421 | return this.remoteId + this.extname |
379 | } | 422 | } |
380 | 423 | ||
381 | function getThumbnailName () { | 424 | getThumbnailName = function () { |
382 | // We always have a copy of the thumbnail | 425 | // We always have a copy of the thumbnail |
383 | return this.id + '.jpg' | 426 | return this.id + '.jpg' |
384 | } | 427 | } |
385 | 428 | ||
386 | function getPreviewName () { | 429 | getPreviewName = function () { |
387 | const extension = '.jpg' | 430 | const extension = '.jpg' |
388 | 431 | ||
389 | if (this.isOwned()) return this.id + extension | 432 | if (this.isOwned()) return this.id + extension |
@@ -391,7 +434,7 @@ function getPreviewName () { | |||
391 | return this.remoteId + extension | 434 | return this.remoteId + extension |
392 | } | 435 | } |
393 | 436 | ||
394 | function getTorrentName () { | 437 | getTorrentName = function () { |
395 | const extension = '.torrent' | 438 | const extension = '.torrent' |
396 | 439 | ||
397 | if (this.isOwned()) return this.id + extension | 440 | if (this.isOwned()) return this.id + extension |
@@ -399,11 +442,11 @@ function getTorrentName () { | |||
399 | return this.remoteId + extension | 442 | return this.remoteId + extension |
400 | } | 443 | } |
401 | 444 | ||
402 | function isOwned () { | 445 | isOwned = function () { |
403 | return this.remoteId === null | 446 | return this.remoteId === null |
404 | } | 447 | } |
405 | 448 | ||
406 | function toFormatedJSON () { | 449 | toFormatedJSON = function () { |
407 | let podHost | 450 | let podHost |
408 | 451 | ||
409 | if (this.Author.Pod) { | 452 | if (this.Author.Pod) { |
@@ -453,43 +496,41 @@ function toFormatedJSON () { | |||
453 | return json | 496 | return json |
454 | } | 497 | } |
455 | 498 | ||
456 | function toAddRemoteJSON (callback) { | 499 | toAddRemoteJSON = function (callback) { |
457 | const self = this | ||
458 | |||
459 | // Get thumbnail data to send to the other pod | 500 | // Get thumbnail data to send to the other pod |
460 | const thumbnailPath = join(CONFIG.STORAGE.THUMBNAILS_DIR, this.getThumbnailName()) | 501 | const thumbnailPath = join(CONFIG.STORAGE.THUMBNAILS_DIR, this.getThumbnailName()) |
461 | fs.readFile(thumbnailPath, function (err, thumbnailData) { | 502 | fs.readFile(thumbnailPath, (err, thumbnailData) => { |
462 | if (err) { | 503 | if (err) { |
463 | logger.error('Cannot read the thumbnail of the video') | 504 | logger.error('Cannot read the thumbnail of the video') |
464 | return callback(err) | 505 | return callback(err) |
465 | } | 506 | } |
466 | 507 | ||
467 | const remoteVideo = { | 508 | const remoteVideo = { |
468 | name: self.name, | 509 | name: this.name, |
469 | category: self.category, | 510 | category: this.category, |
470 | licence: self.licence, | 511 | licence: this.licence, |
471 | language: self.language, | 512 | language: this.language, |
472 | nsfw: self.nsfw, | 513 | nsfw: this.nsfw, |
473 | description: self.description, | 514 | description: this.description, |
474 | infoHash: self.infoHash, | 515 | infoHash: this.infoHash, |
475 | remoteId: self.id, | 516 | remoteId: this.id, |
476 | author: self.Author.name, | 517 | author: this.Author.name, |
477 | duration: self.duration, | 518 | duration: this.duration, |
478 | thumbnailData: thumbnailData.toString('binary'), | 519 | thumbnailData: thumbnailData.toString('binary'), |
479 | tags: map(self.Tags, 'name'), | 520 | tags: map(this.Tags, 'name'), |
480 | createdAt: self.createdAt, | 521 | createdAt: this.createdAt, |
481 | updatedAt: self.updatedAt, | 522 | updatedAt: this.updatedAt, |
482 | extname: self.extname, | 523 | extname: this.extname, |
483 | views: self.views, | 524 | views: this.views, |
484 | likes: self.likes, | 525 | likes: this.likes, |
485 | dislikes: self.dislikes | 526 | dislikes: this.dislikes |
486 | } | 527 | } |
487 | 528 | ||
488 | return callback(null, remoteVideo) | 529 | return callback(null, remoteVideo) |
489 | }) | 530 | }) |
490 | } | 531 | } |
491 | 532 | ||
492 | function toUpdateRemoteJSON (callback) { | 533 | toUpdateRemoteJSON = function (callback) { |
493 | const json = { | 534 | const json = { |
494 | name: this.name, | 535 | name: this.name, |
495 | category: this.category, | 536 | category: this.category, |
@@ -513,7 +554,7 @@ function toUpdateRemoteJSON (callback) { | |||
513 | return json | 554 | return json |
514 | } | 555 | } |
515 | 556 | ||
516 | function transcodeVideofile (finalCallback) { | 557 | transcodeVideofile = function (finalCallback) { |
517 | const video = this | 558 | const video = this |
518 | 559 | ||
519 | const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR | 560 | const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR |
@@ -568,7 +609,7 @@ function transcodeVideofile (finalCallback) { | |||
568 | 609 | ||
569 | // ------------------------------ STATICS ------------------------------ | 610 | // ------------------------------ STATICS ------------------------------ |
570 | 611 | ||
571 | function generateThumbnailFromData (video, thumbnailData, callback) { | 612 | generateThumbnailFromData = function (video, thumbnailData, callback) { |
572 | // Creating the thumbnail for a remote video | 613 | // Creating the thumbnail for a remote video |
573 | 614 | ||
574 | const thumbnailName = video.getThumbnailName() | 615 | const thumbnailName = video.getThumbnailName() |
@@ -580,7 +621,7 @@ function generateThumbnailFromData (video, thumbnailData, callback) { | |||
580 | }) | 621 | }) |
581 | } | 622 | } |
582 | 623 | ||
583 | function getDurationFromFile (videoPath, callback) { | 624 | getDurationFromFile = function (videoPath, callback) { |
584 | ffmpeg.ffprobe(videoPath, function (err, metadata) { | 625 | ffmpeg.ffprobe(videoPath, function (err, metadata) { |
585 | if (err) return callback(err) | 626 | if (err) return callback(err) |
586 | 627 | ||
@@ -588,46 +629,46 @@ function getDurationFromFile (videoPath, callback) { | |||
588 | }) | 629 | }) |
589 | } | 630 | } |
590 | 631 | ||
591 | function list (callback) { | 632 | list = function (callback) { |
592 | return this.findAll().asCallback(callback) | 633 | return Video.findAll().asCallback(callback) |
593 | } | 634 | } |
594 | 635 | ||
595 | function listForApi (start, count, sort, callback) { | 636 | listForApi = function (start, count, sort, callback) { |
596 | // Exclude Blakclisted videos from the list | 637 | // Exclude Blakclisted videos from the list |
597 | const query = { | 638 | const query = { |
639 | distinct: true, | ||
598 | offset: start, | 640 | offset: start, |
599 | limit: count, | 641 | limit: count, |
600 | distinct: true, // For the count, a video can have many tags | 642 | order: [ getSort(sort), [ Video['sequelize'].models.Tag, 'name', 'ASC' ] ], |
601 | order: [ getSort(sort), [ this.sequelize.models.Tag, 'name', 'ASC' ] ], | ||
602 | include: [ | 643 | include: [ |
603 | { | 644 | { |
604 | model: this.sequelize.models.Author, | 645 | model: Video['sequelize'].models.Author, |
605 | include: [ { model: this.sequelize.models.Pod, required: false } ] | 646 | include: [ { model: Video['sequelize'].models.Pod, required: false } ] |
606 | }, | 647 | }, |
607 | 648 | ||
608 | this.sequelize.models.Tag | 649 | Video['sequelize'].models.Tag |
609 | ], | 650 | ], |
610 | where: createBaseVideosWhere.call(this) | 651 | where: createBaseVideosWhere() |
611 | } | 652 | } |
612 | 653 | ||
613 | return this.findAndCountAll(query).asCallback(function (err, result) { | 654 | return Video.findAndCountAll(query).asCallback(function (err, result) { |
614 | if (err) return callback(err) | 655 | if (err) return callback(err) |
615 | 656 | ||
616 | return callback(null, result.rows, result.count) | 657 | return callback(null, result.rows, result.count) |
617 | }) | 658 | }) |
618 | } | 659 | } |
619 | 660 | ||
620 | function loadByHostAndRemoteId (fromHost, remoteId, callback) { | 661 | loadByHostAndRemoteId = function (fromHost, remoteId, callback) { |
621 | const query = { | 662 | const query = { |
622 | where: { | 663 | where: { |
623 | remoteId: remoteId | 664 | remoteId: remoteId |
624 | }, | 665 | }, |
625 | include: [ | 666 | include: [ |
626 | { | 667 | { |
627 | model: this.sequelize.models.Author, | 668 | model: Video['sequelize'].models.Author, |
628 | include: [ | 669 | include: [ |
629 | { | 670 | { |
630 | model: this.sequelize.models.Pod, | 671 | model: Video['sequelize'].models.Pod, |
631 | required: true, | 672 | required: true, |
632 | where: { | 673 | where: { |
633 | host: fromHost | 674 | host: fromHost |
@@ -638,29 +679,29 @@ function loadByHostAndRemoteId (fromHost, remoteId, callback) { | |||
638 | ] | 679 | ] |
639 | } | 680 | } |
640 | 681 | ||
641 | return this.findOne(query).asCallback(callback) | 682 | return Video.findOne(query).asCallback(callback) |
642 | } | 683 | } |
643 | 684 | ||
644 | function listOwnedAndPopulateAuthorAndTags (callback) { | 685 | listOwnedAndPopulateAuthorAndTags = function (callback) { |
645 | // If remoteId is null this is *our* video | 686 | // If remoteId is null this is *our* video |
646 | const query = { | 687 | const query = { |
647 | where: { | 688 | where: { |
648 | remoteId: null | 689 | remoteId: null |
649 | }, | 690 | }, |
650 | include: [ this.sequelize.models.Author, this.sequelize.models.Tag ] | 691 | include: [ Video['sequelize'].models.Author, Video['sequelize'].models.Tag ] |
651 | } | 692 | } |
652 | 693 | ||
653 | return this.findAll(query).asCallback(callback) | 694 | return Video.findAll(query).asCallback(callback) |
654 | } | 695 | } |
655 | 696 | ||
656 | function listOwnedByAuthor (author, callback) { | 697 | listOwnedByAuthor = function (author, callback) { |
657 | const query = { | 698 | const query = { |
658 | where: { | 699 | where: { |
659 | remoteId: null | 700 | remoteId: null |
660 | }, | 701 | }, |
661 | include: [ | 702 | include: [ |
662 | { | 703 | { |
663 | model: this.sequelize.models.Author, | 704 | model: Video['sequelize'].models.Author, |
664 | where: { | 705 | where: { |
665 | name: author | 706 | name: author |
666 | } | 707 | } |
@@ -668,58 +709,58 @@ function listOwnedByAuthor (author, callback) { | |||
668 | ] | 709 | ] |
669 | } | 710 | } |
670 | 711 | ||
671 | return this.findAll(query).asCallback(callback) | 712 | return Video.findAll(query).asCallback(callback) |
672 | } | 713 | } |
673 | 714 | ||
674 | function load (id, callback) { | 715 | load = function (id, callback) { |
675 | return this.findById(id).asCallback(callback) | 716 | return Video.findById(id).asCallback(callback) |
676 | } | 717 | } |
677 | 718 | ||
678 | function loadAndPopulateAuthor (id, callback) { | 719 | loadAndPopulateAuthor = function (id, callback) { |
679 | const options = { | 720 | const options = { |
680 | include: [ this.sequelize.models.Author ] | 721 | include: [ Video['sequelize'].models.Author ] |
681 | } | 722 | } |
682 | 723 | ||
683 | return this.findById(id, options).asCallback(callback) | 724 | return Video.findById(id, options).asCallback(callback) |
684 | } | 725 | } |
685 | 726 | ||
686 | function loadAndPopulateAuthorAndPodAndTags (id, callback) { | 727 | loadAndPopulateAuthorAndPodAndTags = function (id, callback) { |
687 | const options = { | 728 | const options = { |
688 | include: [ | 729 | include: [ |
689 | { | 730 | { |
690 | model: this.sequelize.models.Author, | 731 | model: Video['sequelize'].models.Author, |
691 | include: [ { model: this.sequelize.models.Pod, required: false } ] | 732 | include: [ { model: Video['sequelize'].models.Pod, required: false } ] |
692 | }, | 733 | }, |
693 | this.sequelize.models.Tag | 734 | Video['sequelize'].models.Tag |
694 | ] | 735 | ] |
695 | } | 736 | } |
696 | 737 | ||
697 | return this.findById(id, options).asCallback(callback) | 738 | return Video.findById(id, options).asCallback(callback) |
698 | } | 739 | } |
699 | 740 | ||
700 | function searchAndPopulateAuthorAndPodAndTags (value, field, start, count, sort, callback) { | 741 | searchAndPopulateAuthorAndPodAndTags = function (value, field, start, count, sort, callback) { |
701 | const podInclude: any = { | 742 | const podInclude: any = { |
702 | model: this.sequelize.models.Pod, | 743 | model: Video['sequelize'].models.Pod, |
703 | required: false | 744 | required: false |
704 | } | 745 | } |
705 | 746 | ||
706 | const authorInclude: any = { | 747 | const authorInclude: any = { |
707 | model: this.sequelize.models.Author, | 748 | model: Video['sequelize'].models.Author, |
708 | include: [ | 749 | include: [ |
709 | podInclude | 750 | podInclude |
710 | ] | 751 | ] |
711 | } | 752 | } |
712 | 753 | ||
713 | const tagInclude: any = { | 754 | const tagInclude: any = { |
714 | model: this.sequelize.models.Tag | 755 | model: Video['sequelize'].models.Tag |
715 | } | 756 | } |
716 | 757 | ||
717 | const query: any = { | 758 | const query: any = { |
718 | where: createBaseVideosWhere.call(this), | 759 | distinct: true, |
760 | where: createBaseVideosWhere(), | ||
719 | offset: start, | 761 | offset: start, |
720 | limit: count, | 762 | limit: count, |
721 | distinct: true, // For the count, a video can have many tags | 763 | order: [ getSort(sort), [ Video['sequelize'].models.Tag, 'name', 'ASC' ] ] |
722 | order: [ getSort(sort), [ this.sequelize.models.Tag, 'name', 'ASC' ] ] | ||
723 | } | 764 | } |
724 | 765 | ||
725 | // Make an exact search with the magnet | 766 | // Make an exact search with the magnet |
@@ -727,8 +768,8 @@ function searchAndPopulateAuthorAndPodAndTags (value, field, start, count, sort, | |||
727 | const infoHash = magnetUtil.decode(value).infoHash | 768 | const infoHash = magnetUtil.decode(value).infoHash |
728 | query.where.infoHash = infoHash | 769 | query.where.infoHash = infoHash |
729 | } else if (field === 'tags') { | 770 | } else if (field === 'tags') { |
730 | const escapedValue = this.sequelize.escape('%' + value + '%') | 771 | const escapedValue = Video['sequelize'].escape('%' + value + '%') |
731 | query.where.id.$in = this.sequelize.literal( | 772 | query.where.id.$in = Video['sequelize'].literal( |
732 | '(SELECT "VideoTags"."videoId" FROM "Tags" INNER JOIN "VideoTags" ON "Tags"."id" = "VideoTags"."tagId" WHERE name LIKE ' + escapedValue + ')' | 773 | '(SELECT "VideoTags"."videoId" FROM "Tags" INNER JOIN "VideoTags" ON "Tags"."id" = "VideoTags"."tagId" WHERE name LIKE ' + escapedValue + ')' |
733 | ) | 774 | ) |
734 | } else if (field === 'host') { | 775 | } else if (field === 'host') { |
@@ -758,10 +799,10 @@ function searchAndPopulateAuthorAndPodAndTags (value, field, start, count, sort, | |||
758 | ] | 799 | ] |
759 | 800 | ||
760 | if (tagInclude.where) { | 801 | if (tagInclude.where) { |
761 | // query.include.push([ this.sequelize.models.Tag ]) | 802 | // query.include.push([ Video['sequelize'].models.Tag ]) |
762 | } | 803 | } |
763 | 804 | ||
764 | return this.findAndCountAll(query).asCallback(function (err, result) { | 805 | return Video.findAndCountAll(query).asCallback(function (err, result) { |
765 | if (err) return callback(err) | 806 | if (err) return callback(err) |
766 | 807 | ||
767 | return callback(null, result.rows, result.count) | 808 | return callback(null, result.rows, result.count) |
@@ -773,7 +814,7 @@ function searchAndPopulateAuthorAndPodAndTags (value, field, start, count, sort, | |||
773 | function createBaseVideosWhere () { | 814 | function createBaseVideosWhere () { |
774 | return { | 815 | return { |
775 | id: { | 816 | id: { |
776 | $notIn: this.sequelize.literal( | 817 | $notIn: Video['sequelize'].literal( |
777 | '(SELECT "BlacklistedVideos"."videoId" FROM "BlacklistedVideos")' | 818 | '(SELECT "BlacklistedVideos"."videoId" FROM "BlacklistedVideos")' |
778 | ) | 819 | ) |
779 | } | 820 | } |