aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/models/video
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2019-08-22 10:43:11 +0200
committerChocobozzz <me@florianbigard.com>2019-08-22 10:43:11 +0200
commit5c5e587307a27e173333789b5b5167d35f468b01 (patch)
tree94e3721caf2e11d38fd5f4112c0fc98da89ac535 /server/models/video
parent1b42d73f44811eec1b7ddd72dd0d640a57c3376c (diff)
parentb5fecbf44192144d1ca27c23a0b53922de288c10 (diff)
downloadPeerTube-5c5e587307a27e173333789b5b5167d35f468b01.tar.gz
PeerTube-5c5e587307a27e173333789b5b5167d35f468b01.tar.zst
PeerTube-5c5e587307a27e173333789b5b5167d35f468b01.zip
Merge branch 'feature/strong-model-types' into develop
Diffstat (limited to 'server/models/video')
-rw-r--r--server/models/video/schedule-video-update.ts3
-rw-r--r--server/models/video/tag.ts9
-rw-r--r--server/models/video/video-abuse.ts8
-rw-r--r--server/models/video/video-blacklist.ts8
-rw-r--r--server/models/video/video-caption.ts16
-rw-r--r--server/models/video/video-change-ownership.ts8
-rw-r--r--server/models/video/video-channel.ts67
-rw-r--r--server/models/video/video-comment.ts87
-rw-r--r--server/models/video/video-file.ts3
-rw-r--r--server/models/video/video-format-utils.ts17
-rw-r--r--server/models/video/video-import.ts14
-rw-r--r--server/models/video/video-playlist-element.ts34
-rw-r--r--server/models/video/video-playlist.ts23
-rw-r--r--server/models/video/video-share.ts14
-rw-r--r--server/models/video/video-streaming-playlist.ts12
-rw-r--r--server/models/video/video.ts125
16 files changed, 242 insertions, 206 deletions
diff --git a/server/models/video/schedule-video-update.ts b/server/models/video/schedule-video-update.ts
index 603d55692..fc2a424aa 100644
--- a/server/models/video/schedule-video-update.ts
+++ b/server/models/video/schedule-video-update.ts
@@ -2,6 +2,7 @@ import { AllowNull, BelongsTo, Column, CreatedAt, Default, ForeignKey, Model, Ta
2import { ScopeNames as VideoScopeNames, VideoModel } from './video' 2import { ScopeNames as VideoScopeNames, VideoModel } from './video'
3import { VideoPrivacy } from '../../../shared/models/videos' 3import { VideoPrivacy } from '../../../shared/models/videos'
4import { Op, Transaction } from 'sequelize' 4import { Op, Transaction } from 'sequelize'
5import { MScheduleVideoUpdateFormattable } from '@server/typings/models'
5 6
6@Table({ 7@Table({
7 tableName: 'scheduleVideoUpdate', 8 tableName: 'scheduleVideoUpdate',
@@ -96,7 +97,7 @@ export class ScheduleVideoUpdateModel extends Model<ScheduleVideoUpdateModel> {
96 return ScheduleVideoUpdateModel.destroy(query) 97 return ScheduleVideoUpdateModel.destroy(query)
97 } 98 }
98 99
99 toFormattedJSON () { 100 toFormattedJSON (this: MScheduleVideoUpdateFormattable) {
100 return { 101 return {
101 updateAt: this.updateAt, 102 updateAt: this.updateAt,
102 privacy: this.privacy || undefined 103 privacy: this.privacy || undefined
diff --git a/server/models/video/tag.ts b/server/models/video/tag.ts
index 0fc3cfd4c..b110f2a43 100644
--- a/server/models/video/tag.ts
+++ b/server/models/video/tag.ts
@@ -6,6 +6,7 @@ import { throwIfNotValid } from '../utils'
6import { VideoModel } from './video' 6import { VideoModel } from './video'
7import { VideoTagModel } from './video-tag' 7import { VideoTagModel } from './video-tag'
8import { VideoPrivacy, VideoState } from '../../../shared/models/videos' 8import { VideoPrivacy, VideoState } from '../../../shared/models/videos'
9import { MTag } from '@server/typings/models'
9 10
10@Table({ 11@Table({
11 tableName: 'tag', 12 tableName: 'tag',
@@ -37,10 +38,10 @@ export class TagModel extends Model<TagModel> {
37 }) 38 })
38 Videos: VideoModel[] 39 Videos: VideoModel[]
39 40
40 static findOrCreateTags (tags: string[], transaction: Transaction) { 41 static findOrCreateTags (tags: string[], transaction: Transaction): Promise<MTag[]> {
41 if (tags === null) return [] 42 if (tags === null) return Promise.resolve([])
42 43
43 const tasks: Bluebird<TagModel>[] = [] 44 const tasks: Bluebird<MTag>[] = []
44 tags.forEach(tag => { 45 tags.forEach(tag => {
45 const query = { 46 const query = {
46 where: { 47 where: {
@@ -52,7 +53,7 @@ export class TagModel extends Model<TagModel> {
52 transaction 53 transaction
53 } 54 }
54 55
55 const promise = TagModel.findOrCreate(query) 56 const promise = TagModel.findOrCreate<MTag>(query)
56 .then(([ tagInstance ]) => tagInstance) 57 .then(([ tagInstance ]) => tagInstance)
57 tasks.push(promise) 58 tasks.push(promise)
58 }) 59 })
diff --git a/server/models/video/video-abuse.ts b/server/models/video/video-abuse.ts
index 1ac7919b3..6ef1a915d 100644
--- a/server/models/video/video-abuse.ts
+++ b/server/models/video/video-abuse.ts
@@ -11,6 +11,8 @@ import { getSort, throwIfNotValid } from '../utils'
11import { VideoModel } from './video' 11import { VideoModel } from './video'
12import { VideoAbuseState } from '../../../shared' 12import { VideoAbuseState } from '../../../shared'
13import { CONSTRAINTS_FIELDS, VIDEO_ABUSE_STATES } from '../../initializers/constants' 13import { CONSTRAINTS_FIELDS, VIDEO_ABUSE_STATES } from '../../initializers/constants'
14import { MVideoAbuse, MVideoAbuseFormattable, MVideoAbuseVideo } from '../../typings/models'
15import * as Bluebird from 'bluebird'
14 16
15@Table({ 17@Table({
16 tableName: 'videoAbuse', 18 tableName: 'videoAbuse',
@@ -73,7 +75,7 @@ export class VideoAbuseModel extends Model<VideoAbuseModel> {
73 }) 75 })
74 Video: VideoModel 76 Video: VideoModel
75 77
76 static loadByIdAndVideoId (id: number, videoId: number) { 78 static loadByIdAndVideoId (id: number, videoId: number): Bluebird<MVideoAbuse> {
77 const query = { 79 const query = {
78 where: { 80 where: {
79 id, 81 id,
@@ -106,7 +108,7 @@ export class VideoAbuseModel extends Model<VideoAbuseModel> {
106 }) 108 })
107 } 109 }
108 110
109 toFormattedJSON (): VideoAbuse { 111 toFormattedJSON (this: MVideoAbuseFormattable): VideoAbuse {
110 return { 112 return {
111 id: this.id, 113 id: this.id,
112 reason: this.reason, 114 reason: this.reason,
@@ -125,7 +127,7 @@ export class VideoAbuseModel extends Model<VideoAbuseModel> {
125 } 127 }
126 } 128 }
127 129
128 toActivityPubObject (): VideoAbuseObject { 130 toActivityPubObject (this: MVideoAbuseVideo): VideoAbuseObject {
129 return { 131 return {
130 type: 'Flag' as 'Flag', 132 type: 'Flag' as 'Flag',
131 content: this.reason, 133 content: this.reason,
diff --git a/server/models/video/video-blacklist.ts b/server/models/video/video-blacklist.ts
index 22d949da0..b4df6cd6a 100644
--- a/server/models/video/video-blacklist.ts
+++ b/server/models/video/video-blacklist.ts
@@ -1,12 +1,14 @@
1import { AllowNull, BelongsTo, Column, CreatedAt, DataType, Default, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' 1import { AllowNull, BelongsTo, Column, CreatedAt, DataType, Default, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
2import { getSortOnModel, SortType, throwIfNotValid } from '../utils' 2import { getSortOnModel, SortType, throwIfNotValid } from '../utils'
3import { ScopeNames as VideoModelScopeNames, VideoModel } from './video' 3import { VideoModel } from './video'
4import { ScopeNames as VideoChannelScopeNames, SummaryOptions, VideoChannelModel } from './video-channel' 4import { ScopeNames as VideoChannelScopeNames, SummaryOptions, VideoChannelModel } from './video-channel'
5import { isVideoBlacklistReasonValid, isVideoBlacklistTypeValid } from '../../helpers/custom-validators/video-blacklist' 5import { isVideoBlacklistReasonValid, isVideoBlacklistTypeValid } from '../../helpers/custom-validators/video-blacklist'
6import { VideoBlacklist, VideoBlacklistType } from '../../../shared/models/videos' 6import { VideoBlacklist, VideoBlacklistType } from '../../../shared/models/videos'
7import { CONSTRAINTS_FIELDS } from '../../initializers/constants' 7import { CONSTRAINTS_FIELDS } from '../../initializers/constants'
8import { FindOptions } from 'sequelize' 8import { FindOptions } from 'sequelize'
9import { ThumbnailModel } from './thumbnail' 9import { ThumbnailModel } from './thumbnail'
10import * as Bluebird from 'bluebird'
11import { MVideoBlacklist, MVideoBlacklistFormattable } from '@server/typings/models'
10 12
11@Table({ 13@Table({
12 tableName: 'videoBlacklist', 14 tableName: 'videoBlacklist',
@@ -99,7 +101,7 @@ export class VideoBlacklistModel extends Model<VideoBlacklistModel> {
99 }) 101 })
100 } 102 }
101 103
102 static loadByVideoId (id: number) { 104 static loadByVideoId (id: number): Bluebird<MVideoBlacklist> {
103 const query = { 105 const query = {
104 where: { 106 where: {
105 videoId: id 107 videoId: id
@@ -109,7 +111,7 @@ export class VideoBlacklistModel extends Model<VideoBlacklistModel> {
109 return VideoBlacklistModel.findOne(query) 111 return VideoBlacklistModel.findOne(query)
110 } 112 }
111 113
112 toFormattedJSON (): VideoBlacklist { 114 toFormattedJSON (this: MVideoBlacklistFormattable): VideoBlacklist {
113 return { 115 return {
114 id: this.id, 116 id: this.id,
115 createdAt: this.createdAt, 117 createdAt: this.createdAt,
diff --git a/server/models/video/video-caption.ts b/server/models/video/video-caption.ts
index a01565851..ad5801768 100644
--- a/server/models/video/video-caption.ts
+++ b/server/models/video/video-caption.ts
@@ -21,6 +21,8 @@ import { join } from 'path'
21import { logger } from '../../helpers/logger' 21import { logger } from '../../helpers/logger'
22import { remove } from 'fs-extra' 22import { remove } from 'fs-extra'
23import { CONFIG } from '../../initializers/config' 23import { CONFIG } from '../../initializers/config'
24import * as Bluebird from 'bluebird'
25import { MVideoCaptionFormattable, MVideoCaptionVideo } from '@server/typings/models'
24 26
25export enum ScopeNames { 27export enum ScopeNames {
26 WITH_VIDEO_UUID_AND_REMOTE = 'WITH_VIDEO_UUID_AND_REMOTE' 28 WITH_VIDEO_UUID_AND_REMOTE = 'WITH_VIDEO_UUID_AND_REMOTE'
@@ -30,7 +32,7 @@ export enum ScopeNames {
30 [ScopeNames.WITH_VIDEO_UUID_AND_REMOTE]: { 32 [ScopeNames.WITH_VIDEO_UUID_AND_REMOTE]: {
31 include: [ 33 include: [
32 { 34 {
33 attributes: [ 'uuid', 'remote' ], 35 attributes: [ 'id', 'uuid', 'remote' ],
34 model: VideoModel.unscoped(), 36 model: VideoModel.unscoped(),
35 required: true 37 required: true
36 } 38 }
@@ -93,7 +95,7 @@ export class VideoCaptionModel extends Model<VideoCaptionModel> {
93 return undefined 95 return undefined
94 } 96 }
95 97
96 static loadByVideoIdAndLanguage (videoId: string | number, language: string) { 98 static loadByVideoIdAndLanguage (videoId: string | number, language: string): Bluebird<MVideoCaptionVideo> {
97 const videoInclude = { 99 const videoInclude = {
98 model: VideoModel.unscoped(), 100 model: VideoModel.unscoped(),
99 attributes: [ 'id', 'remote', 'uuid' ], 101 attributes: [ 'id', 'remote', 'uuid' ],
@@ -122,7 +124,7 @@ export class VideoCaptionModel extends Model<VideoCaptionModel> {
122 .then(([ caption ]) => caption) 124 .then(([ caption ]) => caption)
123 } 125 }
124 126
125 static listVideoCaptions (videoId: number) { 127 static listVideoCaptions (videoId: number): Bluebird<MVideoCaptionVideo[]> {
126 const query = { 128 const query = {
127 order: [ [ 'language', 'ASC' ] ] as OrderItem[], 129 order: [ [ 'language', 'ASC' ] ] as OrderItem[],
128 where: { 130 where: {
@@ -152,7 +154,7 @@ export class VideoCaptionModel extends Model<VideoCaptionModel> {
152 return this.Video.remote === false 154 return this.Video.remote === false
153 } 155 }
154 156
155 toFormattedJSON (): VideoCaption { 157 toFormattedJSON (this: MVideoCaptionFormattable): VideoCaption {
156 return { 158 return {
157 language: { 159 language: {
158 id: this.language, 160 id: this.language,
@@ -162,15 +164,15 @@ export class VideoCaptionModel extends Model<VideoCaptionModel> {
162 } 164 }
163 } 165 }
164 166
165 getCaptionStaticPath () { 167 getCaptionStaticPath (this: MVideoCaptionFormattable) {
166 return join(LAZY_STATIC_PATHS.VIDEO_CAPTIONS, this.getCaptionName()) 168 return join(LAZY_STATIC_PATHS.VIDEO_CAPTIONS, this.getCaptionName())
167 } 169 }
168 170
169 getCaptionName () { 171 getCaptionName (this: MVideoCaptionFormattable) {
170 return `${this.Video.uuid}-${this.language}.vtt` 172 return `${this.Video.uuid}-${this.language}.vtt`
171 } 173 }
172 174
173 removeCaptionFile () { 175 removeCaptionFile (this: MVideoCaptionFormattable) {
174 return remove(CONFIG.STORAGE.CAPTIONS_DIR + this.getCaptionName()) 176 return remove(CONFIG.STORAGE.CAPTIONS_DIR + this.getCaptionName())
175 } 177 }
176} 178}
diff --git a/server/models/video/video-change-ownership.ts b/server/models/video/video-change-ownership.ts
index b545a2f8c..f7a351329 100644
--- a/server/models/video/video-change-ownership.ts
+++ b/server/models/video/video-change-ownership.ts
@@ -3,6 +3,8 @@ import { AccountModel } from '../account/account'
3import { ScopeNames as VideoScopeNames, VideoModel } from './video' 3import { ScopeNames as VideoScopeNames, VideoModel } from './video'
4import { VideoChangeOwnership, VideoChangeOwnershipStatus } from '../../../shared/models/videos' 4import { VideoChangeOwnership, VideoChangeOwnershipStatus } from '../../../shared/models/videos'
5import { getSort } from '../utils' 5import { getSort } from '../utils'
6import { MVideoChangeOwnershipFormattable, MVideoChangeOwnershipFull } from '@server/typings/models/video/video-change-ownership'
7import * as Bluebird from 'bluebird'
6 8
7enum ScopeNames { 9enum ScopeNames {
8 WITH_ACCOUNTS = 'WITH_ACCOUNTS', 10 WITH_ACCOUNTS = 'WITH_ACCOUNTS',
@@ -108,16 +110,16 @@ export class VideoChangeOwnershipModel extends Model<VideoChangeOwnershipModel>
108 110
109 return Promise.all([ 111 return Promise.all([
110 VideoChangeOwnershipModel.scope(ScopeNames.WITH_ACCOUNTS).count(query), 112 VideoChangeOwnershipModel.scope(ScopeNames.WITH_ACCOUNTS).count(query),
111 VideoChangeOwnershipModel.scope([ ScopeNames.WITH_ACCOUNTS, ScopeNames.WITH_VIDEO ]).findAll(query) 113 VideoChangeOwnershipModel.scope([ ScopeNames.WITH_ACCOUNTS, ScopeNames.WITH_VIDEO ]).findAll<MVideoChangeOwnershipFull>(query)
112 ]).then(([ count, rows ]) => ({ total: count, data: rows })) 114 ]).then(([ count, rows ]) => ({ total: count, data: rows }))
113 } 115 }
114 116
115 static load (id: number) { 117 static load (id: number): Bluebird<MVideoChangeOwnershipFull> {
116 return VideoChangeOwnershipModel.scope([ ScopeNames.WITH_ACCOUNTS, ScopeNames.WITH_VIDEO ]) 118 return VideoChangeOwnershipModel.scope([ ScopeNames.WITH_ACCOUNTS, ScopeNames.WITH_VIDEO ])
117 .findByPk(id) 119 .findByPk(id)
118 } 120 }
119 121
120 toFormattedJSON (): VideoChangeOwnership { 122 toFormattedJSON (this: MVideoChangeOwnershipFormattable): VideoChangeOwnership {
121 return { 123 return {
122 id: this.id, 124 id: this.id,
123 status: this.status, 125 status: this.status,
diff --git a/server/models/video/video-channel.ts b/server/models/video/video-channel.ts
index 6241a75a3..7178631b4 100644
--- a/server/models/video/video-channel.ts
+++ b/server/models/video/video-channel.ts
@@ -33,6 +33,15 @@ import { ServerModel } from '../server/server'
33import { FindOptions, ModelIndexesOptions, Op } from 'sequelize' 33import { FindOptions, ModelIndexesOptions, Op } from 'sequelize'
34import { AvatarModel } from '../avatar/avatar' 34import { AvatarModel } from '../avatar/avatar'
35import { VideoPlaylistModel } from './video-playlist' 35import { VideoPlaylistModel } from './video-playlist'
36import * as Bluebird from 'bluebird'
37import {
38 MChannelAccountDefault,
39 MChannelActor,
40 MChannelActorAccountDefaultVideos,
41 MChannelAP,
42 MChannelFormattable,
43 MChannelSummaryFormattable
44} from '../../typings/models/video'
36 45
37// FIXME: Define indexes here because there is an issue with TS and Sequelize.literal when called directly in the annotation 46// FIXME: Define indexes here because there is an issue with TS and Sequelize.literal when called directly in the annotation
38const indexes: ModelIndexesOptions[] = [ 47const indexes: ModelIndexesOptions[] = [
@@ -47,7 +56,7 @@ const indexes: ModelIndexesOptions[] = [
47] 56]
48 57
49export enum ScopeNames { 58export enum ScopeNames {
50 AVAILABLE_FOR_LIST = 'AVAILABLE_FOR_LIST', 59 FOR_API = 'FOR_API',
51 WITH_ACCOUNT = 'WITH_ACCOUNT', 60 WITH_ACCOUNT = 'WITH_ACCOUNT',
52 WITH_ACTOR = 'WITH_ACTOR', 61 WITH_ACTOR = 'WITH_ACTOR',
53 WITH_VIDEOS = 'WITH_VIDEOS', 62 WITH_VIDEOS = 'WITH_VIDEOS',
@@ -74,10 +83,10 @@ export type SummaryOptions = {
74@Scopes(() => ({ 83@Scopes(() => ({
75 [ScopeNames.SUMMARY]: (options: SummaryOptions = {}) => { 84 [ScopeNames.SUMMARY]: (options: SummaryOptions = {}) => {
76 const base: FindOptions = { 85 const base: FindOptions = {
77 attributes: [ 'name', 'description', 'id', 'actorId' ], 86 attributes: [ 'id', 'name', 'description', 'actorId' ],
78 include: [ 87 include: [
79 { 88 {
80 attributes: [ 'preferredUsername', 'url', 'serverId', 'avatarId' ], 89 attributes: [ 'id', 'preferredUsername', 'url', 'serverId', 'avatarId' ],
81 model: ActorModel.unscoped(), 90 model: ActorModel.unscoped(),
82 required: true, 91 required: true,
83 include: [ 92 include: [
@@ -106,7 +115,7 @@ export type SummaryOptions = {
106 115
107 return base 116 return base
108 }, 117 },
109 [ScopeNames.AVAILABLE_FOR_LIST]: (options: AvailableForListOptions) => { 118 [ScopeNames.FOR_API]: (options: AvailableForListOptions) => {
110 // Only list local channels OR channels that are on an instance followed by actorId 119 // Only list local channels OR channels that are on an instance followed by actorId
111 const inQueryInstanceFollow = buildServerIdsFollowedBy(options.actorId) 120 const inQueryInstanceFollow = buildServerIdsFollowedBy(options.actorId)
112 121
@@ -268,7 +277,7 @@ export class VideoChannelModel extends Model<VideoChannelModel> {
268 } 277 }
269 278
270 const scopes = { 279 const scopes = {
271 method: [ ScopeNames.AVAILABLE_FOR_LIST, { actorId } as AvailableForListOptions ] 280 method: [ ScopeNames.FOR_API, { actorId } as AvailableForListOptions ]
272 } 281 }
273 return VideoChannelModel 282 return VideoChannelModel
274 .scope(scopes) 283 .scope(scopes)
@@ -278,7 +287,7 @@ export class VideoChannelModel extends Model<VideoChannelModel> {
278 }) 287 })
279 } 288 }
280 289
281 static listLocalsForSitemap (sort: string) { 290 static listLocalsForSitemap (sort: string): Bluebird<MChannelActor[]> {
282 const query = { 291 const query = {
283 attributes: [ ], 292 attributes: [ ],
284 offset: 0, 293 offset: 0,
@@ -331,7 +340,7 @@ export class VideoChannelModel extends Model<VideoChannelModel> {
331 } 340 }
332 341
333 const scopes = { 342 const scopes = {
334 method: [ ScopeNames.AVAILABLE_FOR_LIST, { actorId: options.actorId } as AvailableForListOptions ] 343 method: [ ScopeNames.FOR_API, { actorId: options.actorId } as AvailableForListOptions ]
335 } 344 }
336 return VideoChannelModel 345 return VideoChannelModel
337 .scope(scopes) 346 .scope(scopes)
@@ -369,13 +378,13 @@ export class VideoChannelModel extends Model<VideoChannelModel> {
369 }) 378 })
370 } 379 }
371 380
372 static loadByIdAndPopulateAccount (id: number) { 381 static loadByIdAndPopulateAccount (id: number): Bluebird<MChannelAccountDefault> {
373 return VideoChannelModel.unscoped() 382 return VideoChannelModel.unscoped()
374 .scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT ]) 383 .scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT ])
375 .findByPk(id) 384 .findByPk(id)
376 } 385 }
377 386
378 static loadByIdAndAccount (id: number, accountId: number) { 387 static loadByIdAndAccount (id: number, accountId: number): Bluebird<MChannelAccountDefault> {
379 const query = { 388 const query = {
380 where: { 389 where: {
381 id, 390 id,
@@ -388,13 +397,13 @@ export class VideoChannelModel extends Model<VideoChannelModel> {
388 .findOne(query) 397 .findOne(query)
389 } 398 }
390 399
391 static loadAndPopulateAccount (id: number) { 400 static loadAndPopulateAccount (id: number): Bluebird<MChannelAccountDefault> {
392 return VideoChannelModel.unscoped() 401 return VideoChannelModel.unscoped()
393 .scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT ]) 402 .scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT ])
394 .findByPk(id) 403 .findByPk(id)
395 } 404 }
396 405
397 static loadByUrlAndPopulateAccount (url: string) { 406 static loadByUrlAndPopulateAccount (url: string): Bluebird<MChannelAccountDefault> {
398 const query = { 407 const query = {
399 include: [ 408 include: [
400 { 409 {
@@ -420,7 +429,7 @@ export class VideoChannelModel extends Model<VideoChannelModel> {
420 return VideoChannelModel.loadByNameAndHostAndPopulateAccount(name, host) 429 return VideoChannelModel.loadByNameAndHostAndPopulateAccount(name, host)
421 } 430 }
422 431
423 static loadLocalByNameAndPopulateAccount (name: string) { 432 static loadLocalByNameAndPopulateAccount (name: string): Bluebird<MChannelAccountDefault> {
424 const query = { 433 const query = {
425 include: [ 434 include: [
426 { 435 {
@@ -439,7 +448,7 @@ export class VideoChannelModel extends Model<VideoChannelModel> {
439 .findOne(query) 448 .findOne(query)
440 } 449 }
441 450
442 static loadByNameAndHostAndPopulateAccount (name: string, host: string) { 451 static loadByNameAndHostAndPopulateAccount (name: string, host: string): Bluebird<MChannelAccountDefault> {
443 const query = { 452 const query = {
444 include: [ 453 include: [
445 { 454 {
@@ -464,7 +473,7 @@ export class VideoChannelModel extends Model<VideoChannelModel> {
464 .findOne(query) 473 .findOne(query)
465 } 474 }
466 475
467 static loadAndPopulateAccountAndVideos (id: number) { 476 static loadAndPopulateAccountAndVideos (id: number): Bluebird<MChannelActorAccountDefaultVideos> {
468 const options = { 477 const options = {
469 include: [ 478 include: [
470 VideoModel 479 VideoModel
@@ -476,7 +485,20 @@ export class VideoChannelModel extends Model<VideoChannelModel> {
476 .findByPk(id, options) 485 .findByPk(id, options)
477 } 486 }
478 487
479 toFormattedJSON (): VideoChannel { 488 toFormattedSummaryJSON (this: MChannelSummaryFormattable): VideoChannelSummary {
489 const actor = this.Actor.toFormattedSummaryJSON()
490
491 return {
492 id: this.id,
493 name: actor.name,
494 displayName: this.getDisplayName(),
495 url: actor.url,
496 host: actor.host,
497 avatar: actor.avatar
498 }
499 }
500
501 toFormattedJSON (this: MChannelFormattable): VideoChannel {
480 const actor = this.Actor.toFormattedJSON() 502 const actor = this.Actor.toFormattedJSON()
481 const videoChannel = { 503 const videoChannel = {
482 id: this.id, 504 id: this.id,
@@ -494,20 +516,7 @@ export class VideoChannelModel extends Model<VideoChannelModel> {
494 return Object.assign(actor, videoChannel) 516 return Object.assign(actor, videoChannel)
495 } 517 }
496 518
497 toFormattedSummaryJSON (): VideoChannelSummary { 519 toActivityPubObject (this: MChannelAP): ActivityPubActor {
498 const actor = this.Actor.toFormattedJSON()
499
500 return {
501 id: this.id,
502 name: actor.name,
503 displayName: this.getDisplayName(),
504 url: actor.url,
505 host: actor.host,
506 avatar: actor.avatar
507 }
508 }
509
510 toActivityPubObject (): ActivityPubActor {
511 const obj = this.Actor.toActivityPubObject(this.name, 'VideoChannel') 520 const obj = this.Actor.toActivityPubObject(this.name, 'VideoChannel')
512 521
513 return Object.assign(obj, { 522 return Object.assign(obj, {
diff --git a/server/models/video/video-comment.ts b/server/models/video/video-comment.ts
index 58b75510d..2e4220434 100644
--- a/server/models/video/video-comment.ts
+++ b/server/models/video/video-comment.ts
@@ -1,36 +1,32 @@
1import { 1import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript'
2 AllowNull,
3 BeforeDestroy,
4 BelongsTo,
5 Column,
6 CreatedAt,
7 DataType,
8 ForeignKey,
9 Is,
10 Model,
11 Scopes,
12 Table,
13 UpdatedAt
14} from 'sequelize-typescript'
15import { ActivityTagObject } from '../../../shared/models/activitypub/objects/common-objects' 2import { ActivityTagObject } from '../../../shared/models/activitypub/objects/common-objects'
16import { VideoCommentObject } from '../../../shared/models/activitypub/objects/video-comment-object' 3import { VideoCommentObject } from '../../../shared/models/activitypub/objects/video-comment-object'
17import { VideoComment } from '../../../shared/models/videos/video-comment.model' 4import { VideoComment } from '../../../shared/models/videos/video-comment.model'
18import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' 5import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
19import { CONSTRAINTS_FIELDS, WEBSERVER } from '../../initializers/constants' 6import { CONSTRAINTS_FIELDS, WEBSERVER } from '../../initializers/constants'
20import { sendDeleteVideoComment } from '../../lib/activitypub/send'
21import { AccountModel } from '../account/account' 7import { AccountModel } from '../account/account'
22import { ActorModel } from '../activitypub/actor' 8import { ActorModel } from '../activitypub/actor'
23import { AvatarModel } from '../avatar/avatar'
24import { ServerModel } from '../server/server'
25import { buildBlockedAccountSQL, buildLocalAccountIdsIn, getSort, throwIfNotValid } from '../utils' 9import { buildBlockedAccountSQL, buildLocalAccountIdsIn, getSort, throwIfNotValid } from '../utils'
26import { VideoModel } from './video' 10import { VideoModel } from './video'
27import { VideoChannelModel } from './video-channel' 11import { VideoChannelModel } from './video-channel'
28import { getServerActor } from '../../helpers/utils' 12import { getServerActor } from '../../helpers/utils'
29import { UserModel } from '../account/user'
30import { actorNameAlphabet } from '../../helpers/custom-validators/activitypub/actor' 13import { actorNameAlphabet } from '../../helpers/custom-validators/activitypub/actor'
31import { regexpCapture } from '../../helpers/regexp' 14import { regexpCapture } from '../../helpers/regexp'
32import { uniq } from 'lodash' 15import { uniq } from 'lodash'
33import { FindOptions, literal, Op, Order, ScopeOptions, Sequelize, Transaction } from 'sequelize' 16import { FindOptions, Op, Order, ScopeOptions, Sequelize, Transaction } from 'sequelize'
17import * as Bluebird from 'bluebird'
18import {
19 MComment,
20 MCommentAP,
21 MCommentFormattable,
22 MCommentId,
23 MCommentOwner,
24 MCommentOwnerReplyVideoLight,
25 MCommentOwnerVideo,
26 MCommentOwnerVideoFeed,
27 MCommentOwnerVideoReply
28} from '../../typings/models/video'
29import { MUserAccountId } from '@server/typings/models'
34 30
35enum ScopeNames { 31enum ScopeNames {
36 WITH_ACCOUNT = 'WITH_ACCOUNT', 32 WITH_ACCOUNT = 'WITH_ACCOUNT',
@@ -68,22 +64,7 @@ enum ScopeNames {
68 [ScopeNames.WITH_ACCOUNT]: { 64 [ScopeNames.WITH_ACCOUNT]: {
69 include: [ 65 include: [
70 { 66 {
71 model: AccountModel, 67 model: AccountModel
72 include: [
73 {
74 model: ActorModel,
75 include: [
76 {
77 model: ServerModel,
78 required: false
79 },
80 {
81 model: AvatarModel,
82 required: false
83 }
84 ]
85 }
86 ]
87 } 68 }
88 ] 69 ]
89 }, 70 },
@@ -102,22 +83,12 @@ enum ScopeNames {
102 required: true, 83 required: true,
103 include: [ 84 include: [
104 { 85 {
105 model: VideoChannelModel.unscoped(), 86 model: VideoChannelModel,
106 required: true, 87 required: true,
107 include: [ 88 include: [
108 { 89 {
109 model: ActorModel,
110 required: true
111 },
112 {
113 model: AccountModel, 90 model: AccountModel,
114 required: true, 91 required: true
115 include: [
116 {
117 model: ActorModel,
118 required: true
119 }
120 ]
121 } 92 }
122 ] 93 ]
123 } 94 }
@@ -212,7 +183,7 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
212 }) 183 })
213 Account: AccountModel 184 Account: AccountModel
214 185
215 static loadById (id: number, t?: Transaction) { 186 static loadById (id: number, t?: Transaction): Bluebird<MComment> {
216 const query: FindOptions = { 187 const query: FindOptions = {
217 where: { 188 where: {
218 id 189 id
@@ -224,7 +195,7 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
224 return VideoCommentModel.findOne(query) 195 return VideoCommentModel.findOne(query)
225 } 196 }
226 197
227 static loadByIdAndPopulateVideoAndAccountAndReply (id: number, t?: Transaction) { 198 static loadByIdAndPopulateVideoAndAccountAndReply (id: number, t?: Transaction): Bluebird<MCommentOwnerVideoReply> {
228 const query: FindOptions = { 199 const query: FindOptions = {
229 where: { 200 where: {
230 id 201 id
@@ -238,7 +209,7 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
238 .findOne(query) 209 .findOne(query)
239 } 210 }
240 211
241 static loadByUrlAndPopulateAccountAndVideo (url: string, t?: Transaction) { 212 static loadByUrlAndPopulateAccountAndVideo (url: string, t?: Transaction): Bluebird<MCommentOwnerVideo> {
242 const query: FindOptions = { 213 const query: FindOptions = {
243 where: { 214 where: {
244 url 215 url
@@ -250,7 +221,7 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
250 return VideoCommentModel.scope([ ScopeNames.WITH_ACCOUNT, ScopeNames.WITH_VIDEO ]).findOne(query) 221 return VideoCommentModel.scope([ ScopeNames.WITH_ACCOUNT, ScopeNames.WITH_VIDEO ]).findOne(query)
251 } 222 }
252 223
253 static loadByUrlAndPopulateReplyAndVideoUrlAndAccount (url: string, t?: Transaction) { 224 static loadByUrlAndPopulateReplyAndVideoUrlAndAccount (url: string, t?: Transaction): Bluebird<MCommentOwnerReplyVideoLight> {
254 const query: FindOptions = { 225 const query: FindOptions = {
255 where: { 226 where: {
256 url 227 url
@@ -273,7 +244,7 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
273 start: number, 244 start: number,
274 count: number, 245 count: number,
275 sort: string, 246 sort: string,
276 user?: UserModel 247 user?: MUserAccountId
277 }) { 248 }) {
278 const { videoId, start, count, sort, user } = parameters 249 const { videoId, start, count, sort, user } = parameters
279 250
@@ -314,7 +285,7 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
314 static async listThreadCommentsForApi (parameters: { 285 static async listThreadCommentsForApi (parameters: {
315 videoId: number, 286 videoId: number,
316 threadId: number, 287 threadId: number,
317 user?: UserModel 288 user?: MUserAccountId
318 }) { 289 }) {
319 const { videoId, threadId, user } = parameters 290 const { videoId, threadId, user } = parameters
320 291
@@ -353,7 +324,7 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
353 }) 324 })
354 } 325 }
355 326
356 static listThreadParentComments (comment: VideoCommentModel, t: Transaction, order: 'ASC' | 'DESC' = 'ASC') { 327 static listThreadParentComments (comment: MCommentId, t: Transaction, order: 'ASC' | 'DESC' = 'ASC'): Bluebird<MCommentOwner[]> {
357 const query = { 328 const query = {
358 order: [ [ 'createdAt', order ] ] as Order, 329 order: [ [ 'createdAt', order ] ] as Order,
359 where: { 330 where: {
@@ -389,10 +360,10 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
389 transaction: t 360 transaction: t
390 } 361 }
391 362
392 return VideoCommentModel.findAndCountAll(query) 363 return VideoCommentModel.findAndCountAll<MComment>(query)
393 } 364 }
394 365
395 static listForFeed (start: number, count: number, videoId?: number) { 366 static listForFeed (start: number, count: number, videoId?: number): Bluebird<MCommentOwnerVideoFeed[]> {
396 const query = { 367 const query = {
397 order: [ [ 'createdAt', 'DESC' ] ] as Order, 368 order: [ [ 'createdAt', 'DESC' ] ] as Order,
398 offset: start, 369 offset: start,
@@ -506,7 +477,7 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
506 return uniq(result) 477 return uniq(result)
507 } 478 }
508 479
509 toFormattedJSON () { 480 toFormattedJSON (this: MCommentFormattable) {
510 return { 481 return {
511 id: this.id, 482 id: this.id,
512 url: this.url, 483 url: this.url,
@@ -521,7 +492,7 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
521 } as VideoComment 492 } as VideoComment
522 } 493 }
523 494
524 toActivityPubObject (threadParentComments: VideoCommentModel[]): VideoCommentObject { 495 toActivityPubObject (this: MCommentAP, threadParentComments: MCommentOwner[]): VideoCommentObject {
525 let inReplyTo: string 496 let inReplyTo: string
526 // New thread, so in AS we reply to the video 497 // New thread, so in AS we reply to the video
527 if (this.inReplyToCommentId === null) { 498 if (this.inReplyToCommentId === null) {
diff --git a/server/models/video/video-file.ts b/server/models/video/video-file.ts
index 05c490759..6304f741c 100644
--- a/server/models/video/video-file.ts
+++ b/server/models/video/video-file.ts
@@ -25,6 +25,7 @@ import { VideoRedundancyModel } from '../redundancy/video-redundancy'
25import { VideoStreamingPlaylistModel } from './video-streaming-playlist' 25import { VideoStreamingPlaylistModel } from './video-streaming-playlist'
26import { FindOptions, QueryTypes, Transaction } from 'sequelize' 26import { FindOptions, QueryTypes, Transaction } from 'sequelize'
27import { MIMETYPES } from '../../initializers/constants' 27import { MIMETYPES } from '../../initializers/constants'
28import { MVideoFile } from '@server/typings/models'
28 29
29@Table({ 30@Table({
30 tableName: 'videoFile', 31 tableName: 'videoFile',
@@ -166,7 +167,7 @@ export class VideoFileModel extends Model<VideoFileModel> {
166 return !!MIMETYPES.AUDIO.EXT_MIMETYPE[this.extname] 167 return !!MIMETYPES.AUDIO.EXT_MIMETYPE[this.extname]
167 } 168 }
168 169
169 hasSameUniqueKeysThan (other: VideoFileModel) { 170 hasSameUniqueKeysThan (other: MVideoFile) {
170 return this.fps === other.fps && 171 return this.fps === other.fps &&
171 this.resolution === other.resolution && 172 this.resolution === other.resolution &&
172 this.videoId === other.videoId 173 this.videoId === other.videoId
diff --git a/server/models/video/video-format-utils.ts b/server/models/video/video-format-utils.ts
index 284539def..2987aa780 100644
--- a/server/models/video/video-format-utils.ts
+++ b/server/models/video/video-format-utils.ts
@@ -1,6 +1,5 @@
1import { Video, VideoDetails, VideoFile } from '../../../shared/models/videos' 1import { Video, VideoDetails, VideoFile } from '../../../shared/models/videos'
2import { VideoModel } from './video' 2import { VideoModel } from './video'
3import { VideoFileModel } from './video-file'
4import { 3import {
5 ActivityPlaylistInfohashesObject, 4 ActivityPlaylistInfohashesObject,
6 ActivityPlaylistSegmentHashesObject, 5 ActivityPlaylistSegmentHashesObject,
@@ -17,7 +16,9 @@ import {
17} from '../../lib/activitypub' 16} from '../../lib/activitypub'
18import { isArray } from '../../helpers/custom-validators/misc' 17import { isArray } from '../../helpers/custom-validators/misc'
19import { VideoStreamingPlaylist } from '../../../shared/models/videos/video-streaming-playlist.model' 18import { VideoStreamingPlaylist } from '../../../shared/models/videos/video-streaming-playlist.model'
20import { VideoStreamingPlaylistModel } from './video-streaming-playlist' 19import { MStreamingPlaylistRedundanciesOpt, MVideo, MVideoAP, MVideoFormattable, MVideoFormattableDetails } from '../../typings/models'
20import { MStreamingPlaylistRedundancies } from '../../typings/models/video/video-streaming-playlist'
21import { MVideoFileRedundanciesOpt } from '../../typings/models/video/video-file'
21 22
22export type VideoFormattingJSONOptions = { 23export type VideoFormattingJSONOptions = {
23 completeDescription?: boolean 24 completeDescription?: boolean
@@ -28,7 +29,7 @@ export type VideoFormattingJSONOptions = {
28 blacklistInfo?: boolean 29 blacklistInfo?: boolean
29 } 30 }
30} 31}
31function videoModelToFormattedJSON (video: VideoModel, options?: VideoFormattingJSONOptions): Video { 32function videoModelToFormattedJSON (video: MVideoFormattable, options?: VideoFormattingJSONOptions): Video {
32 const userHistory = isArray(video.UserVideoHistories) ? video.UserVideoHistories[0] : undefined 33 const userHistory = isArray(video.UserVideoHistories) ? video.UserVideoHistories[0] : undefined
33 34
34 const videoObject: Video = { 35 const videoObject: Video = {
@@ -102,7 +103,7 @@ function videoModelToFormattedJSON (video: VideoModel, options?: VideoFormatting
102 return videoObject 103 return videoObject
103} 104}
104 105
105function videoModelToFormattedDetailsJSON (video: VideoModel): VideoDetails { 106function videoModelToFormattedDetailsJSON (video: MVideoFormattableDetails): VideoDetails {
106 const formattedJson = video.toFormattedJSON({ 107 const formattedJson = video.toFormattedJSON({
107 additionalAttributes: { 108 additionalAttributes: {
108 scheduledUpdate: true, 109 scheduledUpdate: true,
@@ -114,7 +115,7 @@ function videoModelToFormattedDetailsJSON (video: VideoModel): VideoDetails {
114 115
115 const tags = video.Tags ? video.Tags.map(t => t.name) : [] 116 const tags = video.Tags ? video.Tags.map(t => t.name) : []
116 117
117 const streamingPlaylists = streamingPlaylistsModelToFormattedJSON(video, video.VideoStreamingPlaylists) 118 const streamingPlaylists = streamingPlaylistsModelToFormattedJSON(video.VideoStreamingPlaylists)
118 119
119 const detailsJson = { 120 const detailsJson = {
120 support: video.support, 121 support: video.support,
@@ -142,7 +143,7 @@ function videoModelToFormattedDetailsJSON (video: VideoModel): VideoDetails {
142 return Object.assign(formattedJson, detailsJson) 143 return Object.assign(formattedJson, detailsJson)
143} 144}
144 145
145function streamingPlaylistsModelToFormattedJSON (video: VideoModel, playlists: VideoStreamingPlaylistModel[]): VideoStreamingPlaylist[] { 146function streamingPlaylistsModelToFormattedJSON (playlists: MStreamingPlaylistRedundanciesOpt[]): VideoStreamingPlaylist[] {
146 if (isArray(playlists) === false) return [] 147 if (isArray(playlists) === false) return []
147 148
148 return playlists 149 return playlists
@@ -161,7 +162,7 @@ function streamingPlaylistsModelToFormattedJSON (video: VideoModel, playlists: V
161 }) 162 })
162} 163}
163 164
164function videoFilesModelToFormattedJSON (video: VideoModel, videoFiles: VideoFileModel[]): VideoFile[] { 165function videoFilesModelToFormattedJSON (video: MVideo, videoFiles: MVideoFileRedundanciesOpt[]): VideoFile[] {
165 const { baseUrlHttp, baseUrlWs } = video.getBaseUrls() 166 const { baseUrlHttp, baseUrlWs } = video.getBaseUrls()
166 167
167 return videoFiles 168 return videoFiles
@@ -189,7 +190,7 @@ function videoFilesModelToFormattedJSON (video: VideoModel, videoFiles: VideoFil
189 }) 190 })
190} 191}
191 192
192function videoModelToActivityPubObject (video: VideoModel): VideoTorrentObject { 193function videoModelToActivityPubObject (video: MVideoAP): VideoTorrentObject {
193 const { baseUrlHttp, baseUrlWs } = video.getBaseUrls() 194 const { baseUrlHttp, baseUrlWs } = video.getBaseUrls()
194 if (!video.Tags) video.Tags = [] 195 if (!video.Tags) video.Tags = []
195 196
diff --git a/server/models/video/video-import.ts b/server/models/video/video-import.ts
index 480a671c8..af5314ce9 100644
--- a/server/models/video/video-import.ts
+++ b/server/models/video/video-import.ts
@@ -20,6 +20,8 @@ import { isVideoImportStateValid, isVideoImportTargetUrlValid } from '../../help
20import { VideoImport, VideoImportState } from '../../../shared' 20import { VideoImport, VideoImportState } from '../../../shared'
21import { isVideoMagnetUriValid } from '../../helpers/custom-validators/videos' 21import { isVideoMagnetUriValid } from '../../helpers/custom-validators/videos'
22import { UserModel } from '../account/user' 22import { UserModel } from '../account/user'
23import * as Bluebird from 'bluebird'
24import { MVideoImportDefault, MVideoImportFormattable } from '@server/typings/models/video/video-import'
23 25
24@DefaultScope(() => ({ 26@DefaultScope(() => ({
25 include: [ 27 include: [
@@ -28,7 +30,11 @@ import { UserModel } from '../account/user'
28 required: true 30 required: true
29 }, 31 },
30 { 32 {
31 model: VideoModel.scope([ VideoModelScopeNames.WITH_ACCOUNT_DETAILS, VideoModelScopeNames.WITH_TAGS]), 33 model: VideoModel.scope([
34 VideoModelScopeNames.WITH_ACCOUNT_DETAILS,
35 VideoModelScopeNames.WITH_TAGS,
36 VideoModelScopeNames.WITH_THUMBNAILS
37 ]),
32 required: false 38 required: false
33 } 39 }
34 ] 40 ]
@@ -114,7 +120,7 @@ export class VideoImportModel extends Model<VideoImportModel> {
114 return undefined 120 return undefined
115 } 121 }
116 122
117 static loadAndPopulateVideo (id: number) { 123 static loadAndPopulateVideo (id: number): Bluebird<MVideoImportDefault> {
118 return VideoImportModel.findByPk(id) 124 return VideoImportModel.findByPk(id)
119 } 125 }
120 126
@@ -135,7 +141,7 @@ export class VideoImportModel extends Model<VideoImportModel> {
135 } 141 }
136 } 142 }
137 143
138 return VideoImportModel.findAndCountAll(query) 144 return VideoImportModel.findAndCountAll<MVideoImportDefault>(query)
139 .then(({ rows, count }) => { 145 .then(({ rows, count }) => {
140 return { 146 return {
141 data: rows, 147 data: rows,
@@ -148,7 +154,7 @@ export class VideoImportModel extends Model<VideoImportModel> {
148 return this.targetUrl || this.magnetUri || this.torrentName 154 return this.targetUrl || this.magnetUri || this.torrentName
149 } 155 }
150 156
151 toFormattedJSON (): VideoImport { 157 toFormattedJSON (this: MVideoImportFormattable): VideoImport {
152 const videoFormatOptions = { 158 const videoFormatOptions = {
153 completeDescription: true, 159 completeDescription: true,
154 additionalAttributes: { state: true, waitTranscoding: true, scheduledUpdate: true } 160 additionalAttributes: { state: true, waitTranscoding: true, scheduledUpdate: true }
diff --git a/server/models/video/video-playlist-element.ts b/server/models/video/video-playlist-element.ts
index dd7653533..a28021313 100644
--- a/server/models/video/video-playlist-element.ts
+++ b/server/models/video/video-playlist-element.ts
@@ -21,10 +21,18 @@ import { CONSTRAINTS_FIELDS } from '../../initializers/constants'
21import { PlaylistElementObject } from '../../../shared/models/activitypub/objects/playlist-element-object' 21import { PlaylistElementObject } from '../../../shared/models/activitypub/objects/playlist-element-object'
22import * as validator from 'validator' 22import * as validator from 'validator'
23import { AggregateOptions, Op, ScopeOptions, Sequelize, Transaction } from 'sequelize' 23import { AggregateOptions, Op, ScopeOptions, Sequelize, Transaction } from 'sequelize'
24import { UserModel } from '../account/user'
25import { VideoPlaylistElement, VideoPlaylistElementType } from '../../../shared/models/videos/playlist/video-playlist-element.model' 24import { VideoPlaylistElement, VideoPlaylistElementType } from '../../../shared/models/videos/playlist/video-playlist-element.model'
26import { AccountModel } from '../account/account' 25import { AccountModel } from '../account/account'
27import { VideoPrivacy } from '../../../shared/models/videos' 26import { VideoPrivacy } from '../../../shared/models/videos'
27import * as Bluebird from 'bluebird'
28import {
29 MVideoPlaylistElement,
30 MVideoPlaylistElementAP,
31 MVideoPlaylistElementFormattable,
32 MVideoPlaylistElementVideoUrlPlaylistPrivacy,
33 MVideoPlaylistVideoThumbnail
34} from '@server/typings/models/video/video-playlist-element'
35import { MUserAccountId } from '@server/typings/models'
28 36
29@Table({ 37@Table({
30 tableName: 'videoPlaylistElement', 38 tableName: 'videoPlaylistElement',
@@ -116,7 +124,7 @@ export class VideoPlaylistElementModel extends Model<VideoPlaylistElementModel>
116 count: number, 124 count: number,
117 videoPlaylistId: number, 125 videoPlaylistId: number,
118 serverAccount: AccountModel, 126 serverAccount: AccountModel,
119 user?: UserModel 127 user?: MUserAccountId
120 }) { 128 }) {
121 const accountIds = [ options.serverAccount.id ] 129 const accountIds = [ options.serverAccount.id ]
122 const videoScope: (ScopeOptions | string)[] = [ 130 const videoScope: (ScopeOptions | string)[] = [
@@ -162,7 +170,7 @@ export class VideoPlaylistElementModel extends Model<VideoPlaylistElementModel>
162 ]).then(([ total, data ]) => ({ total, data })) 170 ]).then(([ total, data ]) => ({ total, data }))
163 } 171 }
164 172
165 static loadByPlaylistAndVideo (videoPlaylistId: number, videoId: number) { 173 static loadByPlaylistAndVideo (videoPlaylistId: number, videoId: number): Bluebird<MVideoPlaylistElement> {
166 const query = { 174 const query = {
167 where: { 175 where: {
168 videoPlaylistId, 176 videoPlaylistId,
@@ -173,11 +181,14 @@ export class VideoPlaylistElementModel extends Model<VideoPlaylistElementModel>
173 return VideoPlaylistElementModel.findOne(query) 181 return VideoPlaylistElementModel.findOne(query)
174 } 182 }
175 183
176 static loadById (playlistElementId: number) { 184 static loadById (playlistElementId: number): Bluebird<MVideoPlaylistElement> {
177 return VideoPlaylistElementModel.findByPk(playlistElementId) 185 return VideoPlaylistElementModel.findByPk(playlistElementId)
178 } 186 }
179 187
180 static loadByPlaylistAndVideoForAP (playlistId: number | string, videoId: number | string) { 188 static loadByPlaylistAndVideoForAP (
189 playlistId: number | string,
190 videoId: number | string
191 ): Bluebird<MVideoPlaylistElementVideoUrlPlaylistPrivacy> {
181 const playlistWhere = validator.isUUID('' + playlistId) ? { uuid: playlistId } : { id: playlistId } 192 const playlistWhere = validator.isUUID('' + playlistId) ? { uuid: playlistId } : { id: playlistId }
182 const videoWhere = validator.isUUID('' + videoId) ? { uuid: videoId } : { id: videoId } 193 const videoWhere = validator.isUUID('' + videoId) ? { uuid: videoId } : { id: videoId }
183 194
@@ -218,7 +229,7 @@ export class VideoPlaylistElementModel extends Model<VideoPlaylistElementModel>
218 }) 229 })
219 } 230 }
220 231
221 static loadFirstElementWithVideoThumbnail (videoPlaylistId: number) { 232 static loadFirstElementWithVideoThumbnail (videoPlaylistId: number): Bluebird<MVideoPlaylistVideoThumbnail> {
222 const query = { 233 const query = {
223 order: getSort('position'), 234 order: getSort('position'),
224 where: { 235 where: {
@@ -290,7 +301,7 @@ export class VideoPlaylistElementModel extends Model<VideoPlaylistElementModel>
290 return VideoPlaylistElementModel.increment({ position: by }, query) 301 return VideoPlaylistElementModel.increment({ position: by }, query)
291 } 302 }
292 303
293 getType (displayNSFW?: boolean, accountId?: number) { 304 getType (this: MVideoPlaylistElementFormattable, displayNSFW?: boolean, accountId?: number) {
294 const video = this.Video 305 const video = this.Video
295 306
296 if (!video) return VideoPlaylistElementType.DELETED 307 if (!video) return VideoPlaylistElementType.DELETED
@@ -306,14 +317,17 @@ export class VideoPlaylistElementModel extends Model<VideoPlaylistElementModel>
306 return VideoPlaylistElementType.REGULAR 317 return VideoPlaylistElementType.REGULAR
307 } 318 }
308 319
309 getVideoElement (displayNSFW?: boolean, accountId?: number) { 320 getVideoElement (this: MVideoPlaylistElementFormattable, displayNSFW?: boolean, accountId?: number) {
310 if (!this.Video) return null 321 if (!this.Video) return null
311 if (this.getType(displayNSFW, accountId) !== VideoPlaylistElementType.REGULAR) return null 322 if (this.getType(displayNSFW, accountId) !== VideoPlaylistElementType.REGULAR) return null
312 323
313 return this.Video.toFormattedJSON() 324 return this.Video.toFormattedJSON()
314 } 325 }
315 326
316 toFormattedJSON (options: { displayNSFW?: boolean, accountId?: number } = {}): VideoPlaylistElement { 327 toFormattedJSON (
328 this: MVideoPlaylistElementFormattable,
329 options: { displayNSFW?: boolean, accountId?: number } = {}
330 ): VideoPlaylistElement {
317 return { 331 return {
318 id: this.id, 332 id: this.id,
319 position: this.position, 333 position: this.position,
@@ -326,7 +340,7 @@ export class VideoPlaylistElementModel extends Model<VideoPlaylistElementModel>
326 } 340 }
327 } 341 }
328 342
329 toActivityPubObject (): PlaylistElementObject { 343 toActivityPubObject (this: MVideoPlaylistElementAP): PlaylistElementObject {
330 const base: PlaylistElementObject = { 344 const base: PlaylistElementObject = {
331 id: this.url, 345 id: this.url,
332 type: 'PlaylistElement', 346 type: 'PlaylistElement',
diff --git a/server/models/video/video-playlist.ts b/server/models/video/video-playlist.ts
index c8e97c491..278d80ac0 100644
--- a/server/models/video/video-playlist.ts
+++ b/server/models/video/video-playlist.ts
@@ -43,6 +43,15 @@ import { VideoPlaylistType } from '../../../shared/models/videos/playlist/video-
43import { ThumbnailModel } from './thumbnail' 43import { ThumbnailModel } from './thumbnail'
44import { ActivityIconObject } from '../../../shared/models/activitypub/objects' 44import { ActivityIconObject } from '../../../shared/models/activitypub/objects'
45import { FindOptions, literal, Op, ScopeOptions, Transaction, WhereOptions } from 'sequelize' 45import { FindOptions, literal, Op, ScopeOptions, Transaction, WhereOptions } from 'sequelize'
46import * as Bluebird from 'bluebird'
47import {
48 MVideoPlaylistAccountThumbnail, MVideoPlaylistAP,
49 MVideoPlaylistFormattable,
50 MVideoPlaylistFull,
51 MVideoPlaylistFullSummary,
52 MVideoPlaylistIdWithElements
53} from '../../typings/models/video/video-playlist'
54import { MThumbnail } from '../../typings/models/video/thumbnail'
46 55
47enum ScopeNames { 56enum ScopeNames {
48 AVAILABLE_FOR_LIST = 'AVAILABLE_FOR_LIST', 57 AVAILABLE_FOR_LIST = 'AVAILABLE_FOR_LIST',
@@ -332,7 +341,7 @@ export class VideoPlaylistModel extends Model<VideoPlaylistModel> {
332 }) 341 })
333 } 342 }
334 343
335 static listPlaylistIdsOf (accountId: number, videoIds: number[]) { 344 static listPlaylistIdsOf (accountId: number, videoIds: number[]): Bluebird<MVideoPlaylistIdWithElements[]> {
336 const query = { 345 const query = {
337 attributes: [ 'id' ], 346 attributes: [ 'id' ],
338 where: { 347 where: {
@@ -368,7 +377,7 @@ export class VideoPlaylistModel extends Model<VideoPlaylistModel> {
368 .then(e => !!e) 377 .then(e => !!e)
369 } 378 }
370 379
371 static loadWithAccountAndChannelSummary (id: number | string, transaction: Transaction) { 380 static loadWithAccountAndChannelSummary (id: number | string, transaction: Transaction): Bluebird<MVideoPlaylistFullSummary> {
372 const where = buildWhereIdOrUUID(id) 381 const where = buildWhereIdOrUUID(id)
373 382
374 const query = { 383 const query = {
@@ -381,7 +390,7 @@ export class VideoPlaylistModel extends Model<VideoPlaylistModel> {
381 .findOne(query) 390 .findOne(query)
382 } 391 }
383 392
384 static loadWithAccountAndChannel (id: number | string, transaction: Transaction) { 393 static loadWithAccountAndChannel (id: number | string, transaction: Transaction): Bluebird<MVideoPlaylistFull> {
385 const where = buildWhereIdOrUUID(id) 394 const where = buildWhereIdOrUUID(id)
386 395
387 const query = { 396 const query = {
@@ -394,7 +403,7 @@ export class VideoPlaylistModel extends Model<VideoPlaylistModel> {
394 .findOne(query) 403 .findOne(query)
395 } 404 }
396 405
397 static loadByUrlAndPopulateAccount (url: string) { 406 static loadByUrlAndPopulateAccount (url: string): Bluebird<MVideoPlaylistAccountThumbnail> {
398 const query = { 407 const query = {
399 where: { 408 where: {
400 url 409 url
@@ -423,7 +432,7 @@ export class VideoPlaylistModel extends Model<VideoPlaylistModel> {
423 return VideoPlaylistModel.update({ privacy: VideoPlaylistPrivacy.PRIVATE, videoChannelId: null }, query) 432 return VideoPlaylistModel.update({ privacy: VideoPlaylistPrivacy.PRIVATE, videoChannelId: null }, query)
424 } 433 }
425 434
426 async setAndSaveThumbnail (thumbnail: ThumbnailModel, t: Transaction) { 435 async setAndSaveThumbnail (thumbnail: MThumbnail, t: Transaction) {
427 thumbnail.videoPlaylistId = this.id 436 thumbnail.videoPlaylistId = this.id
428 437
429 this.Thumbnail = await thumbnail.save({ transaction: t }) 438 this.Thumbnail = await thumbnail.save({ transaction: t })
@@ -471,7 +480,7 @@ export class VideoPlaylistModel extends Model<VideoPlaylistModel> {
471 return isOutdated(this, ACTIVITY_PUB.VIDEO_PLAYLIST_REFRESH_INTERVAL) 480 return isOutdated(this, ACTIVITY_PUB.VIDEO_PLAYLIST_REFRESH_INTERVAL)
472 } 481 }
473 482
474 toFormattedJSON (): VideoPlaylist { 483 toFormattedJSON (this: MVideoPlaylistFormattable): VideoPlaylist {
475 return { 484 return {
476 id: this.id, 485 id: this.id,
477 uuid: this.uuid, 486 uuid: this.uuid,
@@ -501,7 +510,7 @@ export class VideoPlaylistModel extends Model<VideoPlaylistModel> {
501 } 510 }
502 } 511 }
503 512
504 toActivityPubObject (page: number, t: Transaction): Promise<PlaylistObject> { 513 toActivityPubObject (this: MVideoPlaylistAP, page: number, t: Transaction): Promise<PlaylistObject> {
505 const handler = (start: number, count: number) => { 514 const handler = (start: number, count: number) => {
506 return VideoPlaylistElementModel.listUrlsOfForAP(this.id, start, count, t) 515 return VideoPlaylistElementModel.listUrlsOfForAP(this.id, start, count, t)
507 } 516 }
diff --git a/server/models/video/video-share.ts b/server/models/video/video-share.ts
index d8ed64557..9019b401a 100644
--- a/server/models/video/video-share.ts
+++ b/server/models/video/video-share.ts
@@ -8,6 +8,8 @@ import { buildLocalActorIdsIn, throwIfNotValid } from '../utils'
8import { VideoModel } from './video' 8import { VideoModel } from './video'
9import { VideoChannelModel } from './video-channel' 9import { VideoChannelModel } from './video-channel'
10import { Op, Transaction } from 'sequelize' 10import { Op, Transaction } from 'sequelize'
11import { MVideoShareActor, MVideoShareFull } from '../../typings/models/video'
12import { MActorDefault } from '../../typings/models'
11 13
12enum ScopeNames { 14enum ScopeNames {
13 FULL = 'FULL', 15 FULL = 'FULL',
@@ -88,7 +90,7 @@ export class VideoShareModel extends Model<VideoShareModel> {
88 }) 90 })
89 Video: VideoModel 91 Video: VideoModel
90 92
91 static load (actorId: number, videoId: number, t?: Transaction) { 93 static load (actorId: number, videoId: number, t?: Transaction): Bluebird<MVideoShareActor> {
92 return VideoShareModel.scope(ScopeNames.WITH_ACTOR).findOne({ 94 return VideoShareModel.scope(ScopeNames.WITH_ACTOR).findOne({
93 where: { 95 where: {
94 actorId, 96 actorId,
@@ -98,7 +100,7 @@ export class VideoShareModel extends Model<VideoShareModel> {
98 }) 100 })
99 } 101 }
100 102
101 static loadByUrl (url: string, t: Transaction) { 103 static loadByUrl (url: string, t: Transaction): Bluebird<MVideoShareFull> {
102 return VideoShareModel.scope(ScopeNames.FULL).findOne({ 104 return VideoShareModel.scope(ScopeNames.FULL).findOne({
103 where: { 105 where: {
104 url 106 url
@@ -107,7 +109,7 @@ export class VideoShareModel extends Model<VideoShareModel> {
107 }) 109 })
108 } 110 }
109 111
110 static loadActorsByShare (videoId: number, t: Transaction) { 112 static loadActorsByShare (videoId: number, t: Transaction): Bluebird<MActorDefault[]> {
111 const query = { 113 const query = {
112 where: { 114 where: {
113 videoId 115 videoId
@@ -122,10 +124,10 @@ export class VideoShareModel extends Model<VideoShareModel> {
122 } 124 }
123 125
124 return VideoShareModel.scope(ScopeNames.FULL).findAll(query) 126 return VideoShareModel.scope(ScopeNames.FULL).findAll(query)
125 .then(res => res.map(r => r.Actor)) 127 .then((res: MVideoShareFull[]) => res.map(r => r.Actor))
126 } 128 }
127 129
128 static loadActorsWhoSharedVideosOf (actorOwnerId: number, t: Transaction): Bluebird<ActorModel[]> { 130 static loadActorsWhoSharedVideosOf (actorOwnerId: number, t: Transaction): Bluebird<MActorDefault[]> {
129 const query = { 131 const query = {
130 attributes: [], 132 attributes: [],
131 include: [ 133 include: [
@@ -163,7 +165,7 @@ export class VideoShareModel extends Model<VideoShareModel> {
163 .then(res => res.map(r => r.Actor)) 165 .then(res => res.map(r => r.Actor))
164 } 166 }
165 167
166 static loadActorsByVideoChannel (videoChannelId: number, t: Transaction): Bluebird<ActorModel[]> { 168 static loadActorsByVideoChannel (videoChannelId: number, t: Transaction): Bluebird<MActorDefault[]> {
167 const query = { 169 const query = {
168 attributes: [], 170 attributes: [],
169 include: [ 171 include: [
diff --git a/server/models/video/video-streaming-playlist.ts b/server/models/video/video-streaming-playlist.ts
index 31dc82c54..0ea90d28c 100644
--- a/server/models/video/video-streaming-playlist.ts
+++ b/server/models/video/video-streaming-playlist.ts
@@ -1,16 +1,16 @@
1import { AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, HasMany, Is, Model, Table, UpdatedAt, DataType } from 'sequelize-typescript' 1import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, HasMany, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
2import { isVideoFileInfoHashValid } from '../../helpers/custom-validators/videos' 2import { isVideoFileInfoHashValid } from '../../helpers/custom-validators/videos'
3import { throwIfNotValid } from '../utils' 3import { throwIfNotValid } from '../utils'
4import { VideoModel } from './video' 4import { VideoModel } from './video'
5import { VideoRedundancyModel } from '../redundancy/video-redundancy' 5import { VideoRedundancyModel } from '../redundancy/video-redundancy'
6import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type' 6import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type'
7import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' 7import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
8import { CONSTRAINTS_FIELDS, STATIC_PATHS, P2P_MEDIA_LOADER_PEER_VERSION } from '../../initializers/constants' 8import { CONSTRAINTS_FIELDS, P2P_MEDIA_LOADER_PEER_VERSION, STATIC_PATHS } from '../../initializers/constants'
9import { VideoFileModel } from './video-file'
10import { join } from 'path' 9import { join } from 'path'
11import { sha1 } from '../../helpers/core-utils' 10import { sha1 } from '../../helpers/core-utils'
12import { isArrayOf } from '../../helpers/custom-validators/misc' 11import { isArrayOf } from '../../helpers/custom-validators/misc'
13import { QueryTypes, Op } from 'sequelize' 12import { Op, QueryTypes } from 'sequelize'
13import { MStreamingPlaylist, MVideoFile } from '@server/typings/models'
14 14
15@Table({ 15@Table({
16 tableName: 'videoStreamingPlaylist', 16 tableName: 'videoStreamingPlaylist',
@@ -91,7 +91,7 @@ export class VideoStreamingPlaylistModel extends Model<VideoStreamingPlaylistMod
91 .then(results => results.length === 1) 91 .then(results => results.length === 1)
92 } 92 }
93 93
94 static buildP2PMediaLoaderInfoHashes (playlistUrl: string, videoFiles: VideoFileModel[]) { 94 static buildP2PMediaLoaderInfoHashes (playlistUrl: string, videoFiles: MVideoFile[]) {
95 const hashes: string[] = [] 95 const hashes: string[] = []
96 96
97 // https://github.com/Novage/p2p-media-loader/blob/master/p2p-media-loader-core/lib/p2p-media-manager.ts#L115 97 // https://github.com/Novage/p2p-media-loader/blob/master/p2p-media-loader-core/lib/p2p-media-manager.ts#L115
@@ -165,7 +165,7 @@ export class VideoStreamingPlaylistModel extends Model<VideoStreamingPlaylistMod
165 return baseUrlHttp + STATIC_PATHS.REDUNDANCY + this.getStringType() + '/' + this.Video.uuid 165 return baseUrlHttp + STATIC_PATHS.REDUNDANCY + this.getStringType() + '/' + this.Video.uuid
166 } 166 }
167 167
168 hasSameUniqueKeysThan (other: VideoStreamingPlaylistModel) { 168 hasSameUniqueKeysThan (other: MStreamingPlaylist) {
169 return this.type === other.type && 169 return this.type === other.type &&
170 this.videoId === other.videoId 170 this.videoId === other.videoId
171 } 171 }
diff --git a/server/models/video/video.ts b/server/models/video/video.ts
index b59df397d..ab7b49f1e 100644
--- a/server/models/video/video.ts
+++ b/server/models/video/video.ts
@@ -36,7 +36,7 @@ import {
36 Table, 36 Table,
37 UpdatedAt 37 UpdatedAt
38} from 'sequelize-typescript' 38} from 'sequelize-typescript'
39import { UserRight, VideoPrivacy, VideoResolution, VideoState } from '../../../shared' 39import { UserRight, VideoPrivacy, VideoState } from '../../../shared'
40import { VideoTorrentObject } from '../../../shared/models/activitypub/objects' 40import { VideoTorrentObject } from '../../../shared/models/activitypub/objects'
41import { Video, VideoDetails, VideoFile } from '../../../shared/models/videos' 41import { Video, VideoDetails, VideoFile } from '../../../shared/models/videos'
42import { VideoFilter } from '../../../shared/models/videos/video-query.type' 42import { VideoFilter } from '../../../shared/models/videos/video-query.type'
@@ -111,7 +111,6 @@ import {
111 videoModelToFormattedJSON 111 videoModelToFormattedJSON
112} from './video-format-utils' 112} from './video-format-utils'
113import { UserVideoHistoryModel } from '../account/user-video-history' 113import { UserVideoHistoryModel } from '../account/user-video-history'
114import { UserModel } from '../account/user'
115import { VideoImportModel } from './video-import' 114import { VideoImportModel } from './video-import'
116import { VideoStreamingPlaylistModel } from './video-streaming-playlist' 115import { VideoStreamingPlaylistModel } from './video-streaming-playlist'
117import { VideoPlaylistElementModel } from './video-playlist-element' 116import { VideoPlaylistElementModel } from './video-playlist-element'
@@ -120,6 +119,28 @@ import { ThumbnailModel } from './thumbnail'
120import { ThumbnailType } from '../../../shared/models/videos/thumbnail.type' 119import { ThumbnailType } from '../../../shared/models/videos/thumbnail.type'
121import { createTorrentPromise } from '../../helpers/webtorrent' 120import { createTorrentPromise } from '../../helpers/webtorrent'
122import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type' 121import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type'
122import {
123 MChannel,
124 MChannelAccountDefault,
125 MChannelId,
126 MUserAccountId,
127 MUserId,
128 MVideoAccountLight,
129 MVideoAccountLightBlacklistAllFiles,
130 MVideoAP,
131 MVideoDetails,
132 MVideoFormattable,
133 MVideoFormattableDetails,
134 MVideoForUser,
135 MVideoFullLight,
136 MVideoIdThumbnail,
137 MVideoThumbnail,
138 MVideoWithAllFiles,
139 MVideoWithFile,
140 MVideoWithRights
141} from '../../typings/models'
142import { MVideoFile, MVideoFileRedundanciesOpt } from '../../typings/models/video/video-file'
143import { MThumbnail } from '../../typings/models/video/thumbnail'
123 144
124// FIXME: Define indexes here because there is an issue with TS and Sequelize.literal when called directly in the annotation 145// FIXME: Define indexes here because there is an issue with TS and Sequelize.literal when called directly in the annotation
125const indexes: (ModelIndexesOptions & { where?: WhereOptions })[] = [ 146const indexes: (ModelIndexesOptions & { where?: WhereOptions })[] = [
@@ -232,8 +253,8 @@ export type AvailableForListIDsOptions = {
232 videoPlaylistId?: number 253 videoPlaylistId?: number
233 254
234 trendingDays?: number 255 trendingDays?: number
235 user?: UserModel, 256 user?: MUserAccountId
236 historyOfUser?: UserModel 257 historyOfUser?: MUserId
237 258
238 baseWhere?: WhereOptions[] 259 baseWhere?: WhereOptions[]
239} 260}
@@ -634,7 +655,7 @@ export type AvailableForListIDsOptions = {
634 [ ScopeNames.WITH_BLACKLISTED ]: { 655 [ ScopeNames.WITH_BLACKLISTED ]: {
635 include: [ 656 include: [
636 { 657 {
637 attributes: [ 'id', 'reason' ], 658 attributes: [ 'id', 'reason', 'unfederated' ],
638 model: VideoBlacklistModel, 659 model: VideoBlacklistModel,
639 required: false 660 required: false
640 } 661 }
@@ -989,18 +1010,16 @@ export class VideoModel extends Model<VideoModel> {
989 VideoCaptions: VideoCaptionModel[] 1010 VideoCaptions: VideoCaptionModel[]
990 1011
991 @BeforeDestroy 1012 @BeforeDestroy
992 static async sendDelete (instance: VideoModel, options) { 1013 static async sendDelete (instance: MVideoAccountLight, options) {
993 if (instance.isOwned()) { 1014 if (instance.isOwned()) {
994 if (!instance.VideoChannel) { 1015 if (!instance.VideoChannel) {
995 instance.VideoChannel = await instance.$get('VideoChannel', { 1016 instance.VideoChannel = await instance.$get('VideoChannel', {
996 include: [ 1017 include: [
997 { 1018 ActorModel,
998 model: AccountModel, 1019 AccountModel
999 include: [ ActorModel ]
1000 }
1001 ], 1020 ],
1002 transaction: options.transaction 1021 transaction: options.transaction
1003 }) as VideoChannelModel 1022 }) as MChannelAccountDefault
1004 } 1023 }
1005 1024
1006 return sendDeleteVideo(instance, options.transaction) 1025 return sendDeleteVideo(instance, options.transaction)
@@ -1039,7 +1058,7 @@ export class VideoModel extends Model<VideoModel> {
1039 return undefined 1058 return undefined
1040 } 1059 }
1041 1060
1042 static listLocal () { 1061 static listLocal (): Bluebird<MVideoWithAllFiles[]> {
1043 const query = { 1062 const query = {
1044 where: { 1063 where: {
1045 remote: false 1064 remote: false
@@ -1159,7 +1178,7 @@ export class VideoModel extends Model<VideoModel> {
1159 }) 1178 })
1160 } 1179 }
1161 1180
1162 static listUserVideosForApi (accountId: number, start: number, count: number, sort: string, withFiles = false) { 1181 static listUserVideosForApi (accountId: number, start: number, count: number, sort: string) {
1163 function buildBaseQuery (): FindOptions { 1182 function buildBaseQuery (): FindOptions {
1164 return { 1183 return {
1165 offset: start, 1184 offset: start,
@@ -1192,16 +1211,9 @@ export class VideoModel extends Model<VideoModel> {
1192 ScopeNames.WITH_THUMBNAILS 1211 ScopeNames.WITH_THUMBNAILS
1193 ] 1212 ]
1194 1213
1195 if (withFiles === true) {
1196 findQuery.include.push({
1197 model: VideoFileModel.unscoped(),
1198 required: true
1199 })
1200 }
1201
1202 return Promise.all([ 1214 return Promise.all([
1203 VideoModel.count(countQuery), 1215 VideoModel.count(countQuery),
1204 VideoModel.scope(findScopes).findAll(findQuery) 1216 VideoModel.scope(findScopes).findAll<MVideoForUser>(findQuery)
1205 ]).then(([ count, rows ]) => { 1217 ]).then(([ count, rows ]) => {
1206 return { 1218 return {
1207 data: rows, 1219 data: rows,
@@ -1228,8 +1240,8 @@ export class VideoModel extends Model<VideoModel> {
1228 followerActorId?: number 1240 followerActorId?: number
1229 videoPlaylistId?: number, 1241 videoPlaylistId?: number,
1230 trendingDays?: number, 1242 trendingDays?: number,
1231 user?: UserModel, 1243 user?: MUserAccountId,
1232 historyOfUser?: UserModel 1244 historyOfUser?: MUserId
1233 }, countVideos = true) { 1245 }, countVideos = true) {
1234 if (options.filter && options.filter === 'all-local' && !options.user.hasRight(UserRight.SEE_ALL_VIDEOS)) { 1246 if (options.filter && options.filter === 'all-local' && !options.user.hasRight(UserRight.SEE_ALL_VIDEOS)) {
1235 throw new Error('Try to filter all-local but no user has not the see all videos right') 1247 throw new Error('Try to filter all-local but no user has not the see all videos right')
@@ -1294,7 +1306,7 @@ export class VideoModel extends Model<VideoModel> {
1294 tagsAllOf?: string[] 1306 tagsAllOf?: string[]
1295 durationMin?: number // seconds 1307 durationMin?: number // seconds
1296 durationMax?: number // seconds 1308 durationMax?: number // seconds
1297 user?: UserModel, 1309 user?: MUserAccountId,
1298 filter?: VideoFilter 1310 filter?: VideoFilter
1299 }) { 1311 }) {
1300 const whereAnd = [] 1312 const whereAnd = []
@@ -1387,7 +1399,7 @@ export class VideoModel extends Model<VideoModel> {
1387 return VideoModel.getAvailableForApi(query, queryOptions) 1399 return VideoModel.getAvailableForApi(query, queryOptions)
1388 } 1400 }
1389 1401
1390 static load (id: number | string, t?: Transaction) { 1402 static load (id: number | string, t?: Transaction): Bluebird<MVideoThumbnail> {
1391 const where = buildWhereIdOrUUID(id) 1403 const where = buildWhereIdOrUUID(id)
1392 const options = { 1404 const options = {
1393 where, 1405 where,
@@ -1397,7 +1409,7 @@ export class VideoModel extends Model<VideoModel> {
1397 return VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findOne(options) 1409 return VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findOne(options)
1398 } 1410 }
1399 1411
1400 static loadWithRights (id: number | string, t?: Transaction) { 1412 static loadWithRights (id: number | string, t?: Transaction): Bluebird<MVideoWithRights> {
1401 const where = buildWhereIdOrUUID(id) 1413 const where = buildWhereIdOrUUID(id)
1402 const options = { 1414 const options = {
1403 where, 1415 where,
@@ -1411,7 +1423,7 @@ export class VideoModel extends Model<VideoModel> {
1411 ]).findOne(options) 1423 ]).findOne(options)
1412 } 1424 }
1413 1425
1414 static loadOnlyId (id: number | string, t?: Transaction) { 1426 static loadOnlyId (id: number | string, t?: Transaction): Bluebird<MVideoIdThumbnail> {
1415 const where = buildWhereIdOrUUID(id) 1427 const where = buildWhereIdOrUUID(id)
1416 1428
1417 const options = { 1429 const options = {
@@ -1423,7 +1435,7 @@ export class VideoModel extends Model<VideoModel> {
1423 return VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findOne(options) 1435 return VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findOne(options)
1424 } 1436 }
1425 1437
1426 static loadWithFiles (id: number | string, t?: Transaction, logging?: boolean) { 1438 static loadWithFiles (id: number | string, t?: Transaction, logging?: boolean): Bluebird<MVideoWithAllFiles> {
1427 const where = buildWhereIdOrUUID(id) 1439 const where = buildWhereIdOrUUID(id)
1428 1440
1429 const query = { 1441 const query = {
@@ -1439,7 +1451,7 @@ export class VideoModel extends Model<VideoModel> {
1439 ]).findOne(query) 1451 ]).findOne(query)
1440 } 1452 }
1441 1453
1442 static loadByUUID (uuid: string) { 1454 static loadByUUID (uuid: string): Bluebird<MVideoThumbnail> {
1443 const options = { 1455 const options = {
1444 where: { 1456 where: {
1445 uuid 1457 uuid
@@ -1449,7 +1461,7 @@ export class VideoModel extends Model<VideoModel> {
1449 return VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findOne(options) 1461 return VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findOne(options)
1450 } 1462 }
1451 1463
1452 static loadByUrl (url: string, transaction?: Transaction) { 1464 static loadByUrl (url: string, transaction?: Transaction): Bluebird<MVideoThumbnail> {
1453 const query: FindOptions = { 1465 const query: FindOptions = {
1454 where: { 1466 where: {
1455 url 1467 url
@@ -1460,7 +1472,7 @@ export class VideoModel extends Model<VideoModel> {
1460 return VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findOne(query) 1472 return VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findOne(query)
1461 } 1473 }
1462 1474
1463 static loadByUrlAndPopulateAccount (url: string, transaction?: Transaction) { 1475 static loadByUrlAndPopulateAccount (url: string, transaction?: Transaction): Bluebird<MVideoAccountLightBlacklistAllFiles> {
1464 const query: FindOptions = { 1476 const query: FindOptions = {
1465 where: { 1477 where: {
1466 url 1478 url
@@ -1472,11 +1484,12 @@ export class VideoModel extends Model<VideoModel> {
1472 ScopeNames.WITH_ACCOUNT_DETAILS, 1484 ScopeNames.WITH_ACCOUNT_DETAILS,
1473 ScopeNames.WITH_FILES, 1485 ScopeNames.WITH_FILES,
1474 ScopeNames.WITH_STREAMING_PLAYLISTS, 1486 ScopeNames.WITH_STREAMING_PLAYLISTS,
1475 ScopeNames.WITH_THUMBNAILS 1487 ScopeNames.WITH_THUMBNAILS,
1488 ScopeNames.WITH_BLACKLISTED
1476 ]).findOne(query) 1489 ]).findOne(query)
1477 } 1490 }
1478 1491
1479 static loadAndPopulateAccountAndServerAndTags (id: number | string, t?: Transaction, userId?: number) { 1492 static loadAndPopulateAccountAndServerAndTags (id: number | string, t?: Transaction, userId?: number): Bluebird<MVideoFullLight> {
1480 const where = buildWhereIdOrUUID(id) 1493 const where = buildWhereIdOrUUID(id)
1481 1494
1482 const options = { 1495 const options = {
@@ -1508,7 +1521,7 @@ export class VideoModel extends Model<VideoModel> {
1508 id: number | string, 1521 id: number | string,
1509 t?: Transaction, 1522 t?: Transaction,
1510 userId?: number 1523 userId?: number
1511 }) { 1524 }): Bluebird<MVideoDetails> {
1512 const { id, t, userId } = parameters 1525 const { id, t, userId } = parameters
1513 const where = buildWhereIdOrUUID(id) 1526 const where = buildWhereIdOrUUID(id)
1514 1527
@@ -1586,7 +1599,7 @@ export class VideoModel extends Model<VideoModel> {
1586 .then(results => results.length === 1) 1599 .then(results => results.length === 1)
1587 } 1600 }
1588 1601
1589 static bulkUpdateSupportField (videoChannel: VideoChannelModel, t: Transaction) { 1602 static bulkUpdateSupportField (videoChannel: MChannel, t: Transaction) {
1590 const options = { 1603 const options = {
1591 where: { 1604 where: {
1592 channelId: videoChannel.id 1605 channelId: videoChannel.id
@@ -1597,7 +1610,7 @@ export class VideoModel extends Model<VideoModel> {
1597 return VideoModel.update({ support: videoChannel.support }, options) 1610 return VideoModel.update({ support: videoChannel.support }, options)
1598 } 1611 }
1599 1612
1600 static getAllIdsFromChannel (videoChannel: VideoChannelModel) { 1613 static getAllIdsFromChannel (videoChannel: MChannelId): Bluebird<number[]> {
1601 const query = { 1614 const query = {
1602 attributes: [ 'id' ], 1615 attributes: [ 'id' ],
1603 where: { 1616 where: {
@@ -1756,20 +1769,20 @@ export class VideoModel extends Model<VideoModel> {
1756 this.VideoChannel.Account.isBlocked() 1769 this.VideoChannel.Account.isBlocked()
1757 } 1770 }
1758 1771
1759 getOriginalFile () { 1772 getOriginalFile <T extends MVideoWithFile> (this: T) {
1760 if (Array.isArray(this.VideoFiles) === false) return undefined 1773 if (Array.isArray(this.VideoFiles) === false) return undefined
1761 1774
1762 // The original file is the file that have the higher resolution 1775 // The original file is the file that have the higher resolution
1763 return maxBy(this.VideoFiles, file => file.resolution) 1776 return maxBy(this.VideoFiles, file => file.resolution)
1764 } 1777 }
1765 1778
1766 getFile (resolution: number) { 1779 getFile <T extends MVideoWithFile> (this: T, resolution: number) {
1767 if (Array.isArray(this.VideoFiles) === false) return undefined 1780 if (Array.isArray(this.VideoFiles) === false) return undefined
1768 1781
1769 return this.VideoFiles.find(f => f.resolution === resolution) 1782 return this.VideoFiles.find(f => f.resolution === resolution)
1770 } 1783 }
1771 1784
1772 async addAndSaveThumbnail (thumbnail: ThumbnailModel, transaction: Transaction) { 1785 async addAndSaveThumbnail (thumbnail: MThumbnail, transaction: Transaction) {
1773 thumbnail.videoId = this.id 1786 thumbnail.videoId = this.id
1774 1787
1775 const savedThumbnail = await thumbnail.save({ transaction }) 1788 const savedThumbnail = await thumbnail.save({ transaction })
@@ -1782,7 +1795,7 @@ export class VideoModel extends Model<VideoModel> {
1782 this.Thumbnails.push(savedThumbnail) 1795 this.Thumbnails.push(savedThumbnail)
1783 } 1796 }
1784 1797
1785 getVideoFilename (videoFile: VideoFileModel) { 1798 getVideoFilename (videoFile: MVideoFile) {
1786 return this.uuid + '-' + videoFile.resolution + videoFile.extname 1799 return this.uuid + '-' + videoFile.resolution + videoFile.extname
1787 } 1800 }
1788 1801
@@ -1806,7 +1819,7 @@ export class VideoModel extends Model<VideoModel> {
1806 return this.Thumbnails.find(t => t.type === ThumbnailType.PREVIEW) 1819 return this.Thumbnails.find(t => t.type === ThumbnailType.PREVIEW)
1807 } 1820 }
1808 1821
1809 getTorrentFileName (videoFile: VideoFileModel) { 1822 getTorrentFileName (videoFile: MVideoFile) {
1810 const extension = '.torrent' 1823 const extension = '.torrent'
1811 return this.uuid + '-' + videoFile.resolution + extension 1824 return this.uuid + '-' + videoFile.resolution + extension
1812 } 1825 }
@@ -1815,15 +1828,15 @@ export class VideoModel extends Model<VideoModel> {
1815 return this.remote === false 1828 return this.remote === false
1816 } 1829 }
1817 1830
1818 getTorrentFilePath (videoFile: VideoFileModel) { 1831 getTorrentFilePath (videoFile: MVideoFile) {
1819 return join(CONFIG.STORAGE.TORRENTS_DIR, this.getTorrentFileName(videoFile)) 1832 return join(CONFIG.STORAGE.TORRENTS_DIR, this.getTorrentFileName(videoFile))
1820 } 1833 }
1821 1834
1822 getVideoFilePath (videoFile: VideoFileModel) { 1835 getVideoFilePath (videoFile: MVideoFile) {
1823 return join(CONFIG.STORAGE.VIDEOS_DIR, this.getVideoFilename(videoFile)) 1836 return join(CONFIG.STORAGE.VIDEOS_DIR, this.getVideoFilename(videoFile))
1824 } 1837 }
1825 1838
1826 async createTorrentAndSetInfoHash (videoFile: VideoFileModel) { 1839 async createTorrentAndSetInfoHash (videoFile: MVideoFile) {
1827 const options = { 1840 const options = {
1828 // Keep the extname, it's used by the client to stream the file inside a web browser 1841 // Keep the extname, it's used by the client to stream the file inside a web browser
1829 name: `${this.name} ${videoFile.resolution}p${videoFile.extname}`, 1842 name: `${this.name} ${videoFile.resolution}p${videoFile.extname}`,
@@ -1869,11 +1882,11 @@ export class VideoModel extends Model<VideoModel> {
1869 return join(LAZY_STATIC_PATHS.PREVIEWS, preview.filename) 1882 return join(LAZY_STATIC_PATHS.PREVIEWS, preview.filename)
1870 } 1883 }
1871 1884
1872 toFormattedJSON (options?: VideoFormattingJSONOptions): Video { 1885 toFormattedJSON (this: MVideoFormattable, options?: VideoFormattingJSONOptions): Video {
1873 return videoModelToFormattedJSON(this, options) 1886 return videoModelToFormattedJSON(this, options)
1874 } 1887 }
1875 1888
1876 toFormattedDetailsJSON (): VideoDetails { 1889 toFormattedDetailsJSON (this: MVideoFormattableDetails): VideoDetails {
1877 return videoModelToFormattedDetailsJSON(this) 1890 return videoModelToFormattedDetailsJSON(this)
1878 } 1891 }
1879 1892
@@ -1881,7 +1894,7 @@ export class VideoModel extends Model<VideoModel> {
1881 return videoFilesModelToFormattedJSON(this, this.VideoFiles) 1894 return videoFilesModelToFormattedJSON(this, this.VideoFiles)
1882 } 1895 }
1883 1896
1884 toActivityPubObject (): VideoTorrentObject { 1897 toActivityPubObject (this: MVideoAP): VideoTorrentObject {
1885 return videoModelToActivityPubObject(this) 1898 return videoModelToActivityPubObject(this)
1886 } 1899 }
1887 1900
@@ -1908,7 +1921,7 @@ export class VideoModel extends Model<VideoModel> {
1908 return this.VideoStreamingPlaylists.find(p => p.type === VideoStreamingPlaylistType.HLS) 1921 return this.VideoStreamingPlaylists.find(p => p.type === VideoStreamingPlaylistType.HLS)
1909 } 1922 }
1910 1923
1911 removeFile (videoFile: VideoFileModel, isRedundancy = false) { 1924 removeFile (videoFile: MVideoFile, isRedundancy = false) {
1912 const baseDir = isRedundancy ? CONFIG.STORAGE.REDUNDANCY_DIR : CONFIG.STORAGE.VIDEOS_DIR 1925 const baseDir = isRedundancy ? CONFIG.STORAGE.REDUNDANCY_DIR : CONFIG.STORAGE.VIDEOS_DIR
1913 1926
1914 const filePath = join(baseDir, this.getVideoFilename(videoFile)) 1927 const filePath = join(baseDir, this.getVideoFilename(videoFile))
@@ -1916,7 +1929,7 @@ export class VideoModel extends Model<VideoModel> {
1916 .catch(err => logger.warn('Cannot delete file %s.', filePath, { err })) 1929 .catch(err => logger.warn('Cannot delete file %s.', filePath, { err }))
1917 } 1930 }
1918 1931
1919 removeTorrent (videoFile: VideoFileModel) { 1932 removeTorrent (videoFile: MVideoFile) {
1920 const torrentPath = join(CONFIG.STORAGE.TORRENTS_DIR, this.getTorrentFileName(videoFile)) 1933 const torrentPath = join(CONFIG.STORAGE.TORRENTS_DIR, this.getTorrentFileName(videoFile))
1921 return remove(torrentPath) 1934 return remove(torrentPath)
1922 .catch(err => logger.warn('Cannot delete torrent %s.', torrentPath, { err })) 1935 .catch(err => logger.warn('Cannot delete torrent %s.', torrentPath, { err }))
@@ -1957,7 +1970,7 @@ export class VideoModel extends Model<VideoModel> {
1957 return { baseUrlHttp, baseUrlWs } 1970 return { baseUrlHttp, baseUrlWs }
1958 } 1971 }
1959 1972
1960 generateMagnetUri (videoFile: VideoFileModel, baseUrlHttp: string, baseUrlWs: string) { 1973 generateMagnetUri (videoFile: MVideoFileRedundanciesOpt, baseUrlHttp: string, baseUrlWs: string) {
1961 const xs = this.getTorrentUrl(videoFile, baseUrlHttp) 1974 const xs = this.getTorrentUrl(videoFile, baseUrlHttp)
1962 const announce = this.getTrackerUrls(baseUrlHttp, baseUrlWs) 1975 const announce = this.getTrackerUrls(baseUrlHttp, baseUrlWs)
1963 let urlList = [ this.getVideoFileUrl(videoFile, baseUrlHttp) ] 1976 let urlList = [ this.getVideoFileUrl(videoFile, baseUrlHttp) ]
@@ -1980,27 +1993,27 @@ export class VideoModel extends Model<VideoModel> {
1980 return [ baseUrlWs + '/tracker/socket', baseUrlHttp + '/tracker/announce' ] 1993 return [ baseUrlWs + '/tracker/socket', baseUrlHttp + '/tracker/announce' ]
1981 } 1994 }
1982 1995
1983 getTorrentUrl (videoFile: VideoFileModel, baseUrlHttp: string) { 1996 getTorrentUrl (videoFile: MVideoFile, baseUrlHttp: string) {
1984 return baseUrlHttp + STATIC_PATHS.TORRENTS + this.getTorrentFileName(videoFile) 1997 return baseUrlHttp + STATIC_PATHS.TORRENTS + this.getTorrentFileName(videoFile)
1985 } 1998 }
1986 1999
1987 getTorrentDownloadUrl (videoFile: VideoFileModel, baseUrlHttp: string) { 2000 getTorrentDownloadUrl (videoFile: MVideoFile, baseUrlHttp: string) {
1988 return baseUrlHttp + STATIC_DOWNLOAD_PATHS.TORRENTS + this.getTorrentFileName(videoFile) 2001 return baseUrlHttp + STATIC_DOWNLOAD_PATHS.TORRENTS + this.getTorrentFileName(videoFile)
1989 } 2002 }
1990 2003
1991 getVideoFileUrl (videoFile: VideoFileModel, baseUrlHttp: string) { 2004 getVideoFileUrl (videoFile: MVideoFile, baseUrlHttp: string) {
1992 return baseUrlHttp + STATIC_PATHS.WEBSEED + this.getVideoFilename(videoFile) 2005 return baseUrlHttp + STATIC_PATHS.WEBSEED + this.getVideoFilename(videoFile)
1993 } 2006 }
1994 2007
1995 getVideoRedundancyUrl (videoFile: VideoFileModel, baseUrlHttp: string) { 2008 getVideoRedundancyUrl (videoFile: MVideoFile, baseUrlHttp: string) {
1996 return baseUrlHttp + STATIC_PATHS.REDUNDANCY + this.getVideoFilename(videoFile) 2009 return baseUrlHttp + STATIC_PATHS.REDUNDANCY + this.getVideoFilename(videoFile)
1997 } 2010 }
1998 2011
1999 getVideoFileDownloadUrl (videoFile: VideoFileModel, baseUrlHttp: string) { 2012 getVideoFileDownloadUrl (videoFile: MVideoFile, baseUrlHttp: string) {
2000 return baseUrlHttp + STATIC_DOWNLOAD_PATHS.VIDEOS + this.getVideoFilename(videoFile) 2013 return baseUrlHttp + STATIC_DOWNLOAD_PATHS.VIDEOS + this.getVideoFilename(videoFile)
2001 } 2014 }
2002 2015
2003 getBandwidthBits (videoFile: VideoFileModel) { 2016 getBandwidthBits (videoFile: MVideoFile) {
2004 return Math.ceil((videoFile.size * 8) / this.duration) 2017 return Math.ceil((videoFile.size * 8) / this.duration)
2005 } 2018 }
2006} 2019}