From 418d092afa81e2c8fe8ac6838fc4b5eb0af6a782 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 26 Feb 2019 10:55:40 +0100 Subject: Playlist server API --- server/models/video/video-playlist-element.ts | 231 ++++++++++++++++++++++++++ 1 file changed, 231 insertions(+) create mode 100644 server/models/video/video-playlist-element.ts (limited to 'server/models/video/video-playlist-element.ts') diff --git a/server/models/video/video-playlist-element.ts b/server/models/video/video-playlist-element.ts new file mode 100644 index 000000000..d76149d12 --- /dev/null +++ b/server/models/video/video-playlist-element.ts @@ -0,0 +1,231 @@ +import { + AllowNull, + BelongsTo, + Column, + CreatedAt, + DataType, + Default, + ForeignKey, + Is, + IsInt, + Min, + Model, + Table, + UpdatedAt +} from 'sequelize-typescript' +import { VideoModel } from './video' +import { VideoPlaylistModel } from './video-playlist' +import * as Sequelize from 'sequelize' +import { getSort, throwIfNotValid } from '../utils' +import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' +import { CONSTRAINTS_FIELDS } from '../../initializers' +import { PlaylistElementObject } from '../../../shared/models/activitypub/objects/playlist-element-object' + +@Table({ + tableName: 'videoPlaylistElement', + indexes: [ + { + fields: [ 'videoPlaylistId' ] + }, + { + fields: [ 'videoId' ] + }, + { + fields: [ 'videoPlaylistId', 'videoId' ], + unique: true + }, + { + fields: [ 'videoPlaylistId', 'position' ], + unique: true + }, + { + fields: [ 'url' ], + unique: true + } + ] +}) +export class VideoPlaylistElementModel extends Model { + @CreatedAt + createdAt: Date + + @UpdatedAt + updatedAt: Date + + @AllowNull(false) + @Is('VideoPlaylistUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'url')) + @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEO_PLAYLISTS.URL.max)) + url: string + + @AllowNull(false) + @Default(1) + @IsInt + @Min(1) + @Column + position: number + + @AllowNull(true) + @IsInt + @Min(0) + @Column + startTimestamp: number + + @AllowNull(true) + @IsInt + @Min(0) + @Column + stopTimestamp: number + + @ForeignKey(() => VideoPlaylistModel) + @Column + videoPlaylistId: number + + @BelongsTo(() => VideoPlaylistModel, { + foreignKey: { + allowNull: false + }, + onDelete: 'CASCADE' + }) + VideoPlaylist: VideoPlaylistModel + + @ForeignKey(() => VideoModel) + @Column + videoId: number + + @BelongsTo(() => VideoModel, { + foreignKey: { + allowNull: false + }, + onDelete: 'CASCADE' + }) + Video: VideoModel + + static deleteAllOf (videoPlaylistId: number, transaction?: Sequelize.Transaction) { + const query = { + where: { + videoPlaylistId + }, + transaction + } + + return VideoPlaylistElementModel.destroy(query) + } + + static loadByPlaylistAndVideo (videoPlaylistId: number, videoId: number) { + const query = { + where: { + videoPlaylistId, + videoId + } + } + + return VideoPlaylistElementModel.findOne(query) + } + + static loadByPlaylistAndVideoForAP (playlistId: number | string, videoId: number | string) { + const playlistWhere = validator.isUUID('' + playlistId) ? { uuid: playlistId } : { id: playlistId } + const videoWhere = validator.isUUID('' + videoId) ? { uuid: videoId } : { id: videoId } + + const query = { + include: [ + { + attributes: [ 'privacy' ], + model: VideoPlaylistModel.unscoped(), + where: playlistWhere + }, + { + attributes: [ 'url' ], + model: VideoModel.unscoped(), + where: videoWhere + } + ] + } + + return VideoPlaylistElementModel.findOne(query) + } + + static listUrlsOfForAP (videoPlaylistId: number, start: number, count: number) { + const query = { + attributes: [ 'url' ], + offset: start, + limit: count, + order: getSort('position'), + where: { + videoPlaylistId + } + } + + return VideoPlaylistElementModel + .findAndCountAll(query) + .then(({ rows, count }) => { + return { total: count, data: rows.map(e => e.url) } + }) + } + + static getNextPositionOf (videoPlaylistId: number, transaction?: Sequelize.Transaction) { + const query = { + where: { + videoPlaylistId + }, + transaction + } + + return VideoPlaylistElementModel.max('position', query) + .then(position => position ? position + 1 : 1) + } + + static reassignPositionOf ( + videoPlaylistId: number, + firstPosition: number, + endPosition: number, + newPosition: number, + transaction?: Sequelize.Transaction + ) { + const query = { + where: { + videoPlaylistId, + position: { + [Sequelize.Op.gte]: firstPosition, + [Sequelize.Op.lte]: endPosition + } + }, + transaction + } + + return VideoPlaylistElementModel.update({ position: Sequelize.literal(`${newPosition} + "position" - ${firstPosition}`) }, query) + } + + static increasePositionOf ( + videoPlaylistId: number, + fromPosition: number, + toPosition?: number, + by = 1, + transaction?: Sequelize.Transaction + ) { + const query = { + where: { + videoPlaylistId, + position: { + [Sequelize.Op.gte]: fromPosition + } + }, + transaction + } + + return VideoPlaylistElementModel.increment({ position: by }, query) + } + + toActivityPubObject (): PlaylistElementObject { + const base: PlaylistElementObject = { + id: this.url, + type: 'PlaylistElement', + + url: this.Video.url, + position: this.position + } + + if (this.startTimestamp) base.startTimestamp = this.startTimestamp + if (this.stopTimestamp) base.stopTimestamp = this.stopTimestamp + + return base + } +} -- cgit v1.2.3