X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;f=server%2Fmodels%2Fvideo%2Fvideo.ts;h=4e6f602aa8bdfce12b4db4f9c4aab4ad5159ade6;hb=4f0f2ab228d73dbec303914dd59b52f6cdaddf46;hp=329cebd28a6ed37db9d1b477fd10af8b4c325017;hpb=1735c825726edaa0af5035cb6cbb0cc0db502c6d;p=github%2FChocobozzz%2FPeerTube.git diff --git a/server/models/video/video.ts b/server/models/video/video.ts index 329cebd28..4e6f602aa 100644 --- a/server/models/video/video.ts +++ b/server/models/video/video.ts @@ -207,6 +207,8 @@ type AvailableForListIDsOptions = { followerActorId: number includeLocalVideos: boolean + withoutId?: boolean + filter?: VideoFilter categoryOneOf?: number[] nsfw?: boolean @@ -227,18 +229,23 @@ type AvailableForListIDsOptions = { historyOfUser?: UserModel } -@Scopes({ +@Scopes(() => ({ [ ScopeNames.FOR_API ]: (options: ForAPIOptions) => { const query: FindOptions = { where: { id: { - [ Op.in ]: options.ids // FIXME: sequelize any seems broken + [ Op.in ]: options.ids // FIXME: sequelize ANY seems broken } }, include: [ { model: VideoChannelModel.scope({ method: [ VideoChannelScopeNames.SUMMARY, true ] }), required: true + }, + { + attributes: [ 'type', 'filename' ], + model: ThumbnailModel, + required: false } ] } @@ -263,9 +270,11 @@ type AvailableForListIDsOptions = { return query }, [ ScopeNames.AVAILABLE_FOR_LIST_IDS ]: (options: AvailableForListIDsOptions) => { + const attributes = options.withoutId === true ? [] : [ 'id' ] + const query: FindOptions = { raw: true, - attributes: [ 'id' ], + attributes, where: { id: { [ Op.and ]: [ @@ -486,7 +495,7 @@ type AvailableForListIDsOptions = { [ ScopeNames.WITH_THUMBNAILS ]: { include: [ { - model: () => ThumbnailModel, + model: ThumbnailModel, required: false } ] @@ -495,48 +504,48 @@ type AvailableForListIDsOptions = { include: [ { attributes: [ 'accountId' ], - model: () => VideoChannelModel.unscoped(), + model: VideoChannelModel.unscoped(), required: true, include: [ { attributes: [ 'userId' ], - model: () => AccountModel.unscoped(), + model: AccountModel.unscoped(), required: true } ] } - ] as any // FIXME: sequelize typings + ] }, [ ScopeNames.WITH_ACCOUNT_DETAILS ]: { include: [ { - model: () => VideoChannelModel.unscoped(), + model: VideoChannelModel.unscoped(), required: true, include: [ { attributes: { exclude: [ 'privateKey', 'publicKey' ] }, - model: () => ActorModel.unscoped(), + model: ActorModel.unscoped(), required: true, include: [ { attributes: [ 'host' ], - model: () => ServerModel.unscoped(), + model: ServerModel.unscoped(), required: false }, { - model: () => AvatarModel.unscoped(), + model: AvatarModel.unscoped(), required: false } ] }, { - model: () => AccountModel.unscoped(), + model: AccountModel.unscoped(), required: true, include: [ { - model: () => ActorModel.unscoped(), + model: ActorModel.unscoped(), attributes: { exclude: [ 'privateKey', 'publicKey' ] }, @@ -544,11 +553,11 @@ type AvailableForListIDsOptions = { include: [ { attributes: [ 'host' ], - model: () => ServerModel.unscoped(), + model: ServerModel.unscoped(), required: false }, { - model: () => AvatarModel.unscoped(), + model: AvatarModel.unscoped(), required: false } ] @@ -557,16 +566,16 @@ type AvailableForListIDsOptions = { } ] } - ] as any // FIXME: sequelize typings + ] }, [ ScopeNames.WITH_TAGS ]: { - include: [ () => TagModel ] + include: [ TagModel ] }, [ ScopeNames.WITH_BLACKLISTED ]: { include: [ { attributes: [ 'id', 'reason' ], - model: () => VideoBlacklistModel, + model: VideoBlacklistModel, required: false } ] @@ -588,8 +597,7 @@ type AvailableForListIDsOptions = { include: [ { model: VideoFileModel.unscoped(), - // FIXME: typings - [ 'separate' as any ]: true, // We may have multiple files, having multiple redundancies so let's separate this join + separate: true, // We may have multiple files, having multiple redundancies so let's separate this join required: false, include: subInclude } @@ -613,8 +621,7 @@ type AvailableForListIDsOptions = { include: [ { model: VideoStreamingPlaylistModel.unscoped(), - // FIXME: typings - [ 'separate' as any ]: true, // We may have multiple streaming playlists, having multiple redundancies so let's separate this join + separate: true, // We may have multiple streaming playlists, having multiple redundancies so let's separate this join required: false, include: subInclude } @@ -624,7 +631,7 @@ type AvailableForListIDsOptions = { [ ScopeNames.WITH_SCHEDULED_UPDATE ]: { include: [ { - model: () => ScheduleVideoUpdateModel.unscoped(), + model: ScheduleVideoUpdateModel.unscoped(), required: false } ] @@ -643,7 +650,7 @@ type AvailableForListIDsOptions = { ] } } -}) +})) @Table({ tableName: 'video', indexes @@ -1075,15 +1082,14 @@ export class VideoModel extends Model { } return Bluebird.all([ - // FIXME: typing issue - VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findAll(query as any), - VideoModel.sequelize.query<{ total: number }>(rawCountQuery, { type: QueryTypes.SELECT }) + VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findAll(query), + VideoModel.sequelize.query<{ total: string }>(rawCountQuery, { type: QueryTypes.SELECT }) ]).then(([ rows, totals ]) => { // totals: totalVideos + totalVideoShares let totalVideos = 0 let totalVideoShares = 0 - if (totals[ 0 ]) totalVideos = parseInt(totals[ 0 ].total + '', 10) - if (totals[ 1 ]) totalVideoShares = parseInt(totals[ 1 ].total + '', 10) + if (totals[ 0 ]) totalVideos = parseInt(totals[ 0 ].total, 10) + if (totals[ 1 ]) totalVideoShares = parseInt(totals[ 1 ].total, 10) const total = totalVideos + totalVideoShares return { @@ -1094,50 +1100,58 @@ export class VideoModel extends Model { } static listUserVideosForApi (accountId: number, start: number, count: number, sort: string, withFiles = false) { - const query: FindOptions = { - offset: start, - limit: count, - order: getVideoSort(sort), - include: [ - { - model: VideoChannelModel, - required: true, - include: [ - { - model: AccountModel, - where: { - id: accountId - }, - required: true - } - ] - }, - { - model: ScheduleVideoUpdateModel, - required: false - }, - { - model: VideoBlacklistModel, - required: false - } - ] + function buildBaseQuery (): FindOptions { + return { + offset: start, + limit: count, + order: getVideoSort(sort), + include: [ + { + model: VideoChannelModel, + required: true, + include: [ + { + model: AccountModel, + where: { + id: accountId + }, + required: true + } + ] + } + ] + } } + const countQuery = buildBaseQuery() + const findQuery = buildBaseQuery() + + findQuery.include.push({ + model: ScheduleVideoUpdateModel, + required: false + }) + + findQuery.include.push({ + model: VideoBlacklistModel, + required: false + }) + if (withFiles === true) { - query.include.push({ + findQuery.include.push({ model: VideoFileModel.unscoped(), required: true }) } - return VideoModel.scope(ScopeNames.WITH_THUMBNAILS) - .findAndCountAll(query) - .then(({ rows, count }) => { - return { - data: rows, - total: count - } - }) + return Promise.all([ + VideoModel.count(countQuery), + VideoModel.findAll(findQuery) + ]).then(([ count, rows ]) => { + return { + data: rows, + total: count + } + }) } static async listForApi (options: { @@ -1404,12 +1418,12 @@ export class VideoModel extends Model { const where = buildWhereIdOrUUID(id) const options = { - order: [ [ 'Tags', 'name', 'ASC' ] ] as any, // FIXME: sequelize typings + order: [ [ 'Tags', 'name', 'ASC' ] ] as any, where, transaction: t } - const scopes = [ + const scopes: (string | ScopeOptions)[] = [ ScopeNames.WITH_TAGS, ScopeNames.WITH_BLACKLISTED, ScopeNames.WITH_ACCOUNT_DETAILS, @@ -1420,7 +1434,7 @@ export class VideoModel extends Model { ] if (userId) { - scopes.push({ method: [ ScopeNames.WITH_USER_HISTORY, userId ] } as any) // FIXME: typings + scopes.push({ method: [ ScopeNames.WITH_USER_HISTORY, userId ] }) } return VideoModel @@ -1437,18 +1451,18 @@ export class VideoModel extends Model { transaction: t } - const scopes = [ + const scopes: (string | ScopeOptions)[] = [ ScopeNames.WITH_TAGS, ScopeNames.WITH_BLACKLISTED, ScopeNames.WITH_ACCOUNT_DETAILS, ScopeNames.WITH_SCHEDULED_UPDATE, ScopeNames.WITH_THUMBNAILS, - { method: [ ScopeNames.WITH_FILES, true ] } as any, // FIXME: typings - { method: [ ScopeNames.WITH_STREAMING_PLAYLISTS, true ] } as any // FIXME: typings + { method: [ ScopeNames.WITH_FILES, true ] }, + { method: [ ScopeNames.WITH_STREAMING_PLAYLISTS, true ] } ] if (userId) { - scopes.push({ method: [ ScopeNames.WITH_USER_HISTORY, userId ] } as any) // FIXME: typings + scopes.push({ method: [ ScopeNames.WITH_USER_HISTORY, userId ] }) } return VideoModel @@ -1513,16 +1527,17 @@ export class VideoModel extends Model { const scopeOptions: AvailableForListIDsOptions = { serverAccountId: serverActor.Account.id, followerActorId, - includeLocalVideos: true + includeLocalVideos: true, + withoutId: true // Don't break aggregation } const query: FindOptions = { attributes: [ field ], limit: count, group: field, - having: Sequelize.where(Sequelize.fn('COUNT', Sequelize.col(field)), { - [ Op.gte ]: threshold - }) as any, // FIXME: typings + having: Sequelize.where( + Sequelize.fn('COUNT', Sequelize.col(field)), { [ Op.gte ]: threshold } + ), order: [ (this.sequelize as any).random() ] } @@ -1594,16 +1609,10 @@ export class VideoModel extends Model { ] } - // FIXME: typing - const apiScope: any[] = [ ScopeNames.WITH_THUMBNAILS ] + const apiScope: (string | ScopeOptions)[] = [] if (options.user) { apiScope.push({ method: [ ScopeNames.WITH_USER_HISTORY, options.user.id ] }) - - // Even if the relation is n:m, we know that a user only have 0..1 video history - // So we won't have multiple rows for the same video - // A subquery adds some bugs in our query so disable it - secondQuery.subQuery = false } apiScope.push({ @@ -1651,13 +1660,17 @@ export class VideoModel extends Model { return maxBy(this.VideoFiles, file => file.resolution) } - addThumbnail (thumbnail: ThumbnailModel) { + async addAndSaveThumbnail (thumbnail: ThumbnailModel, transaction: Transaction) { + thumbnail.videoId = this.id + + const savedThumbnail = await thumbnail.save({ transaction }) + if (Array.isArray(this.Thumbnails) === false) this.Thumbnails = [] // Already have this thumbnail, skip - if (this.Thumbnails.find(t => t.id === thumbnail.id)) return + if (this.Thumbnails.find(t => t.id === savedThumbnail.id)) return - this.Thumbnails.push(thumbnail) + this.Thumbnails.push(savedThumbnail) } getVideoFilename (videoFile: VideoFileModel) { @@ -1668,10 +1681,10 @@ export class VideoModel extends Model { return this.uuid + '.jpg' } - getThumbnail () { + getMiniature () { if (Array.isArray(this.Thumbnails) === false) return undefined - return this.Thumbnails.find(t => t.type === ThumbnailType.THUMBNAIL) + return this.Thumbnails.find(t => t.type === ThumbnailType.MINIATURE) } generatePreviewName () { @@ -1732,8 +1745,8 @@ export class VideoModel extends Model { return '/videos/embed/' + this.uuid } - getThumbnailStaticPath () { - const thumbnail = this.getThumbnail() + getMiniatureStaticPath () { + const thumbnail = this.getMiniature() if (!thumbnail) return null return join(STATIC_PATHS.THUMBNAILS, thumbnail.filename)