- VIDEO_PRIVACIES
-} from '../../initializers'
-
-import { addMethodsToModel, getSort } from '../utils'
-
-import { TagInstance } from './tag-interface'
-import { VideoFileInstance, VideoFileModel } from './video-file-interface'
-import { VideoAttributes, VideoInstance, VideoMethods } from './video-interface'
-import { sendDeleteVideo } from '../../lib/activitypub/send-request'
-import { isVideoUrlValid } from '../../helpers/custom-validators/videos'
-
-const Buffer = safeBuffer.Buffer
-
-let Video: Sequelize.Model<VideoInstance, VideoAttributes>
-let getOriginalFile: VideoMethods.GetOriginalFile
-let getVideoFilename: VideoMethods.GetVideoFilename
-let getThumbnailName: VideoMethods.GetThumbnailName
-let getThumbnailPath: VideoMethods.GetThumbnailPath
-let getPreviewName: VideoMethods.GetPreviewName
-let getPreviewPath: VideoMethods.GetPreviewPath
-let getTorrentFileName: VideoMethods.GetTorrentFileName
-let isOwned: VideoMethods.IsOwned
-let toFormattedJSON: VideoMethods.ToFormattedJSON
-let toFormattedDetailsJSON: VideoMethods.ToFormattedDetailsJSON
-let toActivityPubObject: VideoMethods.ToActivityPubObject
-let optimizeOriginalVideofile: VideoMethods.OptimizeOriginalVideofile
-let transcodeOriginalVideofile: VideoMethods.TranscodeOriginalVideofile
-let createPreview: VideoMethods.CreatePreview
-let createThumbnail: VideoMethods.CreateThumbnail
-let getVideoFilePath: VideoMethods.GetVideoFilePath
-let createTorrentAndSetInfoHash: VideoMethods.CreateTorrentAndSetInfoHash
-let getOriginalFileHeight: VideoMethods.GetOriginalFileHeight
-let getEmbedPath: VideoMethods.GetEmbedPath
-let getDescriptionPath: VideoMethods.GetDescriptionPath
-let getTruncatedDescription: VideoMethods.GetTruncatedDescription
-let getCategoryLabel: VideoMethods.GetCategoryLabel
-let getLicenceLabel: VideoMethods.GetLicenceLabel
-let getLanguageLabel: VideoMethods.GetLanguageLabel
-
-let generateThumbnailFromData: VideoMethods.GenerateThumbnailFromData
-let list: VideoMethods.List
-let listForApi: VideoMethods.ListForApi
-let listUserVideosForApi: VideoMethods.ListUserVideosForApi
-let loadByHostAndUUID: VideoMethods.LoadByHostAndUUID
-let listOwnedAndPopulateAccountAndTags: VideoMethods.ListOwnedAndPopulateAccountAndTags
-let listOwnedByAccount: VideoMethods.ListOwnedByAccount
-let load: VideoMethods.Load
-let loadByUUID: VideoMethods.LoadByUUID
-let loadByUUIDOrURL: VideoMethods.LoadByUUIDOrURL
-let loadLocalVideoByUUID: VideoMethods.LoadLocalVideoByUUID
-let loadAndPopulateAccount: VideoMethods.LoadAndPopulateAccount
-let loadAndPopulateAccountAndServerAndTags: VideoMethods.LoadAndPopulateAccountAndServerAndTags
-let loadByUUIDAndPopulateAccountAndServerAndTags: VideoMethods.LoadByUUIDAndPopulateAccountAndServerAndTags
-let searchAndPopulateAccountAndServerAndTags: VideoMethods.SearchAndPopulateAccountAndServerAndTags
-let removeThumbnail: VideoMethods.RemoveThumbnail
-let removePreview: VideoMethods.RemovePreview
-let removeFile: VideoMethods.RemoveFile
-let removeTorrent: VideoMethods.RemoveTorrent
-
-export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) {
- Video = sequelize.define<VideoInstance, VideoAttributes>('Video',
- {
- uuid: {
- type: DataTypes.UUID,
- defaultValue: DataTypes.UUIDV4,
- allowNull: false,
- validate: {
- isUUID: 4
+ VIDEO_PRIVACIES,
+ VIDEO_STATES,
+ WEBSERVER
+} from '../../initializers/constants'
+import { sendDeleteVideo } from '../../lib/activitypub/send'
+import { AccountModel } from '../account/account'
+import { AccountVideoRateModel } from '../account/account-video-rate'
+import { ActorModel } from '../activitypub/actor'
+import { AvatarModel } from '../avatar/avatar'
+import { ServerModel } from '../server/server'
+import {
+ buildBlockedAccountSQL,
+ buildTrigramSearchIndex,
+ buildWhereIdOrUUID,
+ createSafeIn,
+ createSimilarityAttribute,
+ getVideoSort,
+ isOutdated,
+ throwIfNotValid
+} from '../utils'
+import { TagModel } from './tag'
+import { VideoAbuseModel } from './video-abuse'
+import { ScopeNames as VideoChannelScopeNames, VideoChannelModel } from './video-channel'
+import { VideoCommentModel } from './video-comment'
+import { VideoFileModel } from './video-file'
+import { VideoShareModel } from './video-share'
+import { VideoTagModel } from './video-tag'
+import { ScheduleVideoUpdateModel } from './schedule-video-update'
+import { VideoCaptionModel } from './video-caption'
+import { VideoBlacklistModel } from './video-blacklist'
+import { remove, writeFile } from 'fs-extra'
+import { VideoViewModel } from './video-views'
+import { VideoRedundancyModel } from '../redundancy/video-redundancy'
+import {
+ videoFilesModelToFormattedJSON,
+ VideoFormattingJSONOptions,
+ videoModelToActivityPubObject,
+ videoModelToFormattedDetailsJSON,
+ videoModelToFormattedJSON
+} from './video-format-utils'
+import { UserVideoHistoryModel } from '../account/user-video-history'
+import { UserModel } from '../account/user'
+import { VideoImportModel } from './video-import'
+import { VideoStreamingPlaylistModel } from './video-streaming-playlist'
+import { VideoPlaylistElementModel } from './video-playlist-element'
+import { CONFIG } from '../../initializers/config'
+import { ThumbnailModel } from './thumbnail'
+import { ThumbnailType } from '../../../shared/models/videos/thumbnail.type'
+import { createTorrentPromise } from '../../helpers/webtorrent'
+
+// FIXME: Define indexes here because there is an issue with TS and Sequelize.literal when called directly in the annotation
+const indexes: (ModelIndexesOptions & { where?: WhereOptions })[] = [
+ buildTrigramSearchIndex('video_name_trigram', 'name'),
+
+ { fields: [ 'createdAt' ] },
+ { fields: [ 'publishedAt' ] },
+ { fields: [ 'duration' ] },
+ { fields: [ 'views' ] },
+ { fields: [ 'channelId' ] },
+ {
+ fields: [ 'originallyPublishedAt' ],
+ where: {
+ originallyPublishedAt: {
+ [Op.ne]: null
+ }
+ }
+ },
+ {
+ fields: [ 'category' ], // We don't care videos with an unknown category
+ where: {
+ category: {
+ [Op.ne]: null
+ }
+ }
+ },
+ {
+ fields: [ 'licence' ], // We don't care videos with an unknown licence
+ where: {
+ licence: {
+ [Op.ne]: null
+ }
+ }
+ },
+ {
+ fields: [ 'language' ], // We don't care videos with an unknown language
+ where: {
+ language: {
+ [Op.ne]: null
+ }
+ }
+ },
+ {
+ fields: [ 'nsfw' ], // Most of the videos are not NSFW
+ where: {
+ nsfw: true
+ }
+ },
+ {
+ fields: [ 'remote' ], // Only index local videos
+ where: {
+ remote: false
+ }
+ },
+ {
+ fields: [ 'uuid' ],
+ unique: true
+ },
+ {
+ fields: [ 'url' ],
+ unique: true
+ }
+]
+
+export enum ScopeNames {
+ AVAILABLE_FOR_LIST_IDS = 'AVAILABLE_FOR_LIST_IDS',
+ FOR_API = 'FOR_API',
+ WITH_ACCOUNT_DETAILS = 'WITH_ACCOUNT_DETAILS',
+ WITH_TAGS = 'WITH_TAGS',
+ WITH_FILES = 'WITH_FILES',
+ WITH_SCHEDULED_UPDATE = 'WITH_SCHEDULED_UPDATE',
+ WITH_BLACKLISTED = 'WITH_BLACKLISTED',
+ WITH_USER_HISTORY = 'WITH_USER_HISTORY',
+ WITH_STREAMING_PLAYLISTS = 'WITH_STREAMING_PLAYLISTS',
+ WITH_USER_ID = 'WITH_USER_ID',
+ WITH_THUMBNAILS = 'WITH_THUMBNAILS'
+}
+
+type ForAPIOptions = {
+ ids: number[]
+
+ videoPlaylistId?: number
+
+ withFiles?: boolean
+}
+
+type AvailableForListIDsOptions = {
+ serverAccountId: number
+ followerActorId: number
+ includeLocalVideos: boolean
+
+ withoutId?: boolean
+
+ filter?: VideoFilter
+ categoryOneOf?: number[]
+ nsfw?: boolean
+ licenceOneOf?: number[]
+ languageOneOf?: string[]
+ tagsOneOf?: string[]
+ tagsAllOf?: string[]
+
+ withFiles?: boolean
+
+ accountId?: number
+ videoChannelId?: number
+
+ videoPlaylistId?: number
+
+ trendingDays?: number
+ user?: UserModel,
+ historyOfUser?: UserModel
+
+ baseWhere?: WhereOptions[]
+}
+
+@Scopes(() => ({
+ [ ScopeNames.FOR_API ]: (options: ForAPIOptions) => {
+ const query: FindOptions = {
+ where: {
+ id: {
+ [ Op.in ]: options.ids // FIXME: sequelize ANY seems broken