+ static async listForApi (options: {
+ start: number,
+ count: number,
+ sort: string,
+ nsfw: boolean,
+ includeLocalVideos: boolean,
+ withFiles: boolean,
+ categoryOneOf?: number[],
+ licenceOneOf?: number[],
+ languageOneOf?: string[],
+ tagsOneOf?: string[],
+ tagsAllOf?: string[],
+ filter?: VideoFilter,
+ accountId?: number,
+ videoChannelId?: number,
+ followerActorId?: number
+ videoPlaylistId?: number,
+ trendingDays?: number,
+ user?: UserModel,
+ historyOfUser?: UserModel
+ }, countVideos = true) {
+ if (options.filter && options.filter === 'all-local' && !options.user.hasRight(UserRight.SEE_ALL_VIDEOS)) {
+ throw new Error('Try to filter all-local but no user has not the see all videos right')
+ }
+
+ const query: FindOptions & { where?: null } = {
+ offset: options.start,
+ limit: options.count,
+ order: getVideoSort(options.sort)
+ }
+
+ let trendingDays: number
+ if (options.sort.endsWith('trending')) {
+ trendingDays = CONFIG.TRENDING.VIDEOS.INTERVAL_DAYS
+
+ query.group = 'VideoModel.id'
+ }
+
+ const serverActor = await getServerActor()
+
+ // followerActorId === null has a meaning, so just check undefined
+ const followerActorId = options.followerActorId !== undefined ? options.followerActorId : serverActor.id
+
+ const queryOptions = {
+ followerActorId,
+ serverAccountId: serverActor.Account.id,
+ nsfw: options.nsfw,
+ categoryOneOf: options.categoryOneOf,
+ licenceOneOf: options.licenceOneOf,
+ languageOneOf: options.languageOneOf,
+ tagsOneOf: options.tagsOneOf,
+ tagsAllOf: options.tagsAllOf,
+ filter: options.filter,
+ withFiles: options.withFiles,
+ accountId: options.accountId,
+ videoChannelId: options.videoChannelId,
+ videoPlaylistId: options.videoPlaylistId,
+ includeLocalVideos: options.includeLocalVideos,
+ user: options.user,
+ historyOfUser: options.historyOfUser,
+ trendingDays
+ }
+
+ return VideoModel.getAvailableForApi(query, queryOptions, countVideos)
+ }
+
+ static async searchAndPopulateAccountAndServer (options: {
+ includeLocalVideos: boolean
+ search?: string
+ start?: number
+ count?: number
+ sort?: string
+ startDate?: string // ISO 8601
+ endDate?: string // ISO 8601
+ originallyPublishedStartDate?: string
+ originallyPublishedEndDate?: string
+ nsfw?: boolean
+ categoryOneOf?: number[]
+ licenceOneOf?: number[]
+ languageOneOf?: string[]
+ tagsOneOf?: string[]
+ tagsAllOf?: string[]
+ durationMin?: number // seconds
+ durationMax?: number // seconds
+ user?: UserModel,
+ filter?: VideoFilter
+ }) {
+ const whereAnd = []
+
+ if (options.startDate || options.endDate) {
+ const publishedAtRange = {}
+
+ if (options.startDate) publishedAtRange[ Op.gte ] = options.startDate
+ if (options.endDate) publishedAtRange[ Op.lte ] = options.endDate
+
+ whereAnd.push({ publishedAt: publishedAtRange })
+ }
+
+ if (options.originallyPublishedStartDate || options.originallyPublishedEndDate) {
+ const originallyPublishedAtRange = {}
+
+ if (options.originallyPublishedStartDate) originallyPublishedAtRange[ Op.gte ] = options.originallyPublishedStartDate
+ if (options.originallyPublishedEndDate) originallyPublishedAtRange[ Op.lte ] = options.originallyPublishedEndDate
+
+ whereAnd.push({ originallyPublishedAt: originallyPublishedAtRange })
+ }
+
+ if (options.durationMin || options.durationMax) {
+ const durationRange = {}
+
+ if (options.durationMin) durationRange[ Op.gte ] = options.durationMin
+ if (options.durationMax) durationRange[ Op.lte ] = options.durationMax
+
+ whereAnd.push({ duration: durationRange })
+ }
+
+ const attributesInclude = []
+ const escapedSearch = VideoModel.sequelize.escape(options.search)
+ const escapedLikeSearch = VideoModel.sequelize.escape('%' + options.search + '%')
+ if (options.search) {
+ whereAnd.push(
+ {
+ id: {
+ [ Op.in ]: Sequelize.literal(
+ '(' +
+ 'SELECT "video"."id" FROM "video" ' +
+ 'WHERE ' +
+ 'lower(immutable_unaccent("video"."name")) % lower(immutable_unaccent(' + escapedSearch + ')) OR ' +
+ 'lower(immutable_unaccent("video"."name")) LIKE lower(immutable_unaccent(' + escapedLikeSearch + '))' +
+ 'UNION ALL ' +
+ 'SELECT "video"."id" FROM "video" LEFT JOIN "videoTag" ON "videoTag"."videoId" = "video"."id" ' +
+ 'INNER JOIN "tag" ON "tag"."id" = "videoTag"."tagId" ' +
+ 'WHERE "tag"."name" = ' + escapedSearch +
+ ')'
+ )
+ }
+ }
+ )
+
+ attributesInclude.push(createSimilarityAttribute('VideoModel.name', options.search))