aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/models/video/video.ts
diff options
context:
space:
mode:
Diffstat (limited to 'server/models/video/video.ts')
-rw-r--r--server/models/video/video.ts378
1 files changed, 200 insertions, 178 deletions
diff --git a/server/models/video/video.ts b/server/models/video/video.ts
index 02dde1726..94af1ece5 100644
--- a/server/models/video/video.ts
+++ b/server/models/video/video.ts
@@ -5,7 +5,6 @@ import { map, maxBy, truncate } from 'lodash'
5import * as parseTorrent from 'parse-torrent' 5import * as parseTorrent from 'parse-torrent'
6import { join } from 'path' 6import { join } from 'path'
7import * as Sequelize from 'sequelize' 7import * as Sequelize from 'sequelize'
8import * as Promise from 'bluebird'
9 8
10import { TagInstance } from './tag-interface' 9import { TagInstance } from './tag-interface'
11import { 10import {
@@ -52,6 +51,7 @@ import {
52 51
53 VideoMethods 52 VideoMethods
54} from './video-interface' 53} from './video-interface'
54import { VideoTorrentObject } from '../../../shared/models/activitypub/objects/video-torrent-object'
55 55
56let Video: Sequelize.Model<VideoInstance, VideoAttributes> 56let Video: Sequelize.Model<VideoInstance, VideoAttributes>
57let getOriginalFile: VideoMethods.GetOriginalFile 57let getOriginalFile: VideoMethods.GetOriginalFile
@@ -64,8 +64,7 @@ let getTorrentFileName: VideoMethods.GetTorrentFileName
64let isOwned: VideoMethods.IsOwned 64let isOwned: VideoMethods.IsOwned
65let toFormattedJSON: VideoMethods.ToFormattedJSON 65let toFormattedJSON: VideoMethods.ToFormattedJSON
66let toFormattedDetailsJSON: VideoMethods.ToFormattedDetailsJSON 66let toFormattedDetailsJSON: VideoMethods.ToFormattedDetailsJSON
67let toAddRemoteJSON: VideoMethods.ToAddRemoteJSON 67let toActivityPubObject: VideoMethods.ToActivityPubObject
68let toUpdateRemoteJSON: VideoMethods.ToUpdateRemoteJSON
69let optimizeOriginalVideofile: VideoMethods.OptimizeOriginalVideofile 68let optimizeOriginalVideofile: VideoMethods.OptimizeOriginalVideofile
70let transcodeOriginalVideofile: VideoMethods.TranscodeOriginalVideofile 69let transcodeOriginalVideofile: VideoMethods.TranscodeOriginalVideofile
71let createPreview: VideoMethods.CreatePreview 70let createPreview: VideoMethods.CreatePreview
@@ -76,21 +75,25 @@ let getOriginalFileHeight: VideoMethods.GetOriginalFileHeight
76let getEmbedPath: VideoMethods.GetEmbedPath 75let getEmbedPath: VideoMethods.GetEmbedPath
77let getDescriptionPath: VideoMethods.GetDescriptionPath 76let getDescriptionPath: VideoMethods.GetDescriptionPath
78let getTruncatedDescription: VideoMethods.GetTruncatedDescription 77let getTruncatedDescription: VideoMethods.GetTruncatedDescription
78let getCategoryLabel: VideoMethods.GetCategoryLabel
79let getLicenceLabel: VideoMethods.GetLicenceLabel
80let getLanguageLabel: VideoMethods.GetLanguageLabel
79 81
80let generateThumbnailFromData: VideoMethods.GenerateThumbnailFromData 82let generateThumbnailFromData: VideoMethods.GenerateThumbnailFromData
81let list: VideoMethods.List 83let list: VideoMethods.List
82let listForApi: VideoMethods.ListForApi 84let listForApi: VideoMethods.ListForApi
83let listUserVideosForApi: VideoMethods.ListUserVideosForApi 85let listUserVideosForApi: VideoMethods.ListUserVideosForApi
84let loadByHostAndUUID: VideoMethods.LoadByHostAndUUID 86let loadByHostAndUUID: VideoMethods.LoadByHostAndUUID
85let listOwnedAndPopulateAuthorAndTags: VideoMethods.ListOwnedAndPopulateAuthorAndTags 87let listOwnedAndPopulateAccountAndTags: VideoMethods.ListOwnedAndPopulateAccountAndTags
86let listOwnedByAuthor: VideoMethods.ListOwnedByAuthor 88let listOwnedByAccount: VideoMethods.ListOwnedByAccount
87let load: VideoMethods.Load 89let load: VideoMethods.Load
88let loadByUUID: VideoMethods.LoadByUUID 90let loadByUUID: VideoMethods.LoadByUUID
91let loadByUrl: VideoMethods.LoadByUrl
89let loadLocalVideoByUUID: VideoMethods.LoadLocalVideoByUUID 92let loadLocalVideoByUUID: VideoMethods.LoadLocalVideoByUUID
90let loadAndPopulateAuthor: VideoMethods.LoadAndPopulateAuthor 93let loadAndPopulateAccount: VideoMethods.LoadAndPopulateAccount
91let loadAndPopulateAuthorAndPodAndTags: VideoMethods.LoadAndPopulateAuthorAndPodAndTags 94let loadAndPopulateAccountAndPodAndTags: VideoMethods.LoadAndPopulateAccountAndPodAndTags
92let loadByUUIDAndPopulateAuthorAndPodAndTags: VideoMethods.LoadByUUIDAndPopulateAuthorAndPodAndTags 95let loadByUUIDAndPopulateAccountAndPodAndTags: VideoMethods.LoadByUUIDAndPopulateAccountAndPodAndTags
93let searchAndPopulateAuthorAndPodAndTags: VideoMethods.SearchAndPopulateAuthorAndPodAndTags 96let searchAndPopulateAccountAndPodAndTags: VideoMethods.SearchAndPopulateAccountAndPodAndTags
94let removeThumbnail: VideoMethods.RemoveThumbnail 97let removeThumbnail: VideoMethods.RemoveThumbnail
95let removePreview: VideoMethods.RemovePreview 98let removePreview: VideoMethods.RemovePreview
96let removeFile: VideoMethods.RemoveFile 99let removeFile: VideoMethods.RemoveFile
@@ -219,6 +222,13 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da
219 type: DataTypes.BOOLEAN, 222 type: DataTypes.BOOLEAN,
220 allowNull: false, 223 allowNull: false,
221 defaultValue: false 224 defaultValue: false
225 },
226 url: {
227 type: DataTypes.STRING,
228 allowNull: false,
229 validate: {
230 isUrl: true
231 }
222 } 232 }
223 }, 233 },
224 { 234 {
@@ -243,6 +253,9 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da
243 }, 253 },
244 { 254 {
245 fields: [ 'channelId' ] 255 fields: [ 'channelId' ]
256 },
257 {
258 fields: [ 'parentId' ]
246 } 259 }
247 ], 260 ],
248 hooks: { 261 hooks: {
@@ -258,16 +271,16 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da
258 list, 271 list,
259 listForApi, 272 listForApi,
260 listUserVideosForApi, 273 listUserVideosForApi,
261 listOwnedAndPopulateAuthorAndTags, 274 listOwnedAndPopulateAccountAndTags,
262 listOwnedByAuthor, 275 listOwnedByAccount,
263 load, 276 load,
264 loadAndPopulateAuthor, 277 loadAndPopulateAccount,
265 loadAndPopulateAuthorAndPodAndTags, 278 loadAndPopulateAccountAndPodAndTags,
266 loadByHostAndUUID, 279 loadByHostAndUUID,
267 loadByUUID, 280 loadByUUID,
268 loadLocalVideoByUUID, 281 loadLocalVideoByUUID,
269 loadByUUIDAndPopulateAuthorAndPodAndTags, 282 loadByUUIDAndPopulateAccountAndPodAndTags,
270 searchAndPopulateAuthorAndPodAndTags 283 searchAndPopulateAccountAndPodAndTags
271 ] 284 ]
272 const instanceMethods = [ 285 const instanceMethods = [
273 createPreview, 286 createPreview,
@@ -286,16 +299,18 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da
286 removePreview, 299 removePreview,
287 removeThumbnail, 300 removeThumbnail,
288 removeTorrent, 301 removeTorrent,
289 toAddRemoteJSON, 302 toActivityPubObject,
290 toFormattedJSON, 303 toFormattedJSON,
291 toFormattedDetailsJSON, 304 toFormattedDetailsJSON,
292 toUpdateRemoteJSON,
293 optimizeOriginalVideofile, 305 optimizeOriginalVideofile,
294 transcodeOriginalVideofile, 306 transcodeOriginalVideofile,
295 getOriginalFileHeight, 307 getOriginalFileHeight,
296 getEmbedPath, 308 getEmbedPath,
297 getTruncatedDescription, 309 getTruncatedDescription,
298 getDescriptionPath 310 getDescriptionPath,
311 getCategoryLabel,
312 getLicenceLabel,
313 getLanguageLabel
299 ] 314 ]
300 addMethodsToModel(Video, classMethods, instanceMethods) 315 addMethodsToModel(Video, classMethods, instanceMethods)
301 316
@@ -313,6 +328,14 @@ function associate (models) {
313 onDelete: 'cascade' 328 onDelete: 'cascade'
314 }) 329 })
315 330
331 Video.belongsTo(models.VideoChannel, {
332 foreignKey: {
333 name: 'parentId',
334 allowNull: true
335 },
336 onDelete: 'cascade'
337 })
338
316 Video.belongsToMany(models.Tag, { 339 Video.belongsToMany(models.Tag, {
317 foreignKey: 'videoId', 340 foreignKey: 'videoId',
318 through: models.VideoTag, 341 through: models.VideoTag,
@@ -423,7 +446,7 @@ getVideoFilePath = function (this: VideoInstance, videoFile: VideoFileInstance)
423 return join(CONFIG.STORAGE.VIDEOS_DIR, this.getVideoFilename(videoFile)) 446 return join(CONFIG.STORAGE.VIDEOS_DIR, this.getVideoFilename(videoFile))
424} 447}
425 448
426createTorrentAndSetInfoHash = function (this: VideoInstance, videoFile: VideoFileInstance) { 449createTorrentAndSetInfoHash = async function (this: VideoInstance, videoFile: VideoFileInstance) {
427 const options = { 450 const options = {
428 announceList: [ 451 announceList: [
429 [ CONFIG.WEBSERVER.WS + '://' + CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT + '/tracker/socket' ] 452 [ CONFIG.WEBSERVER.WS + '://' + CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT + '/tracker/socket' ]
@@ -433,18 +456,15 @@ createTorrentAndSetInfoHash = function (this: VideoInstance, videoFile: VideoFil
433 ] 456 ]
434 } 457 }
435 458
436 return createTorrentPromise(this.getVideoFilePath(videoFile), options) 459 const torrent = await createTorrentPromise(this.getVideoFilePath(videoFile), options)
437 .then(torrent => {
438 const filePath = join(CONFIG.STORAGE.TORRENTS_DIR, this.getTorrentFileName(videoFile))
439 logger.info('Creating torrent %s.', filePath)
440 460
441 return writeFilePromise(filePath, torrent).then(() => torrent) 461 const filePath = join(CONFIG.STORAGE.TORRENTS_DIR, this.getTorrentFileName(videoFile))
442 }) 462 logger.info('Creating torrent %s.', filePath)
443 .then(torrent => {
444 const parsedTorrent = parseTorrent(torrent)
445 463
446 videoFile.infoHash = parsedTorrent.infoHash 464 await writeFilePromise(filePath, torrent)
447 }) 465
466 const parsedTorrent = parseTorrent(torrent)
467 videoFile.infoHash = parsedTorrent.infoHash
448} 468}
449 469
450getEmbedPath = function (this: VideoInstance) { 470getEmbedPath = function (this: VideoInstance) {
@@ -462,40 +482,28 @@ getPreviewPath = function (this: VideoInstance) {
462toFormattedJSON = function (this: VideoInstance) { 482toFormattedJSON = function (this: VideoInstance) {
463 let podHost 483 let podHost
464 484
465 if (this.VideoChannel.Author.Pod) { 485 if (this.VideoChannel.Account.Pod) {
466 podHost = this.VideoChannel.Author.Pod.host 486 podHost = this.VideoChannel.Account.Pod.host
467 } else { 487 } else {
468 // It means it's our video 488 // It means it's our video
469 podHost = CONFIG.WEBSERVER.HOST 489 podHost = CONFIG.WEBSERVER.HOST
470 } 490 }
471 491
472 // Maybe our pod is not up to date and there are new categories since our version
473 let categoryLabel = VIDEO_CATEGORIES[this.category]
474 if (!categoryLabel) categoryLabel = 'Misc'
475
476 // Maybe our pod is not up to date and there are new licences since our version
477 let licenceLabel = VIDEO_LICENCES[this.licence]
478 if (!licenceLabel) licenceLabel = 'Unknown'
479
480 // Language is an optional attribute
481 let languageLabel = VIDEO_LANGUAGES[this.language]
482 if (!languageLabel) languageLabel = 'Unknown'
483
484 const json = { 492 const json = {
485 id: this.id, 493 id: this.id,
486 uuid: this.uuid, 494 uuid: this.uuid,
487 name: this.name, 495 name: this.name,
488 category: this.category, 496 category: this.category,
489 categoryLabel, 497 categoryLabel: this.getCategoryLabel(),
490 licence: this.licence, 498 licence: this.licence,
491 licenceLabel, 499 licenceLabel: this.getLicenceLabel(),
492 language: this.language, 500 language: this.language,
493 languageLabel, 501 languageLabel: this.getLanguageLabel(),
494 nsfw: this.nsfw, 502 nsfw: this.nsfw,
495 description: this.getTruncatedDescription(), 503 description: this.getTruncatedDescription(),
496 podHost, 504 podHost,
497 isLocal: this.isOwned(), 505 isLocal: this.isOwned(),
498 author: this.VideoChannel.Author.name, 506 account: this.VideoChannel.Account.name,
499 duration: this.duration, 507 duration: this.duration,
500 views: this.views, 508 views: this.views,
501 likes: this.likes, 509 likes: this.likes,
@@ -552,75 +560,75 @@ toFormattedDetailsJSON = function (this: VideoInstance) {
552 return Object.assign(formattedJson, detailsJson) 560 return Object.assign(formattedJson, detailsJson)
553} 561}
554 562
555toAddRemoteJSON = function (this: VideoInstance) { 563toActivityPubObject = function (this: VideoInstance) {
556 // Get thumbnail data to send to the other pod 564 const { baseUrlHttp, baseUrlWs } = getBaseUrls(this)
557 const thumbnailPath = join(CONFIG.STORAGE.THUMBNAILS_DIR, this.getThumbnailName())
558 565
559 return readFileBufferPromise(thumbnailPath).then(thumbnailData => { 566 const tag = this.Tags.map(t => ({
560 const remoteVideo = { 567 type: 'Hashtag',
561 uuid: this.uuid, 568 name: t.name
562 name: this.name, 569 }))
563 category: this.category, 570
564 licence: this.licence, 571 const url = []
565 language: this.language, 572 for (const file of this.VideoFiles) {
566 nsfw: this.nsfw, 573 url.push({
567 truncatedDescription: this.getTruncatedDescription(), 574 type: 'Link',
568 channelUUID: this.VideoChannel.uuid, 575 mimeType: 'video/' + file.extname,
569 duration: this.duration, 576 url: getVideoFileUrl(this, file, baseUrlHttp),
570 thumbnailData: thumbnailData.toString('binary'), 577 width: file.resolution,
571 tags: map<TagInstance, string>(this.Tags, 'name'), 578 size: file.size
572 createdAt: this.createdAt, 579 })
573 updatedAt: this.updatedAt,
574 views: this.views,
575 likes: this.likes,
576 dislikes: this.dislikes,
577 privacy: this.privacy,
578 files: []
579 }
580 580
581 this.VideoFiles.forEach(videoFile => { 581 url.push({
582 remoteVideo.files.push({ 582 type: 'Link',
583 infoHash: videoFile.infoHash, 583 mimeType: 'application/x-bittorrent',
584 resolution: videoFile.resolution, 584 url: getTorrentUrl(this, file, baseUrlHttp),
585 extname: videoFile.extname, 585 width: file.resolution
586 size: videoFile.size
587 })
588 }) 586 })
589 587
590 return remoteVideo 588 url.push({
591 }) 589 type: 'Link',
592} 590 mimeType: 'application/x-bittorrent;x-scheme-handler/magnet',
591 url: generateMagnetUri(this, file, baseUrlHttp, baseUrlWs),
592 width: file.resolution
593 })
594 }
593 595
594toUpdateRemoteJSON = function (this: VideoInstance) { 596 const videoObject: VideoTorrentObject = {
595 const json = { 597 type: 'Video',
596 uuid: this.uuid,
597 name: this.name, 598 name: this.name,
598 category: this.category, 599 // https://www.w3.org/TR/activitystreams-vocabulary/#dfn-duration
599 licence: this.licence, 600 duration: 'PT' + this.duration + 'S',
600 language: this.language, 601 uuid: this.uuid,
601 nsfw: this.nsfw, 602 tag,
602 truncatedDescription: this.getTruncatedDescription(), 603 category: {
603 duration: this.duration, 604 id: this.category,
604 tags: map<TagInstance, string>(this.Tags, 'name'), 605 label: this.getCategoryLabel()
605 createdAt: this.createdAt, 606 },
606 updatedAt: this.updatedAt, 607 licence: {
608 id: this.licence,
609 name: this.getLicenceLabel()
610 },
611 language: {
612 id: this.language,
613 name: this.getLanguageLabel()
614 },
607 views: this.views, 615 views: this.views,
608 likes: this.likes, 616 nsfw: this.nsfw,
609 dislikes: this.dislikes, 617 published: this.createdAt,
610 privacy: this.privacy, 618 updated: this.updatedAt,
611 files: [] 619 mediaType: 'text/markdown',
620 content: this.getTruncatedDescription(),
621 icon: {
622 type: 'Image',
623 url: getThumbnailUrl(this, baseUrlHttp),
624 mediaType: 'image/jpeg',
625 width: THUMBNAILS_SIZE.width,
626 height: THUMBNAILS_SIZE.height
627 },
628 url
612 } 629 }
613 630
614 this.VideoFiles.forEach(videoFile => { 631 return videoObject
615 json.files.push({
616 infoHash: videoFile.infoHash,
617 resolution: videoFile.resolution,
618 extname: videoFile.extname,
619 size: videoFile.size
620 })
621 })
622
623 return json
624} 632}
625 633
626getTruncatedDescription = function (this: VideoInstance) { 634getTruncatedDescription = function (this: VideoInstance) {
@@ -631,7 +639,7 @@ getTruncatedDescription = function (this: VideoInstance) {
631 return truncate(this.description, options) 639 return truncate(this.description, options)
632} 640}
633 641
634optimizeOriginalVideofile = function (this: VideoInstance) { 642optimizeOriginalVideofile = async function (this: VideoInstance) {
635 const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR 643 const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR
636 const newExtname = '.mp4' 644 const newExtname = '.mp4'
637 const inputVideoFile = this.getOriginalFile() 645 const inputVideoFile = this.getOriginalFile()
@@ -643,40 +651,32 @@ optimizeOriginalVideofile = function (this: VideoInstance) {
643 outputPath: videoOutputPath 651 outputPath: videoOutputPath
644 } 652 }
645 653
646 return transcode(transcodeOptions) 654 try {
647 .then(() => { 655 // Could be very long!
648 return unlinkPromise(videoInputPath) 656 await transcode(transcodeOptions)
649 })
650 .then(() => {
651 // Important to do this before getVideoFilename() to take in account the new file extension
652 inputVideoFile.set('extname', newExtname)
653 657
654 return renamePromise(videoOutputPath, this.getVideoFilePath(inputVideoFile)) 658 await unlinkPromise(videoInputPath)
655 })
656 .then(() => {
657 return statPromise(this.getVideoFilePath(inputVideoFile))
658 })
659 .then(stats => {
660 return inputVideoFile.set('size', stats.size)
661 })
662 .then(() => {
663 return this.createTorrentAndSetInfoHash(inputVideoFile)
664 })
665 .then(() => {
666 return inputVideoFile.save()
667 })
668 .then(() => {
669 return undefined
670 })
671 .catch(err => {
672 // Auto destruction...
673 this.destroy().catch(err => logger.error('Cannot destruct video after transcoding failure.', err))
674 659
675 throw err 660 // Important to do this before getVideoFilename() to take in account the new file extension
676 }) 661 inputVideoFile.set('extname', newExtname)
662
663 await renamePromise(videoOutputPath, this.getVideoFilePath(inputVideoFile))
664 const stats = await statPromise(this.getVideoFilePath(inputVideoFile))
665
666 inputVideoFile.set('size', stats.size)
667
668 await this.createTorrentAndSetInfoHash(inputVideoFile)
669 await inputVideoFile.save()
670
671 } catch (err) {
672 // Auto destruction...
673 this.destroy().catch(err => logger.error('Cannot destruct video after transcoding failure.', err))
674
675 throw err
676 }
677} 677}
678 678
679transcodeOriginalVideofile = function (this: VideoInstance, resolution: VideoResolution) { 679transcodeOriginalVideofile = async function (this: VideoInstance, resolution: VideoResolution) {
680 const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR 680 const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR
681 const extname = '.mp4' 681 const extname = '.mp4'
682 682
@@ -696,25 +696,18 @@ transcodeOriginalVideofile = function (this: VideoInstance, resolution: VideoRes
696 outputPath: videoOutputPath, 696 outputPath: videoOutputPath,
697 resolution 697 resolution
698 } 698 }
699 return transcode(transcodeOptions)
700 .then(() => {
701 return statPromise(videoOutputPath)
702 })
703 .then(stats => {
704 newVideoFile.set('size', stats.size)
705 699
706 return undefined 700 await transcode(transcodeOptions)
707 }) 701
708 .then(() => { 702 const stats = await statPromise(videoOutputPath)
709 return this.createTorrentAndSetInfoHash(newVideoFile) 703
710 }) 704 newVideoFile.set('size', stats.size)
711 .then(() => { 705
712 return newVideoFile.save() 706 await this.createTorrentAndSetInfoHash(newVideoFile)
713 }) 707
714 .then(() => { 708 await newVideoFile.save()
715 return this.VideoFiles.push(newVideoFile) 709
716 }) 710 this.VideoFiles.push(newVideoFile)
717 .then(() => undefined)
718} 711}
719 712
720getOriginalFileHeight = function (this: VideoInstance) { 713getOriginalFileHeight = function (this: VideoInstance) {
@@ -727,6 +720,31 @@ getDescriptionPath = function (this: VideoInstance) {
727 return `/api/${API_VERSION}/videos/${this.uuid}/description` 720 return `/api/${API_VERSION}/videos/${this.uuid}/description`
728} 721}
729 722
723getCategoryLabel = function (this: VideoInstance) {
724 let categoryLabel = VIDEO_CATEGORIES[this.category]
725
726 // Maybe our pod is not up to date and there are new categories since our version
727 if (!categoryLabel) categoryLabel = 'Misc'
728
729 return categoryLabel
730}
731
732getLicenceLabel = function (this: VideoInstance) {
733 let licenceLabel = VIDEO_LICENCES[this.licence]
734 // Maybe our pod is not up to date and there are new licences since our version
735 if (!licenceLabel) licenceLabel = 'Unknown'
736
737 return licenceLabel
738}
739
740getLanguageLabel = function (this: VideoInstance) {
741 // Language is an optional attribute
742 let languageLabel = VIDEO_LANGUAGES[this.language]
743 if (!languageLabel) languageLabel = 'Unknown'
744
745 return languageLabel
746}
747
730removeThumbnail = function (this: VideoInstance) { 748removeThumbnail = function (this: VideoInstance) {
731 const thumbnailPath = join(CONFIG.STORAGE.THUMBNAILS_DIR, this.getThumbnailName()) 749 const thumbnailPath = join(CONFIG.STORAGE.THUMBNAILS_DIR, this.getThumbnailName())
732 return unlinkPromise(thumbnailPath) 750 return unlinkPromise(thumbnailPath)
@@ -779,7 +797,7 @@ listUserVideosForApi = function (userId: number, start: number, count: number, s
779 required: true, 797 required: true,
780 include: [ 798 include: [
781 { 799 {
782 model: Video['sequelize'].models.Author, 800 model: Video['sequelize'].models.Account,
783 where: { 801 where: {
784 userId 802 userId
785 }, 803 },
@@ -810,7 +828,7 @@ listForApi = function (start: number, count: number, sort: string) {
810 model: Video['sequelize'].models.VideoChannel, 828 model: Video['sequelize'].models.VideoChannel,
811 include: [ 829 include: [
812 { 830 {
813 model: Video['sequelize'].models.Author, 831 model: Video['sequelize'].models.Account,
814 include: [ 832 include: [
815 { 833 {
816 model: Video['sequelize'].models.Pod, 834 model: Video['sequelize'].models.Pod,
@@ -846,7 +864,7 @@ loadByHostAndUUID = function (fromHost: string, uuid: string, t?: Sequelize.Tran
846 model: Video['sequelize'].models.VideoChannel, 864 model: Video['sequelize'].models.VideoChannel,
847 include: [ 865 include: [
848 { 866 {
849 model: Video['sequelize'].models.Author, 867 model: Video['sequelize'].models.Account,
850 include: [ 868 include: [
851 { 869 {
852 model: Video['sequelize'].models.Pod, 870 model: Video['sequelize'].models.Pod,
@@ -867,7 +885,7 @@ loadByHostAndUUID = function (fromHost: string, uuid: string, t?: Sequelize.Tran
867 return Video.findOne(query) 885 return Video.findOne(query)
868} 886}
869 887
870listOwnedAndPopulateAuthorAndTags = function () { 888listOwnedAndPopulateAccountAndTags = function () {
871 const query = { 889 const query = {
872 where: { 890 where: {
873 remote: false 891 remote: false
@@ -876,7 +894,7 @@ listOwnedAndPopulateAuthorAndTags = function () {
876 Video['sequelize'].models.VideoFile, 894 Video['sequelize'].models.VideoFile,
877 { 895 {
878 model: Video['sequelize'].models.VideoChannel, 896 model: Video['sequelize'].models.VideoChannel,
879 include: [ Video['sequelize'].models.Author ] 897 include: [ Video['sequelize'].models.Account ]
880 }, 898 },
881 Video['sequelize'].models.Tag 899 Video['sequelize'].models.Tag
882 ] 900 ]
@@ -885,7 +903,7 @@ listOwnedAndPopulateAuthorAndTags = function () {
885 return Video.findAll(query) 903 return Video.findAll(query)
886} 904}
887 905
888listOwnedByAuthor = function (author: string) { 906listOwnedByAccount = function (account: string) {
889 const query = { 907 const query = {
890 where: { 908 where: {
891 remote: false 909 remote: false
@@ -898,9 +916,9 @@ listOwnedByAuthor = function (author: string) {
898 model: Video['sequelize'].models.VideoChannel, 916 model: Video['sequelize'].models.VideoChannel,
899 include: [ 917 include: [
900 { 918 {
901 model: Video['sequelize'].models.Author, 919 model: Video['sequelize'].models.Account,
902 where: { 920 where: {
903 name: author 921 name: account
904 } 922 }
905 } 923 }
906 ] 924 ]
@@ -942,13 +960,13 @@ loadLocalVideoByUUID = function (uuid: string, t?: Sequelize.Transaction) {
942 return Video.findOne(query) 960 return Video.findOne(query)
943} 961}
944 962
945loadAndPopulateAuthor = function (id: number) { 963loadAndPopulateAccount = function (id: number) {
946 const options = { 964 const options = {
947 include: [ 965 include: [
948 Video['sequelize'].models.VideoFile, 966 Video['sequelize'].models.VideoFile,
949 { 967 {
950 model: Video['sequelize'].models.VideoChannel, 968 model: Video['sequelize'].models.VideoChannel,
951 include: [ Video['sequelize'].models.Author ] 969 include: [ Video['sequelize'].models.Account ]
952 } 970 }
953 ] 971 ]
954 } 972 }
@@ -956,14 +974,14 @@ loadAndPopulateAuthor = function (id: number) {
956 return Video.findById(id, options) 974 return Video.findById(id, options)
957} 975}
958 976
959loadAndPopulateAuthorAndPodAndTags = function (id: number) { 977loadAndPopulateAccountAndPodAndTags = function (id: number) {
960 const options = { 978 const options = {
961 include: [ 979 include: [
962 { 980 {
963 model: Video['sequelize'].models.VideoChannel, 981 model: Video['sequelize'].models.VideoChannel,
964 include: [ 982 include: [
965 { 983 {
966 model: Video['sequelize'].models.Author, 984 model: Video['sequelize'].models.Account,
967 include: [ { model: Video['sequelize'].models.Pod, required: false } ] 985 include: [ { model: Video['sequelize'].models.Pod, required: false } ]
968 } 986 }
969 ] 987 ]
@@ -976,7 +994,7 @@ loadAndPopulateAuthorAndPodAndTags = function (id: number) {
976 return Video.findById(id, options) 994 return Video.findById(id, options)
977} 995}
978 996
979loadByUUIDAndPopulateAuthorAndPodAndTags = function (uuid: string) { 997loadByUUIDAndPopulateAccountAndPodAndTags = function (uuid: string) {
980 const options = { 998 const options = {
981 where: { 999 where: {
982 uuid 1000 uuid
@@ -986,7 +1004,7 @@ loadByUUIDAndPopulateAuthorAndPodAndTags = function (uuid: string) {
986 model: Video['sequelize'].models.VideoChannel, 1004 model: Video['sequelize'].models.VideoChannel,
987 include: [ 1005 include: [
988 { 1006 {
989 model: Video['sequelize'].models.Author, 1007 model: Video['sequelize'].models.Account,
990 include: [ { model: Video['sequelize'].models.Pod, required: false } ] 1008 include: [ { model: Video['sequelize'].models.Pod, required: false } ]
991 } 1009 }
992 ] 1010 ]
@@ -999,20 +1017,20 @@ loadByUUIDAndPopulateAuthorAndPodAndTags = function (uuid: string) {
999 return Video.findOne(options) 1017 return Video.findOne(options)
1000} 1018}
1001 1019
1002searchAndPopulateAuthorAndPodAndTags = function (value: string, field: string, start: number, count: number, sort: string) { 1020searchAndPopulateAccountAndPodAndTags = function (value: string, field: string, start: number, count: number, sort: string) {
1003 const podInclude: Sequelize.IncludeOptions = { 1021 const podInclude: Sequelize.IncludeOptions = {
1004 model: Video['sequelize'].models.Pod, 1022 model: Video['sequelize'].models.Pod,
1005 required: false 1023 required: false
1006 } 1024 }
1007 1025
1008 const authorInclude: Sequelize.IncludeOptions = { 1026 const accountInclude: Sequelize.IncludeOptions = {
1009 model: Video['sequelize'].models.Author, 1027 model: Video['sequelize'].models.Account,
1010 include: [ podInclude ] 1028 include: [ podInclude ]
1011 } 1029 }
1012 1030
1013 const videoChannelInclude: Sequelize.IncludeOptions = { 1031 const videoChannelInclude: Sequelize.IncludeOptions = {
1014 model: Video['sequelize'].models.VideoChannel, 1032 model: Video['sequelize'].models.VideoChannel,
1015 include: [ authorInclude ], 1033 include: [ accountInclude ],
1016 required: true 1034 required: true
1017 } 1035 }
1018 1036
@@ -1045,8 +1063,8 @@ searchAndPopulateAuthorAndPodAndTags = function (value: string, field: string, s
1045 } 1063 }
1046 } 1064 }
1047 podInclude.required = true 1065 podInclude.required = true
1048 } else if (field === 'author') { 1066 } else if (field === 'account') {
1049 authorInclude.where = { 1067 accountInclude.where = {
1050 name: { 1068 name: {
1051 [Sequelize.Op.iLike]: '%' + value + '%' 1069 [Sequelize.Op.iLike]: '%' + value + '%'
1052 } 1070 }
@@ -1090,13 +1108,17 @@ function getBaseUrls (video: VideoInstance) {
1090 baseUrlHttp = CONFIG.WEBSERVER.URL 1108 baseUrlHttp = CONFIG.WEBSERVER.URL
1091 baseUrlWs = CONFIG.WEBSERVER.WS + '://' + CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT 1109 baseUrlWs = CONFIG.WEBSERVER.WS + '://' + CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT
1092 } else { 1110 } else {
1093 baseUrlHttp = REMOTE_SCHEME.HTTP + '://' + video.VideoChannel.Author.Pod.host 1111 baseUrlHttp = REMOTE_SCHEME.HTTP + '://' + video.VideoChannel.Account.Pod.host
1094 baseUrlWs = REMOTE_SCHEME.WS + '://' + video.VideoChannel.Author.Pod.host 1112 baseUrlWs = REMOTE_SCHEME.WS + '://' + video.VideoChannel.Account.Pod.host
1095 } 1113 }
1096 1114
1097 return { baseUrlHttp, baseUrlWs } 1115 return { baseUrlHttp, baseUrlWs }
1098} 1116}
1099 1117
1118function getThumbnailUrl (video: VideoInstance, baseUrlHttp: string) {
1119 return baseUrlHttp + STATIC_PATHS.THUMBNAILS + video.getThumbnailName()
1120}
1121
1100function getTorrentUrl (video: VideoInstance, videoFile: VideoFileInstance, baseUrlHttp: string) { 1122function getTorrentUrl (video: VideoInstance, videoFile: VideoFileInstance, baseUrlHttp: string) {
1101 return baseUrlHttp + STATIC_PATHS.TORRENTS + video.getTorrentFileName(videoFile) 1123 return baseUrlHttp + STATIC_PATHS.TORRENTS + video.getTorrentFileName(videoFile)
1102} 1124}