From 06a05d5f4784a7cbb27aa1188385b5679845dad8 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Thu, 16 Aug 2018 15:25:20 +0200 Subject: Add subscriptions endpoints to REST API --- server/models/video/video-channel.ts | 41 ++++++++++++++++++++++++++++++------ server/models/video/video.ts | 31 ++++++++++++++++++--------- 2 files changed, 56 insertions(+), 16 deletions(-) (limited to 'server/models/video') diff --git a/server/models/video/video-channel.ts b/server/models/video/video-channel.ts index d0dba18d5..0273fab13 100644 --- a/server/models/video/video-channel.ts +++ b/server/models/video/video-channel.ts @@ -1,14 +1,27 @@ import { - AllowNull, BeforeDestroy, BelongsTo, Column, CreatedAt, DefaultScope, ForeignKey, HasMany, Is, Model, Scopes, Table, - UpdatedAt, Default, DataType + AllowNull, + BeforeDestroy, + BelongsTo, + Column, + CreatedAt, + DataType, + Default, + DefaultScope, + ForeignKey, + HasMany, + Is, + Model, + Scopes, + Table, + UpdatedAt } from 'sequelize-typescript' import { ActivityPubActor } from '../../../shared/models/activitypub' import { VideoChannel } from '../../../shared/models/videos' import { - isVideoChannelDescriptionValid, isVideoChannelNameValid, + isVideoChannelDescriptionValid, + isVideoChannelNameValid, isVideoChannelSupportValid } from '../../helpers/custom-validators/video-channels' -import { logger } from '../../helpers/logger' import { sendDeleteActor } from '../../lib/activitypub/send' import { AccountModel } from '../account/account' import { ActorModel } from '../activitypub/actor' @@ -241,6 +254,23 @@ export class VideoChannelModel extends Model { .findById(id, options) } + static loadLocalByName (name: string) { + const query = { + include: [ + { + model: ActorModel, + required: true, + where: { + preferredUsername: name, + serverId: null + } + } + ] + } + + return VideoChannelModel.findOne(query) + } + toFormattedJSON (): VideoChannel { const actor = this.Actor.toFormattedJSON() const videoChannel = { @@ -251,8 +281,7 @@ export class VideoChannelModel extends Model { isLocal: this.Actor.isOwned(), createdAt: this.createdAt, updatedAt: this.updatedAt, - ownerAccount: undefined, - videos: undefined + ownerAccount: undefined } if (this.Account) videoChannel.ownerAccount = this.Account.toFormattedJSON() diff --git a/server/models/video/video.ts b/server/models/video/video.ts index b13dee403..5db718061 100644 --- a/server/models/video/video.ts +++ b/server/models/video/video.ts @@ -133,6 +133,7 @@ export enum ScopeNames { type AvailableForListOptions = { actorId: number, + includeLocalVideos: boolean, filter?: VideoFilter, categoryOneOf?: number[], nsfw?: boolean, @@ -201,6 +202,15 @@ type AvailableForListOptions = { // Force actorId to be a number to avoid SQL injections const actorIdNumber = parseInt(options.actorId.toString(), 10) + let localVideosReq = '' + if (options.includeLocalVideos === true) { + localVideosReq = ' UNION ALL ' + + 'SELECT "video"."id" AS "id" FROM "video" ' + + 'INNER JOIN "videoChannel" ON "videoChannel"."id" = "video"."channelId" ' + + 'INNER JOIN "account" ON "account"."id" = "videoChannel"."accountId" ' + + 'INNER JOIN "actor" ON "account"."actorId" = "actor"."id" ' + + 'WHERE "actor"."serverId" IS NULL' + } // FIXME: It would be more efficient to use a CTE so we join AFTER the filters, but sequelize does not support it... const query: IFindOptions = { @@ -214,12 +224,6 @@ type AvailableForListOptions = { 'SELECT "videoShare"."videoId" AS "id" FROM "videoShare" ' + 'INNER JOIN "actorFollow" ON "actorFollow"."targetActorId" = "videoShare"."actorId" ' + 'WHERE "actorFollow"."actorId" = ' + actorIdNumber + - ' UNION ' + - 'SELECT "video"."id" AS "id" FROM "video" ' + - 'INNER JOIN "videoChannel" ON "videoChannel"."id" = "video"."channelId" ' + - 'INNER JOIN "account" ON "account"."id" = "videoChannel"."accountId" ' + - 'INNER JOIN "actor" ON "account"."actorId" = "actor"."id" ' + - 'WHERE "actor"."serverId" IS NULL ' + ' UNION ALL ' + 'SELECT "video"."id" AS "id" FROM "video" ' + 'INNER JOIN "videoChannel" ON "videoChannel"."id" = "video"."channelId" ' + @@ -227,6 +231,7 @@ type AvailableForListOptions = { 'INNER JOIN "actor" ON "account"."actorId" = "actor"."id" ' + 'INNER JOIN "actorFollow" ON "actorFollow"."targetActorId" = "actor"."id" ' + 'WHERE "actorFollow"."actorId" = ' + actorIdNumber + + localVideosReq + ')' ) }, @@ -825,6 +830,7 @@ export class VideoModel extends Model { count: number, sort: string, nsfw: boolean, + includeLocalVideos: boolean, withFiles: boolean, categoryOneOf?: number[], licenceOneOf?: number[], @@ -833,7 +839,8 @@ export class VideoModel extends Model { tagsAllOf?: string[], filter?: VideoFilter, accountId?: number, - videoChannelId?: number + videoChannelId?: number, + actorId?: number }) { const query = { offset: options.start, @@ -841,11 +848,12 @@ export class VideoModel extends Model { order: getSort(options.sort) } - const serverActor = await getServerActor() + const actorId = options.actorId || (await getServerActor()).id + const scopes = { method: [ ScopeNames.AVAILABLE_FOR_LIST, { - actorId: serverActor.id, + actorId, nsfw: options.nsfw, categoryOneOf: options.categoryOneOf, licenceOneOf: options.licenceOneOf, @@ -855,7 +863,8 @@ export class VideoModel extends Model { filter: options.filter, withFiles: options.withFiles, accountId: options.accountId, - videoChannelId: options.videoChannelId + videoChannelId: options.videoChannelId, + includeLocalVideos: options.includeLocalVideos } as AvailableForListOptions ] } @@ -871,6 +880,7 @@ export class VideoModel extends Model { } static async searchAndPopulateAccountAndServer (options: { + includeLocalVideos: boolean search?: string start?: number count?: number @@ -955,6 +965,7 @@ export class VideoModel extends Model { method: [ ScopeNames.AVAILABLE_FOR_LIST, { actorId: serverActor.id, + includeLocalVideos: options.includeLocalVideos, nsfw: options.nsfw, categoryOneOf: options.categoryOneOf, licenceOneOf: options.licenceOneOf, -- cgit v1.2.3