X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;f=server%2Fmodels%2Fvideo%2Fvideo-playlist-element.ts;h=b832f9768c04a66c0160ac03c47cafc3f8a59e37;hb=0c9668f77901e7540e2c7045eb0f2974a4842a69;hp=a2802131349715f0949d0bc313184342859558e5;hpb=001ed2d40c8d2c8f494f5dc7f91ed62d56df10fd;p=github%2FChocobozzz%2FPeerTube.git diff --git a/server/models/video/video-playlist-element.ts b/server/models/video/video-playlist-element.ts index a28021313..b832f9768 100644 --- a/server/models/video/video-playlist-element.ts +++ b/server/models/video/video-playlist-element.ts @@ -1,3 +1,4 @@ +import { AggregateOptions, Op, ScopeOptions, Sequelize, Transaction } from 'sequelize' import { AllowNull, BelongsTo, @@ -13,26 +14,26 @@ import { Table, UpdatedAt } from 'sequelize-typescript' -import { ForAPIOptions, ScopeNames as VideoScopeNames, VideoModel } from './video' -import { VideoPlaylistModel } from './video-playlist' -import { getSort, throwIfNotValid } from '../utils' -import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' -import { CONSTRAINTS_FIELDS } from '../../initializers/constants' -import { PlaylistElementObject } from '../../../shared/models/activitypub/objects/playlist-element-object' -import * as validator from 'validator' -import { AggregateOptions, Op, ScopeOptions, Sequelize, Transaction } from 'sequelize' -import { VideoPlaylistElement, VideoPlaylistElementType } from '../../../shared/models/videos/playlist/video-playlist-element.model' -import { AccountModel } from '../account/account' -import { VideoPrivacy } from '../../../shared/models/videos' -import * as Bluebird from 'bluebird' +import validator from 'validator' +import { MUserAccountId } from '@server/types/models' import { MVideoPlaylistElement, MVideoPlaylistElementAP, MVideoPlaylistElementFormattable, MVideoPlaylistElementVideoUrlPlaylistPrivacy, MVideoPlaylistVideoThumbnail -} from '@server/typings/models/video/video-playlist-element' -import { MUserAccountId } from '@server/typings/models' +} from '@server/types/models/video/video-playlist-element' +import { forceNumber } from '@shared/core-utils' +import { AttributesOnly } from '@shared/typescript-utils' +import { PlaylistElementObject } from '../../../shared/models/activitypub/objects/playlist-element-object' +import { VideoPrivacy } from '../../../shared/models/videos' +import { VideoPlaylistElement, VideoPlaylistElementType } from '../../../shared/models/videos/playlist/video-playlist-element.model' +import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' +import { CONSTRAINTS_FIELDS } from '../../initializers/constants' +import { AccountModel } from '../account/account' +import { getSort, throwIfNotValid } from '../shared' +import { ForAPIOptions, ScopeNames as VideoScopeNames, VideoModel } from './video' +import { VideoPlaylistModel } from './video-playlist' @Table({ tableName: 'videoPlaylistElement', @@ -43,25 +44,21 @@ import { MUserAccountId } from '@server/typings/models' { fields: [ 'videoId' ] }, - { - fields: [ 'videoPlaylistId', 'videoId' ], - unique: true - }, { fields: [ 'url' ], unique: true } ] }) -export class VideoPlaylistElementModel extends Model { +export class VideoPlaylistElementModel extends Model>> { @CreatedAt createdAt: Date @UpdatedAt updatedAt: Date - @AllowNull(false) - @Is('VideoPlaylistUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'url')) + @AllowNull(true) + @Is('VideoPlaylistUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'url', true)) @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEO_PLAYLISTS.URL.max)) url: string @@ -120,10 +117,10 @@ export class VideoPlaylistElementModel extends Model } static listForApi (options: { - start: number, - count: number, - videoPlaylistId: number, - serverAccount: AccountModel, + start: number + count: number + videoPlaylistId: number + serverAccount: AccountModel user?: MUserAccountId }) { const accountIds = [ options.serverAccount.id ] @@ -170,7 +167,7 @@ export class VideoPlaylistElementModel extends Model ]).then(([ total, data ]) => ({ total, data })) } - static loadByPlaylistAndVideo (videoPlaylistId: number, videoId: number): Bluebird { + static loadByPlaylistAndVideo (videoPlaylistId: number, videoId: number): Promise { const query = { where: { videoPlaylistId, @@ -181,16 +178,17 @@ export class VideoPlaylistElementModel extends Model return VideoPlaylistElementModel.findOne(query) } - static loadById (playlistElementId: number): Bluebird { + static loadById (playlistElementId: number | string): Promise { return VideoPlaylistElementModel.findByPk(playlistElementId) } - static loadByPlaylistAndVideoForAP ( + static loadByPlaylistAndElementIdForAP ( playlistId: number | string, - videoId: number | string - ): Bluebird { - const playlistWhere = validator.isUUID('' + playlistId) ? { uuid: playlistId } : { id: playlistId } - const videoWhere = validator.isUUID('' + videoId) ? { uuid: videoId } : { id: videoId } + playlistElementId: number + ): Promise { + const playlistWhere = validator.isUUID('' + playlistId) + ? { uuid: playlistId } + : { id: playlistId } const query = { include: [ @@ -201,35 +199,43 @@ export class VideoPlaylistElementModel extends Model }, { attributes: [ 'url' ], - model: VideoModel.unscoped(), - where: videoWhere + model: VideoModel.unscoped() } - ] + ], + where: { + id: playlistElementId + } } return VideoPlaylistElementModel.findOne(query) } static listUrlsOfForAP (videoPlaylistId: number, start: number, count: number, t?: Transaction) { - const query = { - attributes: [ 'url' ], - offset: start, - limit: count, - order: getSort('position'), - where: { - videoPlaylistId - }, - transaction: t + const getQuery = (forCount: boolean) => { + return { + attributes: forCount + ? [] + : [ 'url' ], + offset: start, + limit: count, + order: getSort('position'), + where: { + videoPlaylistId + }, + transaction: t + } } - return VideoPlaylistElementModel - .findAndCountAll(query) - .then(({ rows, count }) => { - return { total: count, data: rows.map(e => e.url) } - }) + return Promise.all([ + VideoPlaylistElementModel.count(getQuery(true)), + VideoPlaylistElementModel.findAll(getQuery(false)) + ]).then(([ total, rows ]) => ({ + total, + data: rows.map(e => e.url) + })) } - static loadFirstElementWithVideoThumbnail (videoPlaylistId: number): Bluebird { + static loadFirstElementWithVideoThumbnail (videoPlaylistId: number): Promise { const query = { order: getSort('position'), where: { @@ -259,13 +265,15 @@ export class VideoPlaylistElementModel extends Model .then(position => position ? position + 1 : 1) } - static reassignPositionOf ( - videoPlaylistId: number, - firstPosition: number, - endPosition: number, - newPosition: number, + static reassignPositionOf (options: { + videoPlaylistId: number + firstPosition: number + endPosition: number + newPosition: number transaction?: Transaction - ) { + }) { + const { videoPlaylistId, firstPosition, endPosition, newPosition, transaction } = options + const query = { where: { videoPlaylistId, @@ -278,13 +286,13 @@ export class VideoPlaylistElementModel extends Model validate: false // We use a literal to update the position } - return VideoPlaylistElementModel.update({ position: Sequelize.literal(`${newPosition} + "position" - ${firstPosition}`) }, query) + const positionQuery = Sequelize.literal(`${forceNumber(newPosition)} + "position" - ${forceNumber(firstPosition)}`) + return VideoPlaylistElementModel.update({ position: positionQuery }, query) } static increasePositionOf ( videoPlaylistId: number, fromPosition: number, - toPosition?: number, by = 1, transaction?: Transaction ) { @@ -301,7 +309,23 @@ export class VideoPlaylistElementModel extends Model return VideoPlaylistElementModel.increment({ position: by }, query) } - getType (this: MVideoPlaylistElementFormattable, displayNSFW?: boolean, accountId?: number) { + toFormattedJSON ( + this: MVideoPlaylistElementFormattable, + options: { accountId?: number } = {} + ): VideoPlaylistElement { + return { + id: this.id, + position: this.position, + startTimestamp: this.startTimestamp, + stopTimestamp: this.stopTimestamp, + + type: this.getType(options.accountId), + + video: this.getVideoElement(options.accountId) + } + } + + getType (this: MVideoPlaylistElementFormattable, accountId?: number) { const video = this.Video if (!video) return VideoPlaylistElementType.DELETED @@ -309,43 +333,29 @@ export class VideoPlaylistElementModel extends Model // Owned video, don't filter it if (accountId && video.VideoChannel.Account.id === accountId) return VideoPlaylistElementType.REGULAR - if (video.privacy === VideoPrivacy.PRIVATE) return VideoPlaylistElementType.PRIVATE + // Internal video? + if (video.privacy === VideoPrivacy.INTERNAL && accountId) return VideoPlaylistElementType.REGULAR + + if (video.privacy === VideoPrivacy.PRIVATE || video.privacy === VideoPrivacy.INTERNAL) return VideoPlaylistElementType.PRIVATE if (video.isBlacklisted() || video.isBlocked()) return VideoPlaylistElementType.UNAVAILABLE - if (video.nsfw === true && displayNSFW === false) return VideoPlaylistElementType.UNAVAILABLE return VideoPlaylistElementType.REGULAR } - getVideoElement (this: MVideoPlaylistElementFormattable, displayNSFW?: boolean, accountId?: number) { + getVideoElement (this: MVideoPlaylistElementFormattable, accountId?: number) { if (!this.Video) return null - if (this.getType(displayNSFW, accountId) !== VideoPlaylistElementType.REGULAR) return null + if (this.getType(accountId) !== VideoPlaylistElementType.REGULAR) return null return this.Video.toFormattedJSON() } - toFormattedJSON ( - this: MVideoPlaylistElementFormattable, - options: { displayNSFW?: boolean, accountId?: number } = {} - ): VideoPlaylistElement { - return { - id: this.id, - position: this.position, - startTimestamp: this.startTimestamp, - stopTimestamp: this.stopTimestamp, - - type: this.getType(options.displayNSFW, options.accountId), - - video: this.getVideoElement(options.displayNSFW, options.accountId) - } - } - toActivityPubObject (this: MVideoPlaylistElementAP): PlaylistElementObject { const base: PlaylistElementObject = { id: this.url, type: 'PlaylistElement', - url: this.Video.url, + url: this.Video?.url || null, position: this.position }