+ [ScopeNames.FOR_API]: (options: ForAPIOptions) => {
+ const accountInclude = {
+ attributes: [ 'id', 'name' ],
+ model: AccountModel.unscoped(),
+ required: true,
+ include: [
+ {
+ attributes: [ 'id', 'uuid', 'preferredUsername', 'url', 'serverId', 'avatarId' ],
+ model: ActorModel.unscoped(),
+ required: true,
+ include: [
+ {
+ attributes: [ 'host' ],
+ model: ServerModel.unscoped(),
+ required: false
+ },
+ {
+ model: AvatarModel.unscoped(),
+ required: false
+ }
+ ]
+ }
+ ]
+ }
+
+ const videoChannelInclude = {
+ attributes: [ 'name', 'description', 'id' ],
+ model: VideoChannelModel.unscoped(),
+ required: true,
+ include: [
+ {
+ attributes: [ 'uuid', 'preferredUsername', 'url', 'serverId', 'avatarId' ],
+ model: ActorModel.unscoped(),
+ required: true,
+ include: [
+ {
+ attributes: [ 'host' ],
+ model: ServerModel.unscoped(),
+ required: false
+ },
+ {
+ model: AvatarModel.unscoped(),
+ required: false
+ }
+ ]
+ },
+ accountInclude
+ ]
+ }
+
+ const query: IFindOptions<VideoModel> = {
+ where: {
+ id: {
+ [Sequelize.Op.any]: options.ids
+ }
+ },
+ include: [ videoChannelInclude ]
+ }
+
+ if (options.withFiles === true) {
+ query.include.push({
+ model: VideoFileModel.unscoped(),
+ required: true
+ })
+ }
+
+ return query
+ },
+ [ScopeNames.AVAILABLE_FOR_LIST_IDS]: (options: AvailableForListIDsOptions) => {
+ const query: IFindOptions<VideoModel> = {
+ attributes: [ 'id' ],
+ where: {
+ id: {
+ [Sequelize.Op.and]: [
+ {
+ [ Sequelize.Op.notIn ]: Sequelize.literal(
+ '(SELECT "videoBlacklist"."videoId" FROM "videoBlacklist")'
+ )
+ }
+ ]
+ },
+ // Always list public videos
+ privacy: VideoPrivacy.PUBLIC,
+ // Always list published videos, or videos that are being transcoded but on which we don't want to wait for transcoding
+ [ Sequelize.Op.or ]: [
+ {
+ state: VideoState.PUBLISHED
+ },
+ {
+ [ Sequelize.Op.and ]: {
+ state: VideoState.TO_TRANSCODE,
+ waitTranscoding: false
+ }
+ }
+ ]
+ },
+ include: [ ]
+ }
+
+ if (options.filter || options.accountId || options.videoChannelId) {
+ const videoChannelInclude: IIncludeOptions = {
+ attributes: [],
+ model: VideoChannelModel.unscoped(),
+ required: true
+ }
+
+ if (options.videoChannelId) {
+ videoChannelInclude.where = {
+ id: options.videoChannelId
+ }
+ }
+
+ if (options.filter || options.accountId) {
+ const accountInclude: IIncludeOptions = {
+ attributes: [],
+ model: AccountModel.unscoped(),
+ required: true
+ }
+
+ if (options.filter) {
+ accountInclude.include = [
+ {
+ attributes: [],
+ model: ActorModel.unscoped(),
+ required: true,
+ where: VideoModel.buildActorWhereWithFilter(options.filter)
+ }
+ ]
+ }
+
+ if (options.accountId) {
+ accountInclude.where = { id: options.accountId }
+ }
+
+ videoChannelInclude.include = [ accountInclude ]
+ }
+
+ query.include.push(videoChannelInclude)
+ }
+
+ if (options.actorId) {
+ 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'
+ }
+
+ // Force actorId to be a number to avoid SQL injections
+ const actorIdNumber = parseInt(options.actorId.toString(), 10)
+ query.where['id'][Sequelize.Op.and].push({