]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blobdiff - server/models/video/video.ts
Add videos count in channels list
[github/Chocobozzz/PeerTube.git] / server / models / video / video.ts
index 5e4b7d44c0b86f0162bbd02912a801258216276c..1f590c02dcc5b15442c70afb0fce31899435d77a 100644 (file)
@@ -23,7 +23,7 @@ import {
   Table,
   UpdatedAt
 } from 'sequelize-typescript'
-import { UserRight, VideoPrivacy, VideoState } from '../../../shared'
+import { UserRight, VideoPrivacy, VideoState, ResultList } from '../../../shared'
 import { VideoTorrentObject } from '../../../shared/models/activitypub/objects'
 import { Video, VideoDetails } from '../../../shared/models/videos'
 import { VideoFilter } from '../../../shared/models/videos/video-query.type'
@@ -43,7 +43,6 @@ import {
 } from '../../helpers/custom-validators/videos'
 import { getVideoFileResolution } from '../../helpers/ffmpeg-utils'
 import { logger } from '../../helpers/logger'
-import { getServerActor } from '../../helpers/utils'
 import {
   ACTIVITY_PUB,
   API_VERSION,
@@ -77,7 +76,7 @@ import { ScheduleVideoUpdateModel } from './schedule-video-update'
 import { VideoCaptionModel } from './video-caption'
 import { VideoBlacklistModel } from './video-blacklist'
 import { remove } from 'fs-extra'
-import { VideoViewModel } from './video-views'
+import { VideoViewModel } from './video-view'
 import { VideoRedundancyModel } from '../redundancy/video-redundancy'
 import {
   videoFilesModelToFormattedJSON,
@@ -125,6 +124,9 @@ import { VideoFile } from '@shared/models/videos/video-file.model'
 import { getHLSDirectory, getTorrentFileName, getTorrentFilePath, getVideoFilename, getVideoFilePath } from '@server/lib/video-paths'
 import { ModelCache } from '@server/models/model-cache'
 import { buildListQuery, BuildVideosQueryOptions, wrapForAPIResults } from './video-query-builder'
+import { buildNSFWFilter } from '@server/helpers/express-utils'
+import { getServerActor } from '@server/models/application/application'
+import { getPrivaciesForFederation, isPrivacyForFederation } from "@server/helpers/video"
 
 export enum ScopeNames {
   AVAILABLE_FOR_LIST_IDS = 'AVAILABLE_FOR_LIST_IDS',
@@ -498,7 +500,7 @@ export class VideoModel extends Model<VideoModel> {
   @AllowNull(false)
   @Is('VideoPrivacy', value => throwIfNotValid(value, isVideoPrivacyValid, 'privacy'))
   @Column
-  privacy: number
+  privacy: VideoPrivacy
 
   @AllowNull(false)
   @Is('VideoNSFW', value => throwIfNotValid(value, isBooleanValid, 'NSFW boolean'))
@@ -627,9 +629,9 @@ export class VideoModel extends Model<VideoModel> {
   @HasMany(() => VideoAbuseModel, {
     foreignKey: {
       name: 'videoId',
-      allowNull: false
+      allowNull: true
     },
-    onDelete: 'cascade'
+    onDelete: 'set null'
   })
   VideoAbuses: VideoAbuseModel[]
 
@@ -797,6 +799,33 @@ export class VideoModel extends Model<VideoModel> {
     ModelCache.Instance.invalidateCache('video', instance.id)
   }
 
+  @BeforeDestroy
+  static async saveEssentialDataToAbuses (instance: VideoModel, options) {
+    const tasks: Promise<any>[] = []
+
+    logger.info('Saving video abuses details of video %s.', instance.url)
+
+    if (!Array.isArray(instance.VideoAbuses)) {
+      instance.VideoAbuses = await instance.$get('VideoAbuses')
+
+      if (instance.VideoAbuses.length === 0) return undefined
+    }
+
+    const details = instance.toFormattedDetailsJSON()
+
+    for (const abuse of instance.VideoAbuses) {
+      abuse.deletedVideo = details
+      tasks.push(abuse.save({ transaction: options.transaction }))
+    }
+
+    Promise.all(tasks)
+           .catch(err => {
+             logger.error('Some errors when saving details of video %s in its abuses before destroy hook.', instance.uuid, { err })
+           })
+
+    return undefined
+  }
+
   static listLocal (): Bluebird<MVideoWithAllFiles[]> {
     const query = {
       where: {
@@ -831,15 +860,12 @@ export class VideoModel extends Model<VideoModel> {
       distinct: true,
       offset: start,
       limit: count,
-      order: getVideoSort('createdAt', [ 'Tags', 'name', 'ASC' ] as any), // FIXME: sequelize typings
+      order: getVideoSort('-createdAt', [ 'Tags', 'name', 'ASC' ] as any), // FIXME: sequelize typings
       where: {
         id: {
           [Op.in]: Sequelize.literal('(' + rawQuery + ')')
         },
-        [Op.or]: [
-          { privacy: VideoPrivacy.PUBLIC },
-          { privacy: VideoPrivacy.UNLISTED }
-        ]
+        [Op.or]: getPrivaciesForFederation()
       },
       include: [
         {
@@ -1301,16 +1327,25 @@ export class VideoModel extends Model<VideoModel> {
         remote: false
       }
     })
-    const totalVideos = await VideoModel.count()
 
     let totalLocalVideoViews = await VideoModel.sum('views', {
       where: {
         remote: false
       }
     })
+
     // Sequelize could return null...
     if (!totalLocalVideoViews) totalLocalVideoViews = 0
 
+    const { total: totalVideos } = await VideoModel.listForApi({
+      start: 0,
+      count: 0,
+      sort: '-publishedAt',
+      nsfw: buildNSFWFilter(),
+      includeLocalVideos: true,
+      withFiles: false
+    })
+
     return {
       totalLocalVideos,
       totalLocalVideoViews,
@@ -1331,7 +1366,7 @@ export class VideoModel extends Model<VideoModel> {
     // Instances only share videos
     const query = 'SELECT 1 FROM "videoShare" ' +
       'INNER JOIN "actorFollow" ON "actorFollow"."targetActorId" = "videoShare"."actorId" ' +
-      'WHERE "actorFollow"."actorId" = $followerActorId AND "videoShare"."videoId" = $videoId ' +
+      'WHERE "actorFollow"."actorId" = $followerActorId AND "actorFollow"."state" = \'accepted\' AND "videoShare"."videoId" = $videoId ' +
       'LIMIT 1'
 
     const options = {
@@ -1407,7 +1442,7 @@ export class VideoModel extends Model<VideoModel> {
   private static async getAvailableForApi (
     options: BuildVideosQueryOptions,
     countVideos = true
-  ) {
+  ): Promise<ResultList<VideoModel>> {
     function getCount () {
       if (countVideos !== true) return Promise.resolve(undefined)
 
@@ -1419,6 +1454,8 @@ export class VideoModel extends Model<VideoModel> {
     }
 
     function getModels () {
+      if (options.count === 0) return Promise.resolve([])
+
       const { query, replacements, order } = buildListQuery(VideoModel, options)
       const queryModels = wrapForAPIResults(query, replacements, options, order)
 
@@ -1543,12 +1580,6 @@ export class VideoModel extends Model<VideoModel> {
     return videos
   }
 
-  private static isPrivacyForFederation (privacy: VideoPrivacy) {
-    const castedPrivacy = parseInt(privacy + '', 10)
-
-    return castedPrivacy === VideoPrivacy.PUBLIC || castedPrivacy === VideoPrivacy.UNLISTED
-  }
-
   static getCategoryLabel (id: number) {
     return VIDEO_CATEGORIES[id] || 'Misc'
   }
@@ -1774,11 +1805,11 @@ export class VideoModel extends Model<VideoModel> {
   }
 
   hasPrivacyForFederation () {
-    return VideoModel.isPrivacyForFederation(this.privacy)
+    return isPrivacyForFederation(this.privacy)
   }
 
   isNewVideo (newPrivacy: VideoPrivacy) {
-    return this.hasPrivacyForFederation() === false && VideoModel.isPrivacyForFederation(newPrivacy) === true
+    return this.hasPrivacyForFederation() === false && isPrivacyForFederation(newPrivacy) === true
   }
 
   setAsRefreshed () {
@@ -1849,7 +1880,8 @@ export class VideoModel extends Model<VideoModel> {
 
   getVideoFileMetadataUrl (videoFile: MVideoFile, baseUrlHttp: string) {
     const path = '/api/v1/videos/'
-    return videoFile.metadata
+
+    return this.isOwned()
       ? baseUrlHttp + path + this.uuid + '/metadata/' + videoFile.id
       : videoFile.metadataUrl
   }