aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/models
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2020-01-31 16:56:52 +0100
committerChocobozzz <me@florianbigard.com>2020-02-03 08:31:02 +0100
commita15871560f80e07386c1dabb8370cd2664ecfd1f (patch)
tree44440e140c9e43b0d7f97ade777a76e649e0553d /server/models
parenta22046d166805222ca76060e471b6cb3d419a32d (diff)
downloadPeerTube-a15871560f80e07386c1dabb8370cd2664ecfd1f.tar.gz
PeerTube-a15871560f80e07386c1dabb8370cd2664ecfd1f.tar.zst
PeerTube-a15871560f80e07386c1dabb8370cd2664ecfd1f.zip
Move to eslintcontain
Diffstat (limited to 'server/models')
-rw-r--r--server/models/account/account-video-rate.ts12
-rw-r--r--server/models/account/account.ts8
-rw-r--r--server/models/account/user-notification.ts2
-rw-r--r--server/models/account/user.ts86
-rw-r--r--server/models/activitypub/actor-follow.ts53
-rw-r--r--server/models/activitypub/actor.ts12
-rw-r--r--server/models/oauth/oauth-token.ts4
-rw-r--r--server/models/redundancy/video-redundancy.ts58
-rw-r--r--server/models/server/plugin.ts8
-rw-r--r--server/models/utils.ts4
-rw-r--r--server/models/video/thumbnail.ts2
-rw-r--r--server/models/video/video-abuse.ts6
-rw-r--r--server/models/video/video-caption.ts7
-rw-r--r--server/models/video/video-channel.ts10
-rw-r--r--server/models/video/video-comment.ts20
-rw-r--r--server/models/video/video-format-utils.ts19
-rw-r--r--server/models/video/video-playlist-element.ts8
-rw-r--r--server/models/video/video-playlist.ts34
-rw-r--r--server/models/video/video.ts165
19 files changed, 260 insertions, 258 deletions
diff --git a/server/models/account/account-video-rate.ts b/server/models/account/account-video-rate.ts
index c593595b2..8aeb486d1 100644
--- a/server/models/account/account-video-rate.ts
+++ b/server/models/account/account-video-rate.ts
@@ -99,7 +99,7 @@ export class AccountVideoRateModel extends Model<AccountVideoRateModel> {
99 static loadByAccountAndVideoOrUrl (accountId: number, videoId: number, url: string, t?: Transaction): Bluebird<MAccountVideoRate> { 99 static loadByAccountAndVideoOrUrl (accountId: number, videoId: number, url: string, t?: Transaction): Bluebird<MAccountVideoRate> {
100 const options: FindOptions = { 100 const options: FindOptions = {
101 where: { 101 where: {
102 [ Op.or]: [ 102 [Op.or]: [
103 { 103 {
104 accountId, 104 accountId,
105 videoId 105 videoId
@@ -116,10 +116,10 @@ export class AccountVideoRateModel extends Model<AccountVideoRateModel> {
116 } 116 }
117 117
118 static listByAccountForApi (options: { 118 static listByAccountForApi (options: {
119 start: number, 119 start: number
120 count: number, 120 count: number
121 sort: string, 121 sort: string
122 type?: string, 122 type?: string
123 accountId: number 123 accountId: number
124 }) { 124 }) {
125 const query: FindOptions = { 125 const query: FindOptions = {
@@ -135,7 +135,7 @@ export class AccountVideoRateModel extends Model<AccountVideoRateModel> {
135 required: true, 135 required: true,
136 include: [ 136 include: [
137 { 137 {
138 model: VideoChannelModel.scope({ method: [VideoChannelScopeNames.SUMMARY, { withAccount: true } as SummaryOptions ] }), 138 model: VideoChannelModel.scope({ method: [ VideoChannelScopeNames.SUMMARY, { withAccount: true } as SummaryOptions ] }),
139 required: true 139 required: true
140 } 140 }
141 ] 141 ]
diff --git a/server/models/account/account.ts b/server/models/account/account.ts
index 8a0ffeb63..0905a0fb2 100644
--- a/server/models/account/account.ts
+++ b/server/models/account/account.ts
@@ -53,7 +53,7 @@ export type SummaryOptions = {
53 ] 53 ]
54})) 54}))
55@Scopes(() => ({ 55@Scopes(() => ({
56 [ ScopeNames.SUMMARY ]: (options: SummaryOptions = {}) => { 56 [ScopeNames.SUMMARY]: (options: SummaryOptions = {}) => {
57 const whereActor = options.whereActor || undefined 57 const whereActor = options.whereActor || undefined
58 58
59 const serverInclude: IncludeOptions = { 59 const serverInclude: IncludeOptions = {
@@ -254,15 +254,15 @@ export class AccountModel extends Model<AccountModel> {
254 254
255 const query = { 255 const query = {
256 where: { 256 where: {
257 [ Op.or ]: [ 257 [Op.or]: [
258 { 258 {
259 userId: { 259 userId: {
260 [ Op.ne ]: null 260 [Op.ne]: null
261 } 261 }
262 }, 262 },
263 { 263 {
264 applicationId: { 264 applicationId: {
265 [ Op.ne ]: null 265 [Op.ne]: null
266 } 266 }
267 } 267 }
268 ] 268 ]
diff --git a/server/models/account/user-notification.ts b/server/models/account/user-notification.ts
index f649b8f95..5a725187a 100644
--- a/server/models/account/user-notification.ts
+++ b/server/models/account/user-notification.ts
@@ -379,7 +379,7 @@ export class UserNotificationModel extends Model<UserNotificationModel> {
379 379
380 toFormattedJSON (this: UserNotificationModelForApi): UserNotification { 380 toFormattedJSON (this: UserNotificationModelForApi): UserNotification {
381 const video = this.Video 381 const video = this.Video
382 ? Object.assign(this.formatVideo(this.Video),{ channel: this.formatActor(this.Video.VideoChannel) }) 382 ? Object.assign(this.formatVideo(this.Video), { channel: this.formatActor(this.Video.VideoChannel) })
383 : undefined 383 : undefined
384 384
385 const videoImport = this.VideoImport ? { 385 const videoImport = this.VideoImport ? {
diff --git a/server/models/account/user.ts b/server/models/account/user.ts
index 4c2c5e278..fb4c15aef 100644
--- a/server/models/account/user.ts
+++ b/server/models/account/user.ts
@@ -1,4 +1,4 @@
1import { FindOptions, literal, Op, QueryTypes, where, fn, col } from 'sequelize' 1import { FindOptions, literal, Op, QueryTypes, where, fn, col, WhereOptions } from 'sequelize'
2import { 2import {
3 AfterDestroy, 3 AfterDestroy,
4 AfterUpdate, 4 AfterUpdate,
@@ -101,7 +101,7 @@ enum ScopeNames {
101 required: true, 101 required: true,
102 where: { 102 where: {
103 type: { 103 type: {
104 [ Op.ne ]: VideoPlaylistType.REGULAR 104 [Op.ne]: VideoPlaylistType.REGULAR
105 } 105 }
106 } 106 }
107 } 107 }
@@ -186,7 +186,10 @@ export class UserModel extends Model<UserModel> {
186 186
187 @AllowNull(false) 187 @AllowNull(false)
188 @Default(true) 188 @Default(true)
189 @Is('UserAutoPlayNextVideoPlaylist', value => throwIfNotValid(value, isUserAutoPlayNextVideoPlaylistValid, 'auto play next video for playlists boolean')) 189 @Is(
190 'UserAutoPlayNextVideoPlaylist',
191 value => throwIfNotValid(value, isUserAutoPlayNextVideoPlaylistValid, 'auto play next video for playlists boolean')
192 )
190 @Column 193 @Column
191 autoPlayNextVideoPlaylist: boolean 194 autoPlayNextVideoPlaylist: boolean
192 195
@@ -308,7 +311,8 @@ export class UserModel extends Model<UserModel> {
308 } 311 }
309 312
310 static listForApi (start: number, count: number, sort: string, search?: string) { 313 static listForApi (start: number, count: number, sort: string, search?: string) {
311 let where = undefined 314 let where: WhereOptions
315
312 if (search) { 316 if (search) {
313 where = { 317 where = {
314 [Op.or]: [ 318 [Op.or]: [
@@ -319,7 +323,7 @@ export class UserModel extends Model<UserModel> {
319 }, 323 },
320 { 324 {
321 username: { 325 username: {
322 [ Op.iLike ]: '%' + search + '%' 326 [Op.iLike]: '%' + search + '%'
323 } 327 }
324 } 328 }
325 ] 329 ]
@@ -332,14 +336,14 @@ export class UserModel extends Model<UserModel> {
332 [ 336 [
333 literal( 337 literal(
334 '(' + 338 '(' +
335 'SELECT COALESCE(SUM("size"), 0) ' + 339 'SELECT COALESCE(SUM("size"), 0) ' +
336 'FROM (' + 340 'FROM (' +
337 'SELECT MAX("videoFile"."size") AS "size" FROM "videoFile" ' + 341 'SELECT MAX("videoFile"."size") AS "size" FROM "videoFile" ' +
338 'INNER JOIN "video" ON "videoFile"."videoId" = "video"."id" ' + 342 'INNER JOIN "video" ON "videoFile"."videoId" = "video"."id" ' +
339 'INNER JOIN "videoChannel" ON "videoChannel"."id" = "video"."channelId" ' + 343 'INNER JOIN "videoChannel" ON "videoChannel"."id" = "video"."channelId" ' +
340 'INNER JOIN "account" ON "videoChannel"."accountId" = "account"."id" ' + 344 'INNER JOIN "account" ON "videoChannel"."accountId" = "account"."id" ' +
341 'WHERE "account"."userId" = "UserModel"."id" GROUP BY "video"."id"' + 345 'WHERE "account"."userId" = "UserModel"."id" GROUP BY "video"."id"' +
342 ') t' + 346 ') t' +
343 ')' 347 ')'
344 ), 348 ),
345 'videoQuotaUsed' 349 'videoQuotaUsed'
@@ -353,18 +357,18 @@ export class UserModel extends Model<UserModel> {
353 } 357 }
354 358
355 return UserModel.findAndCountAll(query) 359 return UserModel.findAndCountAll(query)
356 .then(({ rows, count }) => { 360 .then(({ rows, count }) => {
357 return { 361 return {
358 data: rows, 362 data: rows,
359 total: count 363 total: count
360 } 364 }
361 }) 365 })
362 } 366 }
363 367
364 static listWithRight (right: UserRight): Bluebird<MUserDefault[]> { 368 static listWithRight (right: UserRight): Bluebird<MUserDefault[]> {
365 const roles = Object.keys(USER_ROLE_LABELS) 369 const roles = Object.keys(USER_ROLE_LABELS)
366 .map(k => parseInt(k, 10) as UserRole) 370 .map(k => parseInt(k, 10) as UserRole)
367 .filter(role => hasUserRight(role, right)) 371 .filter(role => hasUserRight(role, right))
368 372
369 const query = { 373 const query = {
370 where: { 374 where: {
@@ -390,7 +394,7 @@ export class UserModel extends Model<UserModel> {
390 required: true, 394 required: true,
391 include: [ 395 include: [
392 { 396 {
393 attributes: [ ], 397 attributes: [],
394 model: ActorModel.unscoped(), 398 model: ActorModel.unscoped(),
395 required: true, 399 required: true,
396 where: { 400 where: {
@@ -398,7 +402,7 @@ export class UserModel extends Model<UserModel> {
398 }, 402 },
399 include: [ 403 include: [
400 { 404 {
401 attributes: [ ], 405 attributes: [],
402 as: 'ActorFollowings', 406 as: 'ActorFollowings',
403 model: ActorFollowModel.unscoped(), 407 model: ActorFollowModel.unscoped(),
404 required: true, 408 required: true,
@@ -433,7 +437,7 @@ export class UserModel extends Model<UserModel> {
433 static loadByUsername (username: string): Bluebird<MUserDefault> { 437 static loadByUsername (username: string): Bluebird<MUserDefault> {
434 const query = { 438 const query = {
435 where: { 439 where: {
436 username: { [ Op.iLike ]: username } 440 username: { [Op.iLike]: username }
437 } 441 }
438 } 442 }
439 443
@@ -443,7 +447,7 @@ export class UserModel extends Model<UserModel> {
443 static loadForMeAPI (username: string): Bluebird<MUserNotifSettingChannelDefault> { 447 static loadForMeAPI (username: string): Bluebird<MUserNotifSettingChannelDefault> {
444 const query = { 448 const query = {
445 where: { 449 where: {
446 username: { [ Op.iLike ]: username } 450 username: { [Op.iLike]: username }
447 } 451 }
448 } 452 }
449 453
@@ -465,7 +469,7 @@ export class UserModel extends Model<UserModel> {
465 469
466 const query = { 470 const query = {
467 where: { 471 where: {
468 [ Op.or ]: [ 472 [Op.or]: [
469 where(fn('lower', col('username')), fn('lower', username)), 473 where(fn('lower', col('username')), fn('lower', username)),
470 474
471 { email } 475 { email }
@@ -592,7 +596,7 @@ export class UserModel extends Model<UserModel> {
592 const query = { 596 const query = {
593 where: { 597 where: {
594 username: { 598 username: {
595 [ Op.like ]: `%${search}%` 599 [Op.like]: `%${search}%`
596 } 600 }
597 }, 601 },
598 limit: 10 602 limit: 10
@@ -652,7 +656,7 @@ export class UserModel extends Model<UserModel> {
652 videoLanguages: this.videoLanguages, 656 videoLanguages: this.videoLanguages,
653 657
654 role: this.role, 658 role: this.role,
655 roleLabel: USER_ROLE_LABELS[ this.role ], 659 roleLabel: USER_ROLE_LABELS[this.role],
656 660
657 videoQuota: this.videoQuota, 661 videoQuota: this.videoQuota,
658 videoQuotaDaily: this.videoQuotaDaily, 662 videoQuotaDaily: this.videoQuotaDaily,
@@ -686,13 +690,13 @@ export class UserModel extends Model<UserModel> {
686 690
687 if (Array.isArray(this.Account.VideoChannels) === true) { 691 if (Array.isArray(this.Account.VideoChannels) === true) {
688 json.videoChannels = this.Account.VideoChannels 692 json.videoChannels = this.Account.VideoChannels
689 .map(c => c.toFormattedJSON()) 693 .map(c => c.toFormattedJSON())
690 .sort((v1, v2) => { 694 .sort((v1, v2) => {
691 if (v1.createdAt < v2.createdAt) return -1 695 if (v1.createdAt < v2.createdAt) return -1
692 if (v1.createdAt === v2.createdAt) return 0 696 if (v1.createdAt === v2.createdAt) return 0
693 697
694 return 1 698 return 1
695 }) 699 })
696 } 700 }
697 701
698 return json 702 return json
@@ -702,7 +706,7 @@ export class UserModel extends Model<UserModel> {
702 const formatted = this.toFormattedJSON() 706 const formatted = this.toFormattedJSON()
703 707
704 const specialPlaylists = this.Account.VideoPlaylists 708 const specialPlaylists = this.Account.VideoPlaylists
705 .map(p => ({ id: p.id, name: p.name, type: p.type })) 709 .map(p => ({ id: p.id, name: p.name, type: p.type }))
706 710
707 return Object.assign(formatted, { specialPlaylists }) 711 return Object.assign(formatted, { specialPlaylists })
708 } 712 }
@@ -729,12 +733,12 @@ export class UserModel extends Model<UserModel> {
729 733
730 return 'SELECT SUM("size") AS "total" ' + 734 return 'SELECT SUM("size") AS "total" ' +
731 'FROM (' + 735 'FROM (' +
732 'SELECT MAX("videoFile"."size") AS "size" FROM "videoFile" ' + 736 'SELECT MAX("videoFile"."size") AS "size" FROM "videoFile" ' +
733 'INNER JOIN "video" ON "videoFile"."videoId" = "video"."id" ' + 737 'INNER JOIN "video" ON "videoFile"."videoId" = "video"."id" ' +
734 'INNER JOIN "videoChannel" ON "videoChannel"."id" = "video"."channelId" ' + 738 'INNER JOIN "videoChannel" ON "videoChannel"."id" = "video"."channelId" ' +
735 'INNER JOIN "account" ON "videoChannel"."accountId" = "account"."id" ' + 739 'INNER JOIN "account" ON "videoChannel"."accountId" = "account"."id" ' +
736 'WHERE "account"."userId" = $userId ' + andWhere + 740 'WHERE "account"."userId" = $userId ' + andWhere +
737 'GROUP BY "video"."id"' + 741 'GROUP BY "video"."id"' +
738 ') t' 742 ') t'
739 } 743 }
740 744
diff --git a/server/models/activitypub/actor-follow.ts b/server/models/activitypub/actor-follow.ts
index f21d2b8a2..27643704e 100644
--- a/server/models/activitypub/actor-follow.ts
+++ b/server/models/activitypub/actor-follow.ts
@@ -1,5 +1,5 @@
1import * as Bluebird from 'bluebird' 1import * as Bluebird from 'bluebird'
2import { values, difference } from 'lodash' 2import { difference, values } from 'lodash'
3import { 3import {
4 AfterCreate, 4 AfterCreate,
5 AfterDestroy, 5 AfterDestroy,
@@ -23,7 +23,7 @@ import { logger } from '../../helpers/logger'
23import { getServerActor } from '../../helpers/utils' 23import { getServerActor } from '../../helpers/utils'
24import { ACTOR_FOLLOW_SCORE, FOLLOW_STATES, SERVER_ACTOR_NAME } from '../../initializers/constants' 24import { ACTOR_FOLLOW_SCORE, FOLLOW_STATES, SERVER_ACTOR_NAME } from '../../initializers/constants'
25import { ServerModel } from '../server/server' 25import { ServerModel } from '../server/server'
26import { createSafeIn, getSort, getFollowsSort } from '../utils' 26import { createSafeIn, getFollowsSort, getSort } from '../utils'
27import { ActorModel, unusedActorAttributesForAPI } from './actor' 27import { ActorModel, unusedActorAttributesForAPI } from './actor'
28import { VideoChannelModel } from '../video/video-channel' 28import { VideoChannelModel } from '../video/video-channel'
29import { AccountModel } from '../account/account' 29import { AccountModel } from '../account/account'
@@ -36,7 +36,6 @@ import {
36 MActorFollowSubscriptions 36 MActorFollowSubscriptions
37} from '@server/typings/models' 37} from '@server/typings/models'
38import { ActivityPubActorType } from '@shared/models' 38import { ActivityPubActorType } from '@shared/models'
39import { afterCommitIfTransaction } from '@server/helpers/database-utils'
40 39
41@Table({ 40@Table({
42 tableName: 'actorFollow', 41 tableName: 'actorFollow',
@@ -226,7 +225,7 @@ export class ActorFollowModel extends Model<ActorFollowModel> {
226 225
227 return ActorFollowModel.findOne(query) 226 return ActorFollowModel.findOne(query)
228 .then(result => { 227 .then(result => {
229 if (result && result.ActorFollowing.VideoChannel) { 228 if (result?.ActorFollowing.VideoChannel) {
230 result.ActorFollowing.VideoChannel.Actor = result.ActorFollowing 229 result.ActorFollowing.VideoChannel.Actor = result.ActorFollowing
231 } 230 }
232 231
@@ -239,24 +238,24 @@ export class ActorFollowModel extends Model<ActorFollowModel> {
239 .map(t => { 238 .map(t => {
240 if (t.host) { 239 if (t.host) {
241 return { 240 return {
242 [ Op.and ]: [ 241 [Op.and]: [
243 { 242 {
244 '$preferredUsername$': t.name 243 $preferredUsername$: t.name
245 }, 244 },
246 { 245 {
247 '$host$': t.host 246 $host$: t.host
248 } 247 }
249 ] 248 ]
250 } 249 }
251 } 250 }
252 251
253 return { 252 return {
254 [ Op.and ]: [ 253 [Op.and]: [
255 { 254 {
256 '$preferredUsername$': t.name 255 $preferredUsername$: t.name
257 }, 256 },
258 { 257 {
259 '$serverId$': null 258 $serverId$: null
260 } 259 }
261 ] 260 ]
262 } 261 }
@@ -265,9 +264,9 @@ export class ActorFollowModel extends Model<ActorFollowModel> {
265 const query = { 264 const query = {
266 attributes: [], 265 attributes: [],
267 where: { 266 where: {
268 [ Op.and ]: [ 267 [Op.and]: [
269 { 268 {
270 [ Op.or ]: whereTab 269 [Op.or]: whereTab
271 }, 270 },
272 { 271 {
273 actorId 272 actorId
@@ -295,12 +294,12 @@ export class ActorFollowModel extends Model<ActorFollowModel> {
295 } 294 }
296 295
297 static listFollowingForApi (options: { 296 static listFollowingForApi (options: {
298 id: number, 297 id: number
299 start: number, 298 start: number
300 count: number, 299 count: number
301 sort: string, 300 sort: string
302 state?: FollowState, 301 state?: FollowState
303 actorType?: ActivityPubActorType, 302 actorType?: ActivityPubActorType
304 search?: string 303 search?: string
305 }) { 304 }) {
306 const { id, start, count, sort, search, state, actorType } = options 305 const { id, start, count, sort, search, state, actorType } = options
@@ -312,7 +311,7 @@ export class ActorFollowModel extends Model<ActorFollowModel> {
312 if (search) { 311 if (search) {
313 Object.assign(followingServerWhere, { 312 Object.assign(followingServerWhere, {
314 host: { 313 host: {
315 [ Op.iLike ]: '%' + search + '%' 314 [Op.iLike]: '%' + search + '%'
316 } 315 }
317 }) 316 })
318 } 317 }
@@ -362,12 +361,12 @@ export class ActorFollowModel extends Model<ActorFollowModel> {
362 } 361 }
363 362
364 static listFollowersForApi (options: { 363 static listFollowersForApi (options: {
365 actorId: number, 364 actorId: number
366 start: number, 365 start: number
367 count: number, 366 count: number
368 sort: string, 367 sort: string
369 state?: FollowState, 368 state?: FollowState
370 actorType?: ActivityPubActorType, 369 actorType?: ActivityPubActorType
371 search?: string 370 search?: string
372 }) { 371 }) {
373 const { actorId, start, count, sort, search, state, actorType } = options 372 const { actorId, start, count, sort, search, state, actorType } = options
@@ -379,7 +378,7 @@ export class ActorFollowModel extends Model<ActorFollowModel> {
379 if (search) { 378 if (search) {
380 Object.assign(followerServerWhere, { 379 Object.assign(followerServerWhere, {
381 host: { 380 host: {
382 [ Op.iLike ]: '%' + search + '%' 381 [Op.iLike]: '%' + search + '%'
383 } 382 }
384 }) 383 })
385 } 384 }
@@ -631,7 +630,7 @@ export class ActorFollowModel extends Model<ActorFollowModel> {
631 630
632 const tasks: Bluebird<any>[] = [] 631 const tasks: Bluebird<any>[] = []
633 632
634 for (let selection of selections) { 633 for (const selection of selections) {
635 let query = 'SELECT ' + selection + ' FROM "actor" ' + 634 let query = 'SELECT ' + selection + ' FROM "actor" ' +
636 'INNER JOIN "actorFollow" ON "actorFollow"."' + firstJoin + '" = "actor"."id" ' + 635 'INNER JOIN "actorFollow" ON "actorFollow"."' + firstJoin + '" = "actor"."id" ' +
637 'INNER JOIN "actor" AS "Follows" ON "actorFollow"."' + secondJoin + '" = "Follows"."id" ' + 636 'INNER JOIN "actor" AS "Follows" ON "actorFollow"."' + secondJoin + '" = "Follows"."id" ' +
diff --git a/server/models/activitypub/actor.ts b/server/models/activitypub/actor.ts
index d651a281a..00e8dc954 100644
--- a/server/models/activitypub/actor.ts
+++ b/server/models/activitypub/actor.ts
@@ -16,7 +16,7 @@ import {
16 Table, 16 Table,
17 UpdatedAt 17 UpdatedAt
18} from 'sequelize-typescript' 18} from 'sequelize-typescript'
19import { ActivityPubActorType } from '../../../shared/models/activitypub' 19import { ActivityIconObject, ActivityPubActorType } from '../../../shared/models/activitypub'
20import { Avatar } from '../../../shared/models/avatars/avatar.model' 20import { Avatar } from '../../../shared/models/avatars/avatar.model'
21import { activityPubContextify } from '../../helpers/activitypub' 21import { activityPubContextify } from '../../helpers/activitypub'
22import { 22import {
@@ -335,7 +335,7 @@ export class ActorModel extends Model<ActorModel> {
335 const query = { 335 const query = {
336 where: { 336 where: {
337 followersUrl: { 337 followersUrl: {
338 [ Op.in ]: followersUrls 338 [Op.in]: followersUrls
339 } 339 }
340 }, 340 },
341 transaction 341 transaction
@@ -362,7 +362,7 @@ export class ActorModel extends Model<ActorModel> {
362 .findOne(query) 362 .findOne(query)
363 .then(actor => { 363 .then(actor => {
364 if (preferredUsername === SERVER_ACTOR_NAME) { 364 if (preferredUsername === SERVER_ACTOR_NAME) {
365 ActorModel.localNameCache[ preferredUsername ] = actor 365 ActorModel.localNameCache[preferredUsername] = actor
366 } 366 }
367 367
368 return actor 368 return actor
@@ -388,7 +388,7 @@ export class ActorModel extends Model<ActorModel> {
388 .findOne(query) 388 .findOne(query)
389 .then(actor => { 389 .then(actor => {
390 if (preferredUsername === SERVER_ACTOR_NAME) { 390 if (preferredUsername === SERVER_ACTOR_NAME) {
391 ActorModel.localUrlCache[ preferredUsername ] = actor 391 ActorModel.localUrlCache[preferredUsername] = actor
392 } 392 }
393 393
394 return actor 394 return actor
@@ -500,9 +500,11 @@ export class ActorModel extends Model<ActorModel> {
500 } 500 }
501 501
502 toActivityPubObject (this: MActorAP, name: string) { 502 toActivityPubObject (this: MActorAP, name: string) {
503 let icon = undefined 503 let icon: ActivityIconObject
504
504 if (this.avatarId) { 505 if (this.avatarId) {
505 const extension = extname(this.Avatar.filename) 506 const extension = extname(this.Avatar.filename)
507
506 icon = { 508 icon = {
507 type: 'Image', 509 type: 'Image',
508 mediaType: extension === '.png' ? 'image/png' : 'image/jpeg', 510 mediaType: extension === '.png' ? 'image/png' : 'image/jpeg',
diff --git a/server/models/oauth/oauth-token.ts b/server/models/oauth/oauth-token.ts
index b680be237..d2101ce86 100644
--- a/server/models/oauth/oauth-token.ts
+++ b/server/models/oauth/oauth-token.ts
@@ -23,10 +23,10 @@ import { MOAuthTokenUser } from '@server/typings/models/oauth/oauth-token'
23 23
24export type OAuthTokenInfo = { 24export type OAuthTokenInfo = {
25 refreshToken: string 25 refreshToken: string
26 refreshTokenExpiresAt: Date, 26 refreshTokenExpiresAt: Date
27 client: { 27 client: {
28 id: number 28 id: number
29 }, 29 }
30 user: { 30 user: {
31 id: number 31 id: number
32 } 32 }
diff --git a/server/models/redundancy/video-redundancy.ts b/server/models/redundancy/video-redundancy.ts
index 4e66d72e3..1b63d3818 100644
--- a/server/models/redundancy/video-redundancy.ts
+++ b/server/models/redundancy/video-redundancy.ts
@@ -43,7 +43,7 @@ export enum ScopeNames {
43} 43}
44 44
45@Scopes(() => ({ 45@Scopes(() => ({
46 [ ScopeNames.WITH_VIDEO ]: { 46 [ScopeNames.WITH_VIDEO]: {
47 include: [ 47 include: [
48 { 48 {
49 model: VideoFileModel, 49 model: VideoFileModel,
@@ -167,7 +167,7 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> {
167 logger.info('Removing duplicated video streaming playlist %s.', videoUUID) 167 logger.info('Removing duplicated video streaming playlist %s.', videoUUID)
168 168
169 videoStreamingPlaylist.Video.removeStreamingPlaylistFiles(videoStreamingPlaylist, true) 169 videoStreamingPlaylist.Video.removeStreamingPlaylistFiles(videoStreamingPlaylist, true)
170 .catch(err => logger.error('Cannot delete video streaming playlist files of %s.', videoUUID, { err })) 170 .catch(err => logger.error('Cannot delete video streaming playlist files of %s.', videoUUID, { err }))
171 } 171 }
172 172
173 return undefined 173 return undefined
@@ -230,12 +230,12 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> {
230 }, 230 },
231 include: [ 231 include: [
232 { 232 {
233 attributes: [ ], 233 attributes: [],
234 model: VideoFileModel, 234 model: VideoFileModel,
235 required: true, 235 required: true,
236 include: [ 236 include: [
237 { 237 {
238 attributes: [ ], 238 attributes: [],
239 model: VideoModel, 239 model: VideoModel,
240 required: true, 240 required: true,
241 where: { 241 where: {
@@ -248,7 +248,7 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> {
248 } 248 }
249 249
250 return VideoRedundancyModel.findOne(query) 250 return VideoRedundancyModel.findOne(query)
251 .then(r => !!r) 251 .then(r => !!r)
252 } 252 }
253 253
254 static async getVideoSample (p: Bluebird<VideoModel[]>) { 254 static async getVideoSample (p: Bluebird<VideoModel[]>) {
@@ -310,7 +310,7 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> {
310 where: { 310 where: {
311 privacy: VideoPrivacy.PUBLIC, 311 privacy: VideoPrivacy.PUBLIC,
312 views: { 312 views: {
313 [ Op.gte ]: minViews 313 [Op.gte]: minViews
314 } 314 }
315 }, 315 },
316 include: [ 316 include: [
@@ -333,7 +333,7 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> {
333 actorId: actor.id, 333 actorId: actor.id,
334 strategy, 334 strategy,
335 createdAt: { 335 createdAt: {
336 [ Op.lt ]: expiredDate 336 [Op.lt]: expiredDate
337 } 337 }
338 } 338 }
339 } 339 }
@@ -392,7 +392,7 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> {
392 where: { 392 where: {
393 actorId: actor.id, 393 actorId: actor.id,
394 expiresOn: { 394 expiresOn: {
395 [ Op.lt ]: new Date() 395 [Op.lt]: new Date()
396 } 396 }
397 } 397 }
398 } 398 }
@@ -409,8 +409,8 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> {
409 [Op.ne]: actor.id 409 [Op.ne]: actor.id
410 }, 410 },
411 expiresOn: { 411 expiresOn: {
412 [ Op.lt ]: new Date(), 412 [Op.lt]: new Date(),
413 [ Op.ne ]: null 413 [Op.ne]: null
414 } 414 }
415 } 415 }
416 } 416 }
@@ -464,15 +464,15 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> {
464 } 464 }
465 465
466 static listForApi (options: { 466 static listForApi (options: {
467 start: number, 467 start: number
468 count: number, 468 count: number
469 sort: string, 469 sort: string
470 target: VideoRedundanciesTarget, 470 target: VideoRedundanciesTarget
471 strategy?: string 471 strategy?: string
472 }) { 472 }) {
473 const { start, count, sort, target, strategy } = options 473 const { start, count, sort, target, strategy } = options
474 let redundancyWhere: WhereOptions = {} 474 const redundancyWhere: WhereOptions = {}
475 let videosWhere: WhereOptions = {} 475 const videosWhere: WhereOptions = {}
476 let redundancySqlSuffix = '' 476 let redundancySqlSuffix = ''
477 477
478 if (target === 'my-videos') { 478 if (target === 'my-videos') {
@@ -490,10 +490,10 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> {
490 const videoFilterWhere = { 490 const videoFilterWhere = {
491 [Op.and]: [ 491 [Op.and]: [
492 { 492 {
493 [ Op.or ]: [ 493 [Op.or]: [
494 { 494 {
495 id: { 495 id: {
496 [ Op.in ]: literal( 496 [Op.in]: literal(
497 '(' + 497 '(' +
498 'SELECT "videoId" FROM "videoFile" ' + 498 'SELECT "videoId" FROM "videoFile" ' +
499 'INNER JOIN "videoRedundancy" ON "videoRedundancy"."videoFileId" = "videoFile".id' + 499 'INNER JOIN "videoRedundancy" ON "videoRedundancy"."videoFileId" = "videoFile".id' +
@@ -504,7 +504,7 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> {
504 }, 504 },
505 { 505 {
506 id: { 506 id: {
507 [ Op.in ]: literal( 507 [Op.in]: literal(
508 '(' + 508 '(' +
509 'select "videoId" FROM "videoStreamingPlaylist" ' + 509 'select "videoId" FROM "videoStreamingPlaylist" ' +
510 'INNER JOIN "videoRedundancy" ON "videoRedundancy"."videoStreamingPlaylistId" = "videoStreamingPlaylist".id' + 510 'INNER JOIN "videoRedundancy" ON "videoRedundancy"."videoStreamingPlaylistId" = "videoStreamingPlaylist".id' +
@@ -592,16 +592,16 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> {
592 } 592 }
593 593
594 return VideoRedundancyModel.findOne(query) 594 return VideoRedundancyModel.findOne(query)
595 .then((r: any) => ({ 595 .then((r: any) => ({
596 totalUsed: parseAggregateResult(r.totalUsed), 596 totalUsed: parseAggregateResult(r.totalUsed),
597 totalVideos: r.totalVideos, 597 totalVideos: r.totalVideos,
598 totalVideoFiles: r.totalVideoFiles 598 totalVideoFiles: r.totalVideoFiles
599 })) 599 }))
600 } 600 }
601 601
602 static toFormattedJSONStatic (video: MVideoForRedundancyAPI): VideoRedundancy { 602 static toFormattedJSONStatic (video: MVideoForRedundancyAPI): VideoRedundancy {
603 let filesRedundancies: FileRedundancyInformation[] = [] 603 const filesRedundancies: FileRedundancyInformation[] = []
604 let streamingPlaylistsRedundancies: StreamingPlaylistRedundancyInformation[] = [] 604 const streamingPlaylistsRedundancies: StreamingPlaylistRedundancyInformation[] = []
605 605
606 for (const file of video.VideoFiles) { 606 for (const file of video.VideoFiles) {
607 for (const redundancy of file.RedundancyVideos) { 607 for (const redundancy of file.RedundancyVideos) {
@@ -678,7 +678,7 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> {
678 expires: this.expiresOn ? this.expiresOn.toISOString() : null, 678 expires: this.expiresOn ? this.expiresOn.toISOString() : null,
679 url: { 679 url: {
680 type: 'Link', 680 type: 'Link',
681 mediaType: MIMETYPES.VIDEO.EXT_MIMETYPE[ this.VideoFile.extname ] as any, 681 mediaType: MIMETYPES.VIDEO.EXT_MIMETYPE[this.VideoFile.extname] as any,
682 href: this.fileUrl, 682 href: this.fileUrl,
683 height: this.VideoFile.resolution, 683 height: this.VideoFile.resolution,
684 size: this.VideoFile.size, 684 size: this.VideoFile.size,
@@ -693,7 +693,7 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> {
693 693
694 const notIn = literal( 694 const notIn = literal(
695 '(' + 695 '(' +
696 `SELECT "videoFileId" FROM "videoRedundancy" WHERE "actorId" = ${actor.id} AND "videoFileId" IS NOT NULL` + 696 `SELECT "videoFileId" FROM "videoRedundancy" WHERE "actorId" = ${actor.id} AND "videoFileId" IS NOT NULL` +
697 ')' 697 ')'
698 ) 698 )
699 699
@@ -703,7 +703,7 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> {
703 required: true, 703 required: true,
704 where: { 704 where: {
705 id: { 705 id: {
706 [ Op.notIn ]: notIn 706 [Op.notIn]: notIn
707 } 707 }
708 } 708 }
709 } 709 }
diff --git a/server/models/server/plugin.ts b/server/models/server/plugin.ts
index d094da1f5..95774a467 100644
--- a/server/models/server/plugin.ts
+++ b/server/models/server/plugin.ts
@@ -189,10 +189,10 @@ export class PluginModel extends Model<PluginModel> {
189 } 189 }
190 190
191 static listForApi (options: { 191 static listForApi (options: {
192 pluginType?: PluginType, 192 pluginType?: PluginType
193 uninstalled?: boolean, 193 uninstalled?: boolean
194 start: number, 194 start: number
195 count: number, 195 count: number
196 sort: string 196 sort: string
197 }) { 197 }) {
198 const { uninstalled = false } = options 198 const { uninstalled = false } = options
diff --git a/server/models/utils.ts b/server/models/utils.ts
index f89b80011..f7afb8d4b 100644
--- a/server/models/utils.ts
+++ b/server/models/utils.ts
@@ -67,7 +67,7 @@ function getVideoSort (value: string, lastSort: OrderItem = [ 'id', 'ASC' ]): Or
67function getBlacklistSort (model: any, value: string, lastSort: OrderItem = [ 'id', 'ASC' ]): OrderItem[] { 67function getBlacklistSort (model: any, value: string, lastSort: OrderItem = [ 'id', 'ASC' ]): OrderItem[] {
68 const [ firstSort ] = getSort(value) 68 const [ firstSort ] = getSort(value)
69 69
70 if (model) return [ [ literal(`"${model}.${firstSort[ 0 ]}" ${firstSort[ 1 ]}`) ], lastSort ] as any[] // FIXME: typings 70 if (model) return [ [ literal(`"${model}.${firstSort[0]}" ${firstSort[1]}`) ], lastSort ] as any[] // FIXME: typings
71 return [ firstSort, lastSort ] 71 return [ firstSort, lastSort ]
72} 72}
73 73
@@ -139,7 +139,7 @@ function buildServerIdsFollowedBy (actorId: any) {
139 'SELECT "actor"."serverId" FROM "actorFollow" ' + 139 'SELECT "actor"."serverId" FROM "actorFollow" ' +
140 'INNER JOIN "actor" ON actor.id = "actorFollow"."targetActorId" ' + 140 'INNER JOIN "actor" ON actor.id = "actorFollow"."targetActorId" ' +
141 'WHERE "actorFollow"."actorId" = ' + actorIdNumber + 141 'WHERE "actorFollow"."actorId" = ' + actorIdNumber +
142 ')' 142 ')'
143} 143}
144 144
145function buildWhereIdOrUUID (id: number | string) { 145function buildWhereIdOrUUID (id: number | string) {
diff --git a/server/models/video/thumbnail.ts b/server/models/video/thumbnail.ts
index b69bc0872..e396784d2 100644
--- a/server/models/video/thumbnail.ts
+++ b/server/models/video/thumbnail.ts
@@ -92,7 +92,7 @@ export class ThumbnailModel extends Model<ThumbnailModel> {
92 @UpdatedAt 92 @UpdatedAt
93 updatedAt: Date 93 updatedAt: Date
94 94
95 private static types: { [ id in ThumbnailType ]: { label: string, directory: string, staticPath: string } } = { 95 private static readonly types: { [ id in ThumbnailType ]: { label: string, directory: string, staticPath: string } } = {
96 [ThumbnailType.MINIATURE]: { 96 [ThumbnailType.MINIATURE]: {
97 label: 'miniature', 97 label: 'miniature',
98 directory: CONFIG.STORAGE.THUMBNAILS_DIR, 98 directory: CONFIG.STORAGE.THUMBNAILS_DIR,
diff --git a/server/models/video/video-abuse.ts b/server/models/video/video-abuse.ts
index 3636db18d..da8c1577c 100644
--- a/server/models/video/video-abuse.ts
+++ b/server/models/video/video-abuse.ts
@@ -87,9 +87,9 @@ export class VideoAbuseModel extends Model<VideoAbuseModel> {
87 } 87 }
88 88
89 static listForApi (parameters: { 89 static listForApi (parameters: {
90 start: number, 90 start: number
91 count: number, 91 count: number
92 sort: string, 92 sort: string
93 serverAccountId: number 93 serverAccountId: number
94 user?: MUserAccountId 94 user?: MUserAccountId
95 }) { 95 }) {
diff --git a/server/models/video/video-caption.ts b/server/models/video/video-caption.ts
index 1307c27f1..59d3e1050 100644
--- a/server/models/video/video-caption.ts
+++ b/server/models/video/video-caption.ts
@@ -4,7 +4,8 @@ import {
4 BeforeDestroy, 4 BeforeDestroy,
5 BelongsTo, 5 BelongsTo,
6 Column, 6 Column,
7 CreatedAt, DataType, 7 CreatedAt,
8 DataType,
8 ForeignKey, 9 ForeignKey,
9 Is, 10 Is,
10 Model, 11 Model,
@@ -16,13 +17,13 @@ import { buildWhereIdOrUUID, throwIfNotValid } from '../utils'
16import { VideoModel } from './video' 17import { VideoModel } from './video'
17import { isVideoCaptionLanguageValid } from '../../helpers/custom-validators/video-captions' 18import { isVideoCaptionLanguageValid } from '../../helpers/custom-validators/video-captions'
18import { VideoCaption } from '../../../shared/models/videos/caption/video-caption.model' 19import { VideoCaption } from '../../../shared/models/videos/caption/video-caption.model'
19import { CONSTRAINTS_FIELDS, LAZY_STATIC_PATHS, STATIC_PATHS, VIDEO_LANGUAGES, WEBSERVER } from '../../initializers/constants' 20import { CONSTRAINTS_FIELDS, LAZY_STATIC_PATHS, VIDEO_LANGUAGES, WEBSERVER } from '../../initializers/constants'
20import { join } from 'path' 21import { join } from 'path'
21import { logger } from '../../helpers/logger' 22import { logger } from '../../helpers/logger'
22import { remove } from 'fs-extra' 23import { remove } from 'fs-extra'
23import { CONFIG } from '../../initializers/config' 24import { CONFIG } from '../../initializers/config'
24import * as Bluebird from 'bluebird' 25import * as Bluebird from 'bluebird'
25import { MVideo, MVideoAccountLight, MVideoCaptionFormattable, MVideoCaptionVideo } from '@server/typings/models' 26import { MVideoAccountLight, MVideoCaptionFormattable, MVideoCaptionVideo } from '@server/typings/models'
26import { buildRemoteVideoBaseUrl } from '@server/helpers/activitypub' 27import { buildRemoteVideoBaseUrl } from '@server/helpers/activitypub'
27 28
28export enum ScopeNames { 29export enum ScopeNames {
diff --git a/server/models/video/video-channel.ts b/server/models/video/video-channel.ts
index 4e98e7ba3..835216671 100644
--- a/server/models/video/video-channel.ts
+++ b/server/models/video/video-channel.ts
@@ -30,7 +30,7 @@ import { buildServerIdsFollowedBy, buildTrigramSearchIndex, createSimilarityAttr
30import { VideoModel } from './video' 30import { VideoModel } from './video'
31import { CONSTRAINTS_FIELDS, WEBSERVER } from '../../initializers/constants' 31import { CONSTRAINTS_FIELDS, WEBSERVER } from '../../initializers/constants'
32import { ServerModel } from '../server/server' 32import { ServerModel } from '../server/server'
33import { FindOptions, ModelIndexesOptions, Op } from 'sequelize' 33import { FindOptions, 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' 36import * as Bluebird from 'bluebird'
@@ -121,7 +121,7 @@ export type SummaryOptions = {
121 }, 121 },
122 { 122 {
123 serverId: { 123 serverId: {
124 [ Op.in ]: Sequelize.literal(inQueryInstanceFollow) 124 [Op.in]: Sequelize.literal(inQueryInstanceFollow)
125 } 125 }
126 } 126 }
127 ] 127 ]
@@ -348,9 +348,9 @@ export class VideoChannelModel extends Model<VideoChannelModel> {
348 } 348 }
349 349
350 static listByAccount (options: { 350 static listByAccount (options: {
351 accountId: number, 351 accountId: number
352 start: number, 352 start: number
353 count: number, 353 count: number
354 sort: string 354 sort: string
355 }) { 355 }) {
356 const query = { 356 const query = {
diff --git a/server/models/video/video-comment.ts b/server/models/video/video-comment.ts
index fb4d16b4d..b33c33d5e 100644
--- a/server/models/video/video-comment.ts
+++ b/server/models/video/video-comment.ts
@@ -257,10 +257,10 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
257 } 257 }
258 258
259 static async listThreadsForApi (parameters: { 259 static async listThreadsForApi (parameters: {
260 videoId: number, 260 videoId: number
261 start: number, 261 start: number
262 count: number, 262 count: number
263 sort: string, 263 sort: string
264 user?: MUserAccountId 264 user?: MUserAccountId
265 }) { 265 }) {
266 const { videoId, start, count, sort, user } = parameters 266 const { videoId, start, count, sort, user } = parameters
@@ -300,8 +300,8 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
300 } 300 }
301 301
302 static async listThreadCommentsForApi (parameters: { 302 static async listThreadCommentsForApi (parameters: {
303 videoId: number, 303 videoId: number
304 threadId: number, 304 threadId: number
305 user?: MUserAccountId 305 user?: MUserAccountId
306 }) { 306 }) {
307 const { videoId, threadId, user } = parameters 307 const { videoId, threadId, user } = parameters
@@ -314,7 +314,7 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
314 order: [ [ 'createdAt', 'ASC' ], [ 'updatedAt', 'ASC' ] ] as Order, 314 order: [ [ 'createdAt', 'ASC' ], [ 'updatedAt', 'ASC' ] ] as Order,
315 where: { 315 where: {
316 videoId, 316 videoId,
317 [ Op.or ]: [ 317 [Op.or]: [
318 { id: threadId }, 318 { id: threadId },
319 { originCommentId: threadId } 319 { originCommentId: threadId }
320 ], 320 ],
@@ -346,7 +346,7 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
346 order: [ [ 'createdAt', order ] ] as Order, 346 order: [ [ 'createdAt', order ] ] as Order,
347 where: { 347 where: {
348 id: { 348 id: {
349 [ Op.in ]: Sequelize.literal('(' + 349 [Op.in]: Sequelize.literal('(' +
350 'WITH RECURSIVE children (id, "inReplyToCommentId") AS ( ' + 350 'WITH RECURSIVE children (id, "inReplyToCommentId") AS ( ' +
351 `SELECT id, "inReplyToCommentId" FROM "videoComment" WHERE id = ${comment.id} ` + 351 `SELECT id, "inReplyToCommentId" FROM "videoComment" WHERE id = ${comment.id} ` +
352 'UNION ' + 352 'UNION ' +
@@ -355,7 +355,7 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
355 ') ' + 355 ') ' +
356 'SELECT id FROM children' + 356 'SELECT id FROM children' +
357 ')'), 357 ')'),
358 [ Op.ne ]: comment.id 358 [Op.ne]: comment.id
359 } 359 }
360 }, 360 },
361 transaction: t 361 transaction: t
@@ -461,7 +461,7 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
461 } 461 }
462 462
463 isDeleted () { 463 isDeleted () {
464 return null !== this.deletedAt 464 return this.deletedAt !== null
465 } 465 }
466 466
467 extractMentions () { 467 extractMentions () {
diff --git a/server/models/video/video-format-utils.ts b/server/models/video/video-format-utils.ts
index bb50edcaa..55b0ed062 100644
--- a/server/models/video/video-format-utils.ts
+++ b/server/models/video/video-format-utils.ts
@@ -27,12 +27,13 @@ import { generateMagnetUri } from '@server/helpers/webtorrent'
27export type VideoFormattingJSONOptions = { 27export type VideoFormattingJSONOptions = {
28 completeDescription?: boolean 28 completeDescription?: boolean
29 additionalAttributes: { 29 additionalAttributes: {
30 state?: boolean, 30 state?: boolean
31 waitTranscoding?: boolean, 31 waitTranscoding?: boolean
32 scheduledUpdate?: boolean, 32 scheduledUpdate?: boolean
33 blacklistInfo?: boolean 33 blacklistInfo?: boolean
34 } 34 }
35} 35}
36
36function videoModelToFormattedJSON (video: MVideoFormattable, options?: VideoFormattingJSONOptions): Video { 37function videoModelToFormattedJSON (video: MVideoFormattable, options?: VideoFormattingJSONOptions): Video {
37 const userHistory = isArray(video.UserVideoHistories) ? video.UserVideoHistories[0] : undefined 38 const userHistory = isArray(video.UserVideoHistories) ? video.UserVideoHistories[0] : undefined
38 39
@@ -181,12 +182,10 @@ function videoFilesModelToFormattedJSON (
181): VideoFile[] { 182): VideoFile[] {
182 return videoFiles 183 return videoFiles
183 .map(videoFile => { 184 .map(videoFile => {
184 let resolutionLabel = videoFile.resolution + 'p'
185
186 return { 185 return {
187 resolution: { 186 resolution: {
188 id: videoFile.resolution, 187 id: videoFile.resolution,
189 label: resolutionLabel 188 label: videoFile.resolution + 'p'
190 }, 189 },
191 magnetUri: generateMagnetUri(model, videoFile, baseUrlHttp, baseUrlWs), 190 magnetUri: generateMagnetUri(model, videoFile, baseUrlHttp, baseUrlWs),
192 size: videoFile.size, 191 size: videoFile.size,
@@ -214,7 +213,7 @@ function addVideoFilesInAPAcc (
214 for (const file of files) { 213 for (const file of files) {
215 acc.push({ 214 acc.push({
216 type: 'Link', 215 type: 'Link',
217 mediaType: MIMETYPES.VIDEO.EXT_MIMETYPE[ file.extname ] as any, 216 mediaType: MIMETYPES.VIDEO.EXT_MIMETYPE[file.extname] as any,
218 href: model.getVideoFileUrl(file, baseUrlHttp), 217 href: model.getVideoFileUrl(file, baseUrlHttp),
219 height: file.resolution, 218 height: file.resolution,
220 size: file.size, 219 size: file.size,
@@ -274,10 +273,8 @@ function videoModelToActivityPubObject (video: MVideoAP): VideoTorrentObject {
274 addVideoFilesInAPAcc(url, video, baseUrlHttp, baseUrlWs, video.VideoFiles || []) 273 addVideoFilesInAPAcc(url, video, baseUrlHttp, baseUrlWs, video.VideoFiles || [])
275 274
276 for (const playlist of (video.VideoStreamingPlaylists || [])) { 275 for (const playlist of (video.VideoStreamingPlaylists || [])) {
277 let tag: ActivityTagObject[] 276 const tag = playlist.p2pMediaLoaderInfohashes
278 277 .map(i => ({ type: 'Infohash' as 'Infohash', name: i })) as ActivityTagObject[]
279 tag = playlist.p2pMediaLoaderInfohashes
280 .map(i => ({ type: 'Infohash' as 'Infohash', name: i }))
281 tag.push({ 278 tag.push({
282 type: 'Link', 279 type: 'Link',
283 name: 'sha256', 280 name: 'sha256',
diff --git a/server/models/video/video-playlist-element.ts b/server/models/video/video-playlist-element.ts
index f2d71357f..4ba16f5fd 100644
--- a/server/models/video/video-playlist-element.ts
+++ b/server/models/video/video-playlist-element.ts
@@ -120,10 +120,10 @@ export class VideoPlaylistElementModel extends Model<VideoPlaylistElementModel>
120 } 120 }
121 121
122 static listForApi (options: { 122 static listForApi (options: {
123 start: number, 123 start: number
124 count: number, 124 count: number
125 videoPlaylistId: number, 125 videoPlaylistId: number
126 serverAccount: AccountModel, 126 serverAccount: AccountModel
127 user?: MUserAccountId 127 user?: MUserAccountId
128 }) { 128 }) {
129 const accountIds = [ options.serverAccount.id ] 129 const accountIds = [ options.serverAccount.id ]
diff --git a/server/models/video/video-playlist.ts b/server/models/video/video-playlist.ts
index ba1fc23ea..4ca17ebec 100644
--- a/server/models/video/video-playlist.ts
+++ b/server/models/video/video-playlist.ts
@@ -68,12 +68,12 @@ type AvailableForListOptions = {
68 type?: VideoPlaylistType 68 type?: VideoPlaylistType
69 accountId?: number 69 accountId?: number
70 videoChannelId?: number 70 videoChannelId?: number
71 listMyPlaylists?: boolean, 71 listMyPlaylists?: boolean
72 search?: string 72 search?: string
73} 73}
74 74
75@Scopes(() => ({ 75@Scopes(() => ({
76 [ ScopeNames.WITH_THUMBNAIL ]: { 76 [ScopeNames.WITH_THUMBNAIL]: {
77 include: [ 77 include: [
78 { 78 {
79 model: ThumbnailModel, 79 model: ThumbnailModel,
@@ -81,7 +81,7 @@ type AvailableForListOptions = {
81 } 81 }
82 ] 82 ]
83 }, 83 },
84 [ ScopeNames.WITH_VIDEOS_LENGTH ]: { 84 [ScopeNames.WITH_VIDEOS_LENGTH]: {
85 attributes: { 85 attributes: {
86 include: [ 86 include: [
87 [ 87 [
@@ -91,7 +91,7 @@ type AvailableForListOptions = {
91 ] 91 ]
92 } 92 }
93 } as FindOptions, 93 } as FindOptions,
94 [ ScopeNames.WITH_ACCOUNT ]: { 94 [ScopeNames.WITH_ACCOUNT]: {
95 include: [ 95 include: [
96 { 96 {
97 model: AccountModel, 97 model: AccountModel,
@@ -99,7 +99,7 @@ type AvailableForListOptions = {
99 } 99 }
100 ] 100 ]
101 }, 101 },
102 [ ScopeNames.WITH_ACCOUNT_AND_CHANNEL_SUMMARY ]: { 102 [ScopeNames.WITH_ACCOUNT_AND_CHANNEL_SUMMARY]: {
103 include: [ 103 include: [
104 { 104 {
105 model: AccountModel.scope(AccountScopeNames.SUMMARY), 105 model: AccountModel.scope(AccountScopeNames.SUMMARY),
@@ -111,7 +111,7 @@ type AvailableForListOptions = {
111 } 111 }
112 ] 112 ]
113 }, 113 },
114 [ ScopeNames.WITH_ACCOUNT_AND_CHANNEL ]: { 114 [ScopeNames.WITH_ACCOUNT_AND_CHANNEL]: {
115 include: [ 115 include: [
116 { 116 {
117 model: AccountModel, 117 model: AccountModel,
@@ -123,7 +123,7 @@ type AvailableForListOptions = {
123 } 123 }
124 ] 124 ]
125 }, 125 },
126 [ ScopeNames.AVAILABLE_FOR_LIST ]: (options: AvailableForListOptions) => { 126 [ScopeNames.AVAILABLE_FOR_LIST]: (options: AvailableForListOptions) => {
127 127
128 let whereActor: WhereOptions = {} 128 let whereActor: WhereOptions = {}
129 129
@@ -138,13 +138,13 @@ type AvailableForListOptions = {
138 const inQueryInstanceFollow = buildServerIdsFollowedBy(options.followerActorId) 138 const inQueryInstanceFollow = buildServerIdsFollowedBy(options.followerActorId)
139 139
140 whereActor = { 140 whereActor = {
141 [ Op.or ]: [ 141 [Op.or]: [
142 { 142 {
143 serverId: null 143 serverId: null
144 }, 144 },
145 { 145 {
146 serverId: { 146 serverId: {
147 [ Op.in ]: literal(inQueryInstanceFollow) 147 [Op.in]: literal(inQueryInstanceFollow)
148 } 148 }
149 } 149 }
150 ] 150 ]
@@ -172,7 +172,7 @@ type AvailableForListOptions = {
172 if (options.search) { 172 if (options.search) {
173 whereAnd.push({ 173 whereAnd.push({
174 name: { 174 name: {
175 [ Op.iLike ]: '%' + options.search + '%' 175 [Op.iLike]: '%' + options.search + '%'
176 } 176 }
177 }) 177 })
178 } 178 }
@@ -299,13 +299,13 @@ export class VideoPlaylistModel extends Model<VideoPlaylistModel> {
299 299
300 static listForApi (options: { 300 static listForApi (options: {
301 followerActorId: number 301 followerActorId: number
302 start: number, 302 start: number
303 count: number, 303 count: number
304 sort: string, 304 sort: string
305 type?: VideoPlaylistType, 305 type?: VideoPlaylistType
306 accountId?: number, 306 accountId?: number
307 videoChannelId?: number, 307 videoChannelId?: number
308 listMyPlaylists?: boolean, 308 listMyPlaylists?: boolean
309 search?: string 309 search?: string
310 }) { 310 }) {
311 const query = { 311 const query = {
diff --git a/server/models/video/video.ts b/server/models/video/video.ts
index 1a924e6c9..1ec8d717e 100644
--- a/server/models/video/video.ts
+++ b/server/models/video/video.ts
@@ -187,7 +187,7 @@ export type AvailableForListIDsOptions = {
187} 187}
188 188
189@Scopes(() => ({ 189@Scopes(() => ({
190 [ ScopeNames.FOR_API ]: (options: ForAPIOptions) => { 190 [ScopeNames.FOR_API]: (options: ForAPIOptions) => {
191 const query: FindOptions = { 191 const query: FindOptions = {
192 include: [ 192 include: [
193 { 193 {
@@ -212,7 +212,7 @@ export type AvailableForListIDsOptions = {
212 if (options.ids) { 212 if (options.ids) {
213 query.where = { 213 query.where = {
214 id: { 214 id: {
215 [ Op.in ]: options.ids 215 [Op.in]: options.ids
216 } 216 }
217 } 217 }
218 } 218 }
@@ -236,7 +236,7 @@ export type AvailableForListIDsOptions = {
236 236
237 return query 237 return query
238 }, 238 },
239 [ ScopeNames.AVAILABLE_FOR_LIST_IDS ]: (options: AvailableForListIDsOptions) => { 239 [ScopeNames.AVAILABLE_FOR_LIST_IDS]: (options: AvailableForListIDsOptions) => {
240 const whereAnd = options.baseWhere ? [].concat(options.baseWhere) : [] 240 const whereAnd = options.baseWhere ? [].concat(options.baseWhere) : []
241 241
242 const query: FindOptions = { 242 const query: FindOptions = {
@@ -247,11 +247,11 @@ export type AvailableForListIDsOptions = {
247 const attributesType = options.attributesType || 'id' 247 const attributesType = options.attributesType || 'id'
248 248
249 if (attributesType === 'id') query.attributes = [ 'id' ] 249 if (attributesType === 'id') query.attributes = [ 'id' ]
250 else if (attributesType === 'none') query.attributes = [ ] 250 else if (attributesType === 'none') query.attributes = []
251 251
252 whereAnd.push({ 252 whereAnd.push({
253 id: { 253 id: {
254 [ Op.notIn ]: Sequelize.literal( 254 [Op.notIn]: Sequelize.literal(
255 '(SELECT "videoBlacklist"."videoId" FROM "videoBlacklist")' 255 '(SELECT "videoBlacklist"."videoId" FROM "videoBlacklist")'
256 ) 256 )
257 } 257 }
@@ -260,7 +260,7 @@ export type AvailableForListIDsOptions = {
260 if (options.serverAccountId) { 260 if (options.serverAccountId) {
261 whereAnd.push({ 261 whereAnd.push({
262 channelId: { 262 channelId: {
263 [ Op.notIn ]: Sequelize.literal( 263 [Op.notIn]: Sequelize.literal(
264 '(' + 264 '(' +
265 'SELECT id FROM "videoChannel" WHERE "accountId" IN (' + 265 'SELECT id FROM "videoChannel" WHERE "accountId" IN (' +
266 buildBlockedAccountSQL(options.serverAccountId, options.user ? options.user.Account.id : undefined) + 266 buildBlockedAccountSQL(options.serverAccountId, options.user ? options.user.Account.id : undefined) +
@@ -273,15 +273,14 @@ export type AvailableForListIDsOptions = {
273 273
274 // Only list public/published videos 274 // Only list public/published videos
275 if (!options.filter || options.filter !== 'all-local') { 275 if (!options.filter || options.filter !== 'all-local') {
276
277 const publishWhere = { 276 const publishWhere = {
278 // Always list published videos, or videos that are being transcoded but on which we don't want to wait for transcoding 277 // Always list published videos, or videos that are being transcoded but on which we don't want to wait for transcoding
279 [ Op.or ]: [ 278 [Op.or]: [
280 { 279 {
281 state: VideoState.PUBLISHED 280 state: VideoState.PUBLISHED
282 }, 281 },
283 { 282 {
284 [ Op.and ]: { 283 [Op.and]: {
285 state: VideoState.TO_TRANSCODE, 284 state: VideoState.TO_TRANSCODE,
286 waitTranscoding: false 285 waitTranscoding: false
287 } 286 }
@@ -368,7 +367,7 @@ export type AvailableForListIDsOptions = {
368 [Op.or]: [ 367 [Op.or]: [
369 { 368 {
370 id: { 369 id: {
371 [ Op.in ]: Sequelize.literal( 370 [Op.in]: Sequelize.literal(
372 '(' + 371 '(' +
373 'SELECT "videoShare"."videoId" AS "id" FROM "videoShare" ' + 372 'SELECT "videoShare"."videoId" AS "id" FROM "videoShare" ' +
374 'INNER JOIN "actorFollow" ON "actorFollow"."targetActorId" = "videoShare"."actorId" ' + 373 'INNER JOIN "actorFollow" ON "actorFollow"."targetActorId" = "videoShare"."actorId" ' +
@@ -379,7 +378,7 @@ export type AvailableForListIDsOptions = {
379 }, 378 },
380 { 379 {
381 id: { 380 id: {
382 [ Op.in ]: Sequelize.literal( 381 [Op.in]: Sequelize.literal(
383 '(' + 382 '(' +
384 'SELECT "video"."id" AS "id" FROM "video" ' + 383 'SELECT "video"."id" AS "id" FROM "video" ' +
385 'INNER JOIN "videoChannel" ON "videoChannel"."id" = "video"."channelId" ' + 384 'INNER JOIN "videoChannel" ON "videoChannel"."id" = "video"."channelId" ' +
@@ -399,7 +398,7 @@ export type AvailableForListIDsOptions = {
399 if (options.withFiles === true) { 398 if (options.withFiles === true) {
400 whereAnd.push({ 399 whereAnd.push({
401 id: { 400 id: {
402 [ Op.in ]: Sequelize.literal( 401 [Op.in]: Sequelize.literal(
403 '(SELECT "videoId" FROM "videoFile")' 402 '(SELECT "videoId" FROM "videoFile")'
404 ) 403 )
405 } 404 }
@@ -413,7 +412,7 @@ export type AvailableForListIDsOptions = {
413 412
414 whereAnd.push({ 413 whereAnd.push({
415 id: { 414 id: {
416 [ Op.in ]: Sequelize.literal( 415 [Op.in]: Sequelize.literal(
417 '(' + 416 '(' +
418 'SELECT "videoId" FROM "videoTag" ' + 417 'SELECT "videoId" FROM "videoTag" ' +
419 'INNER JOIN "tag" ON "tag"."id" = "videoTag"."tagId" ' + 418 'INNER JOIN "tag" ON "tag"."id" = "videoTag"."tagId" ' +
@@ -429,7 +428,7 @@ export type AvailableForListIDsOptions = {
429 428
430 whereAnd.push({ 429 whereAnd.push({
431 id: { 430 id: {
432 [ Op.in ]: Sequelize.literal( 431 [Op.in]: Sequelize.literal(
433 '(' + 432 '(' +
434 'SELECT "videoId" FROM "videoTag" ' + 433 'SELECT "videoId" FROM "videoTag" ' +
435 'INNER JOIN "tag" ON "tag"."id" = "videoTag"."tagId" ' + 434 'INNER JOIN "tag" ON "tag"."id" = "videoTag"."tagId" ' +
@@ -449,7 +448,7 @@ export type AvailableForListIDsOptions = {
449 if (options.categoryOneOf) { 448 if (options.categoryOneOf) {
450 whereAnd.push({ 449 whereAnd.push({
451 category: { 450 category: {
452 [ Op.or ]: options.categoryOneOf 451 [Op.or]: options.categoryOneOf
453 } 452 }
454 }) 453 })
455 } 454 }
@@ -457,7 +456,7 @@ export type AvailableForListIDsOptions = {
457 if (options.licenceOneOf) { 456 if (options.licenceOneOf) {
458 whereAnd.push({ 457 whereAnd.push({
459 licence: { 458 licence: {
460 [ Op.or ]: options.licenceOneOf 459 [Op.or]: options.licenceOneOf
461 } 460 }
462 }) 461 })
463 } 462 }
@@ -472,12 +471,12 @@ export type AvailableForListIDsOptions = {
472 [Op.or]: [ 471 [Op.or]: [
473 { 472 {
474 language: { 473 language: {
475 [ Op.or ]: videoLanguages 474 [Op.or]: videoLanguages
476 } 475 }
477 }, 476 },
478 { 477 {
479 id: { 478 id: {
480 [ Op.in ]: Sequelize.literal( 479 [Op.in]: Sequelize.literal(
481 '(' + 480 '(' +
482 'SELECT "videoId" FROM "videoCaption" ' + 481 'SELECT "videoId" FROM "videoCaption" ' +
483 'WHERE "language" IN (' + createSafeIn(VideoModel, options.languageOneOf) + ') ' + 482 'WHERE "language" IN (' + createSafeIn(VideoModel, options.languageOneOf) + ') ' +
@@ -511,12 +510,12 @@ export type AvailableForListIDsOptions = {
511 } 510 }
512 511
513 query.where = { 512 query.where = {
514 [ Op.and ]: whereAnd 513 [Op.and]: whereAnd
515 } 514 }
516 515
517 return query 516 return query
518 }, 517 },
519 [ ScopeNames.WITH_THUMBNAILS ]: { 518 [ScopeNames.WITH_THUMBNAILS]: {
520 include: [ 519 include: [
521 { 520 {
522 model: ThumbnailModel, 521 model: ThumbnailModel,
@@ -524,7 +523,7 @@ export type AvailableForListIDsOptions = {
524 } 523 }
525 ] 524 ]
526 }, 525 },
527 [ ScopeNames.WITH_USER_ID ]: { 526 [ScopeNames.WITH_USER_ID]: {
528 include: [ 527 include: [
529 { 528 {
530 attributes: [ 'accountId' ], 529 attributes: [ 'accountId' ],
@@ -540,7 +539,7 @@ export type AvailableForListIDsOptions = {
540 } 539 }
541 ] 540 ]
542 }, 541 },
543 [ ScopeNames.WITH_ACCOUNT_DETAILS ]: { 542 [ScopeNames.WITH_ACCOUNT_DETAILS]: {
544 include: [ 543 include: [
545 { 544 {
546 model: VideoChannelModel.unscoped(), 545 model: VideoChannelModel.unscoped(),
@@ -592,10 +591,10 @@ export type AvailableForListIDsOptions = {
592 } 591 }
593 ] 592 ]
594 }, 593 },
595 [ ScopeNames.WITH_TAGS ]: { 594 [ScopeNames.WITH_TAGS]: {
596 include: [ TagModel ] 595 include: [ TagModel ]
597 }, 596 },
598 [ ScopeNames.WITH_BLACKLISTED ]: { 597 [ScopeNames.WITH_BLACKLISTED]: {
599 include: [ 598 include: [
600 { 599 {
601 attributes: [ 'id', 'reason', 'unfederated' ], 600 attributes: [ 'id', 'reason', 'unfederated' ],
@@ -604,7 +603,7 @@ export type AvailableForListIDsOptions = {
604 } 603 }
605 ] 604 ]
606 }, 605 },
607 [ ScopeNames.WITH_WEBTORRENT_FILES ]: (withRedundancies = false) => { 606 [ScopeNames.WITH_WEBTORRENT_FILES]: (withRedundancies = false) => {
608 let subInclude: any[] = [] 607 let subInclude: any[] = []
609 608
610 if (withRedundancies === true) { 609 if (withRedundancies === true) {
@@ -628,7 +627,7 @@ export type AvailableForListIDsOptions = {
628 ] 627 ]
629 } 628 }
630 }, 629 },
631 [ ScopeNames.WITH_STREAMING_PLAYLISTS ]: (withRedundancies = false) => { 630 [ScopeNames.WITH_STREAMING_PLAYLISTS]: (withRedundancies = false) => {
632 const subInclude: IncludeOptions[] = [ 631 const subInclude: IncludeOptions[] = [
633 { 632 {
634 model: VideoFileModel.unscoped(), 633 model: VideoFileModel.unscoped(),
@@ -655,7 +654,7 @@ export type AvailableForListIDsOptions = {
655 ] 654 ]
656 } 655 }
657 }, 656 },
658 [ ScopeNames.WITH_SCHEDULED_UPDATE ]: { 657 [ScopeNames.WITH_SCHEDULED_UPDATE]: {
659 include: [ 658 include: [
660 { 659 {
661 model: ScheduleVideoUpdateModel.unscoped(), 660 model: ScheduleVideoUpdateModel.unscoped(),
@@ -663,7 +662,7 @@ export type AvailableForListIDsOptions = {
663 } 662 }
664 ] 663 ]
665 }, 664 },
666 [ ScopeNames.WITH_USER_HISTORY ]: (userId: number) => { 665 [ScopeNames.WITH_USER_HISTORY]: (userId: number) => {
667 return { 666 return {
668 include: [ 667 include: [
669 { 668 {
@@ -1016,7 +1015,7 @@ export class VideoModel extends Model<VideoModel> {
1016 }, 1015 },
1017 onDelete: 'cascade', 1016 onDelete: 'cascade',
1018 hooks: true, 1017 hooks: true,
1019 [ 'separate' as any ]: true 1018 ['separate' as any]: true
1020 }) 1019 })
1021 VideoCaptions: VideoCaptionModel[] 1020 VideoCaptions: VideoCaptionModel[]
1022 1021
@@ -1112,9 +1111,9 @@ export class VideoModel extends Model<VideoModel> {
1112 order: getVideoSort('createdAt', [ 'Tags', 'name', 'ASC' ] as any), // FIXME: sequelize typings 1111 order: getVideoSort('createdAt', [ 'Tags', 'name', 'ASC' ] as any), // FIXME: sequelize typings
1113 where: { 1112 where: {
1114 id: { 1113 id: {
1115 [ Op.in ]: Sequelize.literal('(' + rawQuery + ')') 1114 [Op.in]: Sequelize.literal('(' + rawQuery + ')')
1116 }, 1115 },
1117 [ Op.or ]: [ 1116 [Op.or]: [
1118 { privacy: VideoPrivacy.PUBLIC }, 1117 { privacy: VideoPrivacy.PUBLIC },
1119 { privacy: VideoPrivacy.UNLISTED } 1118 { privacy: VideoPrivacy.UNLISTED }
1120 ] 1119 ]
@@ -1131,10 +1130,10 @@ export class VideoModel extends Model<VideoModel> {
1131 required: false, 1130 required: false,
1132 // We only want videos shared by this actor 1131 // We only want videos shared by this actor
1133 where: { 1132 where: {
1134 [ Op.and ]: [ 1133 [Op.and]: [
1135 { 1134 {
1136 id: { 1135 id: {
1137 [ Op.not ]: null 1136 [Op.not]: null
1138 } 1137 }
1139 }, 1138 },
1140 { 1139 {
@@ -1184,8 +1183,8 @@ export class VideoModel extends Model<VideoModel> {
1184 // totals: totalVideos + totalVideoShares 1183 // totals: totalVideos + totalVideoShares
1185 let totalVideos = 0 1184 let totalVideos = 0
1186 let totalVideoShares = 0 1185 let totalVideoShares = 0
1187 if (totals[ 0 ]) totalVideos = parseInt(totals[ 0 ].total, 10) 1186 if (totals[0]) totalVideos = parseInt(totals[0].total, 10)
1188 if (totals[ 1 ]) totalVideoShares = parseInt(totals[ 1 ].total, 10) 1187 if (totals[1]) totalVideoShares = parseInt(totals[1].total, 10)
1189 1188
1190 const total = totalVideos + totalVideoShares 1189 const total = totalVideos + totalVideoShares
1191 return { 1190 return {
@@ -1228,7 +1227,7 @@ export class VideoModel extends Model<VideoModel> {
1228 baseQuery = Object.assign(baseQuery, { 1227 baseQuery = Object.assign(baseQuery, {
1229 where: { 1228 where: {
1230 name: { 1229 name: {
1231 [ Op.iLike ]: '%' + search + '%' 1230 [Op.iLike]: '%' + search + '%'
1232 } 1231 }
1233 } 1232 }
1234 }) 1233 })
@@ -1258,25 +1257,25 @@ export class VideoModel extends Model<VideoModel> {
1258 } 1257 }
1259 1258
1260 static async listForApi (options: { 1259 static async listForApi (options: {
1261 start: number, 1260 start: number
1262 count: number, 1261 count: number
1263 sort: string, 1262 sort: string
1264 nsfw: boolean, 1263 nsfw: boolean
1265 includeLocalVideos: boolean, 1264 includeLocalVideos: boolean
1266 withFiles: boolean, 1265 withFiles: boolean
1267 categoryOneOf?: number[], 1266 categoryOneOf?: number[]
1268 licenceOneOf?: number[], 1267 licenceOneOf?: number[]
1269 languageOneOf?: string[], 1268 languageOneOf?: string[]
1270 tagsOneOf?: string[], 1269 tagsOneOf?: string[]
1271 tagsAllOf?: string[], 1270 tagsAllOf?: string[]
1272 filter?: VideoFilter, 1271 filter?: VideoFilter
1273 accountId?: number, 1272 accountId?: number
1274 videoChannelId?: number, 1273 videoChannelId?: number
1275 followerActorId?: number 1274 followerActorId?: number
1276 videoPlaylistId?: number, 1275 videoPlaylistId?: number
1277 trendingDays?: number, 1276 trendingDays?: number
1278 user?: MUserAccountId, 1277 user?: MUserAccountId
1279 historyOfUser?: MUserId, 1278 historyOfUser?: MUserId
1280 countVideos?: boolean 1279 countVideos?: boolean
1281 }) { 1280 }) {
1282 if (options.filter && options.filter === 'all-local' && !options.user.hasRight(UserRight.SEE_ALL_VIDEOS)) { 1281 if (options.filter && options.filter === 'all-local' && !options.user.hasRight(UserRight.SEE_ALL_VIDEOS)) {
@@ -1342,7 +1341,7 @@ export class VideoModel extends Model<VideoModel> {
1342 tagsAllOf?: string[] 1341 tagsAllOf?: string[]
1343 durationMin?: number // seconds 1342 durationMin?: number // seconds
1344 durationMax?: number // seconds 1343 durationMax?: number // seconds
1345 user?: MUserAccountId, 1344 user?: MUserAccountId
1346 filter?: VideoFilter 1345 filter?: VideoFilter
1347 }) { 1346 }) {
1348 const whereAnd = [] 1347 const whereAnd = []
@@ -1350,8 +1349,8 @@ export class VideoModel extends Model<VideoModel> {
1350 if (options.startDate || options.endDate) { 1349 if (options.startDate || options.endDate) {
1351 const publishedAtRange = {} 1350 const publishedAtRange = {}
1352 1351
1353 if (options.startDate) publishedAtRange[ Op.gte ] = options.startDate 1352 if (options.startDate) publishedAtRange[Op.gte] = options.startDate
1354 if (options.endDate) publishedAtRange[ Op.lte ] = options.endDate 1353 if (options.endDate) publishedAtRange[Op.lte] = options.endDate
1355 1354
1356 whereAnd.push({ publishedAt: publishedAtRange }) 1355 whereAnd.push({ publishedAt: publishedAtRange })
1357 } 1356 }
@@ -1359,8 +1358,8 @@ export class VideoModel extends Model<VideoModel> {
1359 if (options.originallyPublishedStartDate || options.originallyPublishedEndDate) { 1358 if (options.originallyPublishedStartDate || options.originallyPublishedEndDate) {
1360 const originallyPublishedAtRange = {} 1359 const originallyPublishedAtRange = {}
1361 1360
1362 if (options.originallyPublishedStartDate) originallyPublishedAtRange[ Op.gte ] = options.originallyPublishedStartDate 1361 if (options.originallyPublishedStartDate) originallyPublishedAtRange[Op.gte] = options.originallyPublishedStartDate
1363 if (options.originallyPublishedEndDate) originallyPublishedAtRange[ Op.lte ] = options.originallyPublishedEndDate 1362 if (options.originallyPublishedEndDate) originallyPublishedAtRange[Op.lte] = options.originallyPublishedEndDate
1364 1363
1365 whereAnd.push({ originallyPublishedAt: originallyPublishedAtRange }) 1364 whereAnd.push({ originallyPublishedAt: originallyPublishedAtRange })
1366 } 1365 }
@@ -1368,8 +1367,8 @@ export class VideoModel extends Model<VideoModel> {
1368 if (options.durationMin || options.durationMax) { 1367 if (options.durationMin || options.durationMax) {
1369 const durationRange = {} 1368 const durationRange = {}
1370 1369
1371 if (options.durationMin) durationRange[ Op.gte ] = options.durationMin 1370 if (options.durationMin) durationRange[Op.gte] = options.durationMin
1372 if (options.durationMax) durationRange[ Op.lte ] = options.durationMax 1371 if (options.durationMax) durationRange[Op.lte] = options.durationMax
1373 1372
1374 whereAnd.push({ duration: durationRange }) 1373 whereAnd.push({ duration: durationRange })
1375 } 1374 }
@@ -1380,7 +1379,7 @@ export class VideoModel extends Model<VideoModel> {
1380 if (options.search) { 1379 if (options.search) {
1381 const trigramSearch = { 1380 const trigramSearch = {
1382 id: { 1381 id: {
1383 [ Op.in ]: Sequelize.literal( 1382 [Op.in]: Sequelize.literal(
1384 '(' + 1383 '(' +
1385 'SELECT "video"."id" FROM "video" ' + 1384 'SELECT "video"."id" FROM "video" ' +
1386 'WHERE ' + 1385 'WHERE ' +
@@ -1578,8 +1577,8 @@ export class VideoModel extends Model<VideoModel> {
1578 } 1577 }
1579 1578
1580 static loadForGetAPI (parameters: { 1579 static loadForGetAPI (parameters: {
1581 id: number | string, 1580 id: number | string
1582 t?: Transaction, 1581 t?: Transaction
1583 userId?: number 1582 userId?: number
1584 }): Bluebird<MVideoDetails> { 1583 }): Bluebird<MVideoDetails> {
1585 const { id, t, userId } = parameters 1584 const { id, t, userId } = parameters
@@ -1645,9 +1644,9 @@ export class VideoModel extends Model<VideoModel> {
1645 static checkVideoHasInstanceFollow (videoId: number, followerActorId: number) { 1644 static checkVideoHasInstanceFollow (videoId: number, followerActorId: number) {
1646 // Instances only share videos 1645 // Instances only share videos
1647 const query = 'SELECT 1 FROM "videoShare" ' + 1646 const query = 'SELECT 1 FROM "videoShare" ' +
1648 'INNER JOIN "actorFollow" ON "actorFollow"."targetActorId" = "videoShare"."actorId" ' + 1647 'INNER JOIN "actorFollow" ON "actorFollow"."targetActorId" = "videoShare"."actorId" ' +
1649 'WHERE "actorFollow"."actorId" = $followerActorId AND "videoShare"."videoId" = $videoId ' + 1648 'WHERE "actorFollow"."actorId" = $followerActorId AND "videoShare"."videoId" = $videoId ' +
1650 'LIMIT 1' 1649 'LIMIT 1'
1651 1650
1652 const options = { 1651 const options = {
1653 type: QueryTypes.SELECT as QueryTypes.SELECT, 1652 type: QueryTypes.SELECT as QueryTypes.SELECT,
@@ -1679,7 +1678,7 @@ export class VideoModel extends Model<VideoModel> {
1679 } 1678 }
1680 1679
1681 return VideoModel.findAll(query) 1680 return VideoModel.findAll(query)
1682 .then(videos => videos.map(v => v.id)) 1681 .then(videos => videos.map(v => v.id))
1683 } 1682 }
1684 1683
1685 // threshold corresponds to how many video the field should have to be returned 1684 // threshold corresponds to how many video the field should have to be returned
@@ -1699,14 +1698,14 @@ export class VideoModel extends Model<VideoModel> {
1699 limit: count, 1698 limit: count,
1700 group: field, 1699 group: field,
1701 having: Sequelize.where( 1700 having: Sequelize.where(
1702 Sequelize.fn('COUNT', Sequelize.col(field)), { [ Op.gte ]: threshold } 1701 Sequelize.fn('COUNT', Sequelize.col(field)), { [Op.gte]: threshold }
1703 ), 1702 ),
1704 order: [ (this.sequelize as any).random() ] 1703 order: [ (this.sequelize as any).random() ]
1705 } 1704 }
1706 1705
1707 return VideoModel.scope({ method: [ ScopeNames.AVAILABLE_FOR_LIST_IDS, scopeOptions ] }) 1706 return VideoModel.scope({ method: [ ScopeNames.AVAILABLE_FOR_LIST_IDS, scopeOptions ] })
1708 .findAll(query) 1707 .findAll(query)
1709 .then(rows => rows.map(r => r[ field ])) 1708 .then(rows => rows.map(r => r[field]))
1710 } 1709 }
1711 1710
1712 static buildTrendingQuery (trendingDays: number) { 1711 static buildTrendingQuery (trendingDays: number) {
@@ -1717,7 +1716,7 @@ export class VideoModel extends Model<VideoModel> {
1717 required: false, 1716 required: false,
1718 where: { 1717 where: {
1719 startDate: { 1718 startDate: {
1720 [ Op.gte ]: new Date(new Date().getTime() - (24 * 3600 * 1000) * trendingDays) 1719 [Op.gte]: new Date(new Date().getTime() - (24 * 3600 * 1000) * trendingDays)
1721 } 1720 }
1722 } 1721 }
1723 } 1722 }
@@ -1800,23 +1799,23 @@ export class VideoModel extends Model<VideoModel> {
1800 } 1799 }
1801 1800
1802 static getCategoryLabel (id: number) { 1801 static getCategoryLabel (id: number) {
1803 return VIDEO_CATEGORIES[ id ] || 'Misc' 1802 return VIDEO_CATEGORIES[id] || 'Misc'
1804 } 1803 }
1805 1804
1806 static getLicenceLabel (id: number) { 1805 static getLicenceLabel (id: number) {
1807 return VIDEO_LICENCES[ id ] || 'Unknown' 1806 return VIDEO_LICENCES[id] || 'Unknown'
1808 } 1807 }
1809 1808
1810 static getLanguageLabel (id: string) { 1809 static getLanguageLabel (id: string) {
1811 return VIDEO_LANGUAGES[ id ] || 'Unknown' 1810 return VIDEO_LANGUAGES[id] || 'Unknown'
1812 } 1811 }
1813 1812
1814 static getPrivacyLabel (id: number) { 1813 static getPrivacyLabel (id: number) {
1815 return VIDEO_PRIVACIES[ id ] || 'Unknown' 1814 return VIDEO_PRIVACIES[id] || 'Unknown'
1816 } 1815 }
1817 1816
1818 static getStateLabel (id: number) { 1817 static getStateLabel (id: number) {
1819 return VIDEO_STATES[ id ] || 'Unknown' 1818 return VIDEO_STATES[id] || 'Unknown'
1820 } 1819 }
1821 1820
1822 isBlacklisted () { 1821 isBlacklisted () {
@@ -1828,7 +1827,7 @@ export class VideoModel extends Model<VideoModel> {
1828 this.VideoChannel.Account.isBlocked() 1827 this.VideoChannel.Account.isBlocked()
1829 } 1828 }
1830 1829
1831 getQualityFileBy <T extends MVideoWithFile> (this: T, fun: (files: MVideoFile[], it: (file: MVideoFile) => number) => MVideoFile) { 1830 getQualityFileBy<T extends MVideoWithFile> (this: T, fun: (files: MVideoFile[], it: (file: MVideoFile) => number) => MVideoFile) {
1832 if (Array.isArray(this.VideoFiles) && this.VideoFiles.length !== 0) { 1831 if (Array.isArray(this.VideoFiles) && this.VideoFiles.length !== 0) {
1833 const file = fun(this.VideoFiles, file => file.resolution) 1832 const file = fun(this.VideoFiles, file => file.resolution)
1834 1833
@@ -1846,15 +1845,15 @@ export class VideoModel extends Model<VideoModel> {
1846 return undefined 1845 return undefined
1847 } 1846 }
1848 1847
1849 getMaxQualityFile <T extends MVideoWithFile> (this: T): MVideoFileVideo | MVideoFileStreamingPlaylistVideo { 1848 getMaxQualityFile<T extends MVideoWithFile> (this: T): MVideoFileVideo | MVideoFileStreamingPlaylistVideo {
1850 return this.getQualityFileBy(maxBy) 1849 return this.getQualityFileBy(maxBy)
1851 } 1850 }
1852 1851
1853 getMinQualityFile <T extends MVideoWithFile> (this: T): MVideoFileVideo | MVideoFileStreamingPlaylistVideo { 1852 getMinQualityFile<T extends MVideoWithFile> (this: T): MVideoFileVideo | MVideoFileStreamingPlaylistVideo {
1854 return this.getQualityFileBy(minBy) 1853 return this.getQualityFileBy(minBy)
1855 } 1854 }
1856 1855
1857 getWebTorrentFile <T extends MVideoWithFile> (this: T, resolution: number): MVideoFileVideo { 1856 getWebTorrentFile<T extends MVideoWithFile> (this: T, resolution: number): MVideoFileVideo {
1858 if (Array.isArray(this.VideoFiles) === false) return undefined 1857 if (Array.isArray(this.VideoFiles) === false) return undefined
1859 1858
1860 const file = this.VideoFiles.find(f => f.resolution === resolution) 1859 const file = this.VideoFiles.find(f => f.resolution === resolution)
@@ -1977,8 +1976,8 @@ export class VideoModel extends Model<VideoModel> {
1977 } 1976 }
1978 1977
1979 this.VideoStreamingPlaylists = this.VideoStreamingPlaylists 1978 this.VideoStreamingPlaylists = this.VideoStreamingPlaylists
1980 .filter(s => s.type !== VideoStreamingPlaylistType.HLS) 1979 .filter(s => s.type !== VideoStreamingPlaylistType.HLS)
1981 .concat(toAdd) 1980 .concat(toAdd)
1982 } 1981 }
1983 1982
1984 removeFile (videoFile: MVideoFile, isRedundancy = false) { 1983 removeFile (videoFile: MVideoFile, isRedundancy = false) {
@@ -1999,7 +1998,7 @@ export class VideoModel extends Model<VideoModel> {
1999 await remove(directoryPath) 1998 await remove(directoryPath)
2000 1999
2001 if (isRedundancy !== true) { 2000 if (isRedundancy !== true) {
2002 let streamingPlaylistWithFiles = streamingPlaylist as MStreamingPlaylistFilesVideo 2001 const streamingPlaylistWithFiles = streamingPlaylist as MStreamingPlaylistFilesVideo
2003 streamingPlaylistWithFiles.Video = this 2002 streamingPlaylistWithFiles.Video = this
2004 2003
2005 if (!Array.isArray(streamingPlaylistWithFiles.VideoFiles)) { 2004 if (!Array.isArray(streamingPlaylistWithFiles.VideoFiles)) {