-import { VideoFilter, VideoPrivacy, VideoState } from '@shared/models'
-import { buildDirectionAndField, createSafeIn } from '@server/models/utils'
-import { Model } from 'sequelize-typescript'
-import { MUserAccountId, MUserId } from '@server/types/models'
+import { Sequelize } from 'sequelize/types'
import validator from 'validator'
import { exists } from '@server/helpers/custom-validators/misc'
+import { buildDirectionAndField, createSafeIn } from '@server/models/utils'
+import { MUserAccountId, MUserId } from '@server/types/models'
+import { VideoFilter, VideoPrivacy, VideoState } from '@shared/models'
export type BuildVideosQueryOptions = {
attributes?: string[]
start: number
sort: string
+ nsfw?: boolean
filter?: VideoFilter
+ isLive?: boolean
+
categoryOneOf?: number[]
- nsfw?: boolean
licenceOneOf?: number[]
languageOneOf?: string[]
tagsOneOf?: string[]
having?: string
}
-function buildListQuery (model: typeof Model, options: BuildVideosQueryOptions) {
+function buildListQuery (sequelize: Sequelize, options: BuildVideosQueryOptions) {
const and: string[] = []
const joins: string[] = []
const replacements: any = {}
const blockerIds = [ options.serverAccountId ]
if (options.user) blockerIds.push(options.user.Account.id)
- const inClause = createSafeIn(model, blockerIds)
+ const inClause = createSafeIn(sequelize, blockerIds)
and.push(
'NOT EXISTS (' +
'EXISTS (' +
' SELECT 1 FROM "videoTag" ' +
' INNER JOIN "tag" ON "tag"."id" = "videoTag"."tagId" ' +
- ' WHERE lower("tag"."name") IN (' + createSafeIn(model, tagsOneOfLower) + ') ' +
+ ' WHERE lower("tag"."name") IN (' + createSafeIn(sequelize, tagsOneOfLower) + ') ' +
' AND "video"."id" = "videoTag"."videoId"' +
')'
)
'EXISTS (' +
' SELECT 1 FROM "videoTag" ' +
' INNER JOIN "tag" ON "tag"."id" = "videoTag"."tagId" ' +
- ' WHERE lower("tag"."name") IN (' + createSafeIn(model, tagsAllOfLower) + ') ' +
+ ' WHERE lower("tag"."name") IN (' + createSafeIn(sequelize, tagsAllOfLower) + ') ' +
' AND "video"."id" = "videoTag"."videoId" ' +
' GROUP BY "videoTag"."videoId" HAVING COUNT(*) = ' + tagsAllOfLower.length +
')'
if (options.nsfw === true) {
and.push('"video"."nsfw" IS TRUE')
+ } else if (options.nsfw === false) {
+ and.push('"video"."nsfw" IS FALSE')
}
- if (options.nsfw === false) {
- and.push('"video"."nsfw" IS FALSE')
+ if (options.isLive === true) {
+ and.push('"video"."isLive" IS TRUE')
+ } else if (options.isLive === false) {
+ and.push('"video"."isLive" IS FALSE')
}
if (options.categoryOneOf) {
languagesQueryParts.push(
'EXISTS (' +
' SELECT 1 FROM "videoCaption" WHERE "videoCaption"."language" ' +
- ' IN (' + createSafeIn(model, languages) + ') AND ' +
+ ' IN (' + createSafeIn(sequelize, languages) + ') AND ' +
' "videoCaption"."videoId" = "video"."id"' +
')'
)
* - weights and base score are in number of half-days.
* - all comments are counted, regardless of being written by the video author or not
* see https://github.com/reddit-archive/reddit/blob/master/r2/r2/lib/db/_sorts.pyx#L47-L58
+ * - we have less interactions than on reddit, so multiply weights by an arbitrary factor
*/
const weights = {
- like: 3,
- dislike: -3,
- view: 1 / 12,
- comment: 2, // a comment takes more time than a like to do, but can be done multiple times
- history: -2
+ like: 3 * 50,
+ dislike: -3 * 50,
+ view: Math.floor((1 / 3) * 50),
+ comment: 2 * 50, // a comment takes more time than a like to do, but can be done multiple times
+ history: -2 * 50
}
joins.push('LEFT JOIN "videoComment" ON "video"."id" = "videoComment"."videoId"')
`+ LOG(GREATEST(1, "video"."dislikes" - 1)) * ${weights.dislike} ` + // dislikes (-)
`+ LOG("video"."views" + 1) * ${weights.view} ` + // views (+)
`+ LOG(GREATEST(1, COUNT(DISTINCT "videoComment"."id"))) * ${weights.comment} ` + // comments (+)
- '+ (SELECT EXTRACT(epoch FROM "video"."publishedAt") / 47000) ' // base score (in number of half-days)
+ '+ (SELECT (EXTRACT(epoch FROM "video"."publishedAt") - 1446156582) / 47000) ' // base score (in number of half-days)
if (options.trendingAlgorithm === 'best' && options.user) {
joins.push(
}
if (options.search) {
- const escapedSearch = model.sequelize.escape(options.search)
- const escapedLikeSearch = model.sequelize.escape('%' + options.search + '%')
+ const escapedSearch = sequelize.escape(options.search)
+ const escapedLikeSearch = sequelize.escape('%' + options.search + '%')
cte.push(
'"trigramSearch" AS (' +
'INNER JOIN "actor" AS "VideoChannel->Account->Actor" ON "VideoChannel->Account"."actorId" = "VideoChannel->Account->Actor"."id"',
'LEFT OUTER JOIN "server" AS "VideoChannel->Actor->Server" ON "VideoChannel->Actor"."serverId" = "VideoChannel->Actor->Server"."id"',
- 'LEFT OUTER JOIN "avatar" AS "VideoChannel->Actor->Avatar" ON "VideoChannel->Actor"."avatarId" = "VideoChannel->Actor->Avatar"."id"',
+ 'LEFT OUTER JOIN "actorImage" AS "VideoChannel->Actor->Avatar" ' +
+ 'ON "VideoChannel->Actor"."avatarId" = "VideoChannel->Actor->Avatar"."id"',
'LEFT OUTER JOIN "server" AS "VideoChannel->Account->Actor->Server" ' +
'ON "VideoChannel->Account->Actor"."serverId" = "VideoChannel->Account->Actor->Server"."id"',
- 'LEFT OUTER JOIN "avatar" AS "VideoChannel->Account->Actor->Avatar" ' +
+ 'LEFT OUTER JOIN "actorImage" AS "VideoChannel->Account->Actor->Avatar" ' +
'ON "VideoChannel->Account->Actor"."avatarId" = "VideoChannel->Account->Actor->Avatar"."id"',
'LEFT OUTER JOIN "thumbnail" AS "Thumbnails" ON "video"."id" = "Thumbnails"."videoId"'
'"VideoFiles"."resolution"': '"VideoFiles.resolution"',
'"VideoFiles"."size"': '"VideoFiles.size"',
'"VideoFiles"."extname"': '"VideoFiles.extname"',
+ '"VideoFiles"."filename"': '"VideoFiles.filename"',
+ '"VideoFiles"."fileUrl"': '"VideoFiles.fileUrl"',
+ '"VideoFiles"."torrentFilename"': '"VideoFiles.torrentFilename"',
+ '"VideoFiles"."torrentUrl"': '"VideoFiles.torrentUrl"',
'"VideoFiles"."infoHash"': '"VideoFiles.infoHash"',
'"VideoFiles"."fps"': '"VideoFiles.fps"',
'"VideoFiles"."videoId"': '"VideoFiles.videoId"',
'"VideoStreamingPlaylists->VideoFiles"."resolution"': '"VideoStreamingPlaylists.VideoFiles.resolution"',
'"VideoStreamingPlaylists->VideoFiles"."size"': '"VideoStreamingPlaylists.VideoFiles.size"',
'"VideoStreamingPlaylists->VideoFiles"."extname"': '"VideoStreamingPlaylists.VideoFiles.extname"',
+ '"VideoStreamingPlaylists->VideoFiles"."filename"': '"VideoStreamingPlaylists.VideoFiles.filename"',
+ '"VideoStreamingPlaylists->VideoFiles"."fileUrl"': '"VideoStreamingPlaylists.VideoFiles.fileUrl"',
+ '"VideoStreamingPlaylists->VideoFiles"."torrentFilename"': '"VideoStreamingPlaylists.VideoFiles.torrentFilename"',
+ '"VideoStreamingPlaylists->VideoFiles"."torrentUrl"': '"VideoStreamingPlaylists.VideoFiles.torrentUrl"',
'"VideoStreamingPlaylists->VideoFiles"."infoHash"': '"VideoStreamingPlaylists.VideoFiles.infoHash"',
'"VideoStreamingPlaylists->VideoFiles"."fps"': '"VideoStreamingPlaylists.VideoFiles.fps"',
'"VideoStreamingPlaylists->VideoFiles"."videoStreamingPlaylistId"': '"VideoStreamingPlaylists.VideoFiles.videoStreamingPlaylistId"',