]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blobdiff - server/models/video/video.ts
Lazy load avatars
[github/Chocobozzz/PeerTube.git] / server / models / video / video.ts
index 443aec9c29655508da22c99252e300b3bee5f3d4..1321337ff79d1c939884ecd2910a27a6ef3f322a 100644 (file)
@@ -36,7 +36,7 @@ import {
   Table,
   UpdatedAt
 } from 'sequelize-typescript'
-import { UserRight, VideoPrivacy, VideoState } from '../../../shared'
+import { UserRight, VideoPrivacy, VideoResolution, VideoState } from '../../../shared'
 import { VideoTorrentObject } from '../../../shared/models/activitypub/objects'
 import { Video, VideoDetails, VideoFile } from '../../../shared/models/videos'
 import { VideoFilter } from '../../../shared/models/videos/video-query.type'
@@ -63,6 +63,7 @@ import {
   CONSTRAINTS_FIELDS,
   HLS_REDUNDANCY_DIRECTORY,
   HLS_STREAMING_PLAYLIST_DIRECTORY,
+  LAZY_STATIC_PATHS,
   REMOTE_SCHEME,
   STATIC_DOWNLOAD_PATHS,
   STATIC_PATHS,
@@ -91,7 +92,7 @@ import {
 } from '../utils'
 import { TagModel } from './tag'
 import { VideoAbuseModel } from './video-abuse'
-import { ScopeNames as VideoChannelScopeNames, VideoChannelModel } from './video-channel'
+import { ScopeNames as VideoChannelScopeNames, SummaryOptions, VideoChannelModel } from './video-channel'
 import { VideoCommentModel } from './video-comment'
 import { VideoFileModel } from './video-file'
 import { VideoShareModel } from './video-share'
@@ -190,26 +191,29 @@ export enum ScopeNames {
   WITH_FILES = 'WITH_FILES',
   WITH_SCHEDULED_UPDATE = 'WITH_SCHEDULED_UPDATE',
   WITH_BLACKLISTED = 'WITH_BLACKLISTED',
+  WITH_BLOCKLIST = 'WITH_BLOCKLIST',
   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[]
+export type ForAPIOptions = {
+  ids?: number[]
 
   videoPlaylistId?: number
 
   withFiles?: boolean
+
+  withAccountBlockerIds?: number[]
 }
 
-type AvailableForListIDsOptions = {
+export type AvailableForListIDsOptions = {
   serverAccountId: number
   followerActorId: number
   includeLocalVideos: boolean
 
-  withoutId?: boolean
+  attributesType?: 'none' | 'id' | 'all'
 
   filter?: VideoFilter
   categoryOneOf?: number[]
@@ -236,14 +240,16 @@ type AvailableForListIDsOptions = {
 @Scopes(() => ({
   [ ScopeNames.FOR_API ]: (options: ForAPIOptions) => {
     const query: FindOptions = {
-      where: {
-        id: {
-          [ Op.in ]: options.ids // FIXME: sequelize ANY seems broken
-        }
-      },
       include: [
         {
-          model: VideoChannelModel.scope({ method: [ VideoChannelScopeNames.SUMMARY, true ] }),
+          model: VideoChannelModel.scope({
+            method: [
+              VideoChannelScopeNames.SUMMARY, {
+                withAccount: true,
+                withAccountBlockerIds: options.withAccountBlockerIds
+              } as SummaryOptions
+            ]
+          }),
           required: true
         },
         {
@@ -254,6 +260,14 @@ type AvailableForListIDsOptions = {
       ]
     }
 
+    if (options.ids) {
+      query.where = {
+        id: {
+          [ Op.in ]: options.ids // FIXME: sequelize ANY seems broken
+        }
+      }
+    }
+
     if (options.withFiles === true) {
       query.include.push({
         model: VideoFileModel.unscoped(),
@@ -278,10 +292,14 @@ type AvailableForListIDsOptions = {
 
     const query: FindOptions = {
       raw: true,
-      attributes: options.withoutId === true ? [] : [ 'id' ],
       include: []
     }
 
+    const attributesType = options.attributesType || 'id'
+
+    if (attributesType === 'id') query.attributes = [ 'id' ]
+    else if (attributesType === 'none') query.attributes = [ ]
+
     whereAnd.push({
       id: {
         [ Op.notIn ]: Sequelize.literal(
@@ -290,17 +308,19 @@ type AvailableForListIDsOptions = {
       }
     })
 
-    whereAnd.push({
-      channelId: {
-        [ Op.notIn ]: Sequelize.literal(
-          '(' +
-            'SELECT id FROM "videoChannel" WHERE "accountId" IN (' +
-              buildBlockedAccountSQL(options.serverAccountId, options.user ? options.user.Account.id : undefined) +
-            ')' +
-          ')'
-        )
-      }
-    })
+    if (options.serverAccountId) {
+      whereAnd.push({
+        channelId: {
+          [ Op.notIn ]: Sequelize.literal(
+            '(' +
+              'SELECT id FROM "videoChannel" WHERE "accountId" IN (' +
+                buildBlockedAccountSQL(options.serverAccountId, options.user ? options.user.Account.id : undefined) +
+              ')' +
+            ')'
+          )
+        }
+      })
+    }
 
     // Only list public/published videos
     if (!options.filter || options.filter !== 'all-local') {
@@ -527,6 +547,9 @@ type AvailableForListIDsOptions = {
     }
 
     return query
+  },
+  [ScopeNames.WITH_BLOCKLIST]: {
+
   },
   [ ScopeNames.WITH_THUMBNAILS ]: {
     include: [
@@ -845,9 +868,9 @@ export class VideoModel extends Model<VideoModel> {
   @HasMany(() => VideoPlaylistElementModel, {
     foreignKey: {
       name: 'videoId',
-      allowNull: false
+      allowNull: true
     },
-    onDelete: 'cascade'
+    onDelete: 'set null'
   })
   VideoPlaylistElements: VideoPlaylistElementModel[]
 
@@ -1586,7 +1609,7 @@ export class VideoModel extends Model<VideoModel> {
       serverAccountId: serverActor.Account.id,
       followerActorId,
       includeLocalVideos: true,
-      withoutId: true // Don't break aggregation
+      attributesType: 'none' // Don't break aggregation
     }
 
     const query: FindOptions = {
@@ -1715,6 +1738,15 @@ export class VideoModel extends Model<VideoModel> {
     return VIDEO_STATES[ id ] || 'Unknown'
   }
 
+  isBlacklisted () {
+    return !!this.VideoBlacklist
+  }
+
+  isBlocked () {
+    return (this.VideoChannel.Account.Actor.Server && this.VideoChannel.Account.Actor.Server.isBlocked()) ||
+      this.VideoChannel.Account.isBlocked()
+  }
+
   getOriginalFile () {
     if (Array.isArray(this.VideoFiles) === false) return undefined
 
@@ -1722,6 +1754,12 @@ export class VideoModel extends Model<VideoModel> {
     return maxBy(this.VideoFiles, file => file.resolution)
   }
 
+  getFile (resolution: VideoResolution) {
+    if (Array.isArray(this.VideoFiles) === false) return undefined
+
+    return this.VideoFiles.find(f => f.resolution === resolution)
+  }
+
   async addAndSaveThumbnail (thumbnail: ThumbnailModel, transaction: Transaction) {
     thumbnail.videoId = this.id
 
@@ -1819,7 +1857,7 @@ export class VideoModel extends Model<VideoModel> {
     if (!preview) return null
 
     // We use a local cache, so specify our cache endpoint instead of potential remote URL
-    return join(STATIC_PATHS.PREVIEWS, preview.filename)
+    return join(LAZY_STATIC_PATHS.PREVIEWS, preview.filename)
   }
 
   toFormattedJSON (options?: VideoFormattingJSONOptions): Video {