]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blobdiff - server/models/video/video.ts
Send account activitypub update events
[github/Chocobozzz/PeerTube.git] / server / models / video / video.ts
index b6a2ce6b5f915a11b06134e841ffbafb7e59da3e..c4b716cd21e68013adbc4f1d0b167f9012d53036 100644 (file)
@@ -5,65 +5,26 @@ import * as parseTorrent from 'parse-torrent'
 import { join } from 'path'
 import * as Sequelize from 'sequelize'
 import {
-  AfterDestroy,
-  AllowNull,
-  BelongsTo,
-  BelongsToMany,
-  Column,
-  CreatedAt,
-  DataType,
-  Default,
-  ForeignKey,
-  HasMany,
-  IFindOptions,
-  Is,
-  IsInt,
-  IsUUID,
-  Min,
-  Model,
-  Scopes,
-  Table,
-  UpdatedAt
+  AfterDestroy, AllowNull, BelongsTo, BelongsToMany, Column, CreatedAt, DataType, Default, ForeignKey, HasMany, IFindOptions, Is,
+  IsInt, IsUUID, Min, Model, Scopes, Table, UpdatedAt
 } from 'sequelize-typescript'
 import { IIncludeOptions } from 'sequelize-typescript/lib/interfaces/IIncludeOptions'
 import { VideoPrivacy, VideoResolution } from '../../../shared'
 import { VideoTorrentObject } from '../../../shared/models/activitypub/objects'
 import { Video, VideoDetails } from '../../../shared/models/videos'
+import { activityPubCollection } from '../../helpers/activitypub'
+import { createTorrentPromise, renamePromise, statPromise, unlinkPromise, writeFilePromise } from '../../helpers/core-utils'
+import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
+import { isBooleanValid } from '../../helpers/custom-validators/misc'
 import {
-  activityPubCollection,
-  createTorrentPromise,
-  generateImageFromVideoFile,
-  getVideoFileHeight,
-  logger,
-  renamePromise,
-  statPromise,
-  transcode,
-  unlinkPromise,
-  writeFilePromise
-} from '../../helpers'
-import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub'
-import {
-  isVideoCategoryValid,
-  isVideoDescriptionValid,
-  isVideoDurationValid,
-  isVideoLanguageValid,
-  isVideoLicenceValid,
-  isVideoNameValid,
-  isVideoNSFWValid,
+  isVideoCategoryValid, isVideoDescriptionValid, isVideoDurationValid, isVideoLanguageValid, isVideoLicenceValid, isVideoNameValid,
   isVideoPrivacyValid
 } from '../../helpers/custom-validators/videos'
+import { generateImageFromVideoFile, getVideoFileHeight, transcode } from '../../helpers/ffmpeg-utils'
+import { logger } from '../../helpers/logger'
 import {
-  API_VERSION,
-  CONFIG,
-  CONSTRAINTS_FIELDS,
-  PREVIEWS_SIZE,
-  REMOTE_SCHEME,
-  STATIC_PATHS,
-  THUMBNAILS_SIZE,
-  VIDEO_CATEGORIES,
-  VIDEO_LANGUAGES,
-  VIDEO_LICENCES,
-  VIDEO_PRIVACIES
+  API_VERSION, CONFIG, CONSTRAINTS_FIELDS, PREVIEWS_SIZE, REMOTE_SCHEME, STATIC_PATHS, THUMBNAILS_SIZE, VIDEO_CATEGORIES,
+  VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_PRIVACIES
 } from '../../initializers'
 import { getAnnounceActivityPubUrl } from '../../lib/activitypub'
 import { sendDeleteVideo } from '../../lib/activitypub/send'
@@ -75,6 +36,7 @@ import { getSort, throwIfNotValid } from '../utils'
 import { TagModel } from './tag'
 import { VideoAbuseModel } from './video-abuse'
 import { VideoChannelModel } from './video-channel'
+import { VideoCommentModel } from './video-comment'
 import { VideoFileModel } from './video-file'
 import { VideoShareModel } from './video-share'
 import { VideoTagModel } from './video-tag'
@@ -85,7 +47,8 @@ enum ScopeNames {
   WITH_TAGS = 'WITH_TAGS',
   WITH_FILES = 'WITH_FILES',
   WITH_SHARES = 'WITH_SHARES',
-  WITH_RATES = 'WITH_RATES'
+  WITH_RATES = 'WITH_RATES',
+  WITH_COMMENTS = 'WITH_COMMENTS'
 }
 
 @Scopes({
@@ -151,6 +114,13 @@ enum ScopeNames {
         include: [ () => AccountModel ]
       }
     ]
+  },
+  [ScopeNames.WITH_COMMENTS]: {
+    include: [
+      {
+        model: () => VideoCommentModel
+      }
+    ]
   }
 })
 @Table({
@@ -216,7 +186,7 @@ export class VideoModel extends Model<VideoModel> {
   privacy: number
 
   @AllowNull(false)
-  @Is('VideoNSFW', value => throwIfNotValid(value, isVideoNSFWValid, 'NSFW boolean'))
+  @Is('VideoNSFW', value => throwIfNotValid(value, isBooleanValid, 'NSFW boolean'))
   @Column
   nsfw: boolean
 
@@ -261,6 +231,10 @@ export class VideoModel extends Model<VideoModel> {
   @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEOS.URL.max))
   url: string
 
+  @AllowNull(false)
+  @Column
+  commentsEnabled: boolean
+
   @CreatedAt
   createdAt: Date
 
@@ -322,6 +296,15 @@ export class VideoModel extends Model<VideoModel> {
   })
   AccountVideoRates: AccountVideoRateModel[]
 
+  @HasMany(() => VideoCommentModel, {
+    foreignKey: {
+      name: 'videoId',
+      allowNull: false
+    },
+    onDelete: 'cascade'
+  })
+  VideoComments: VideoCommentModel[]
+
   @AfterDestroy
   static removeFilesAndSendDelete (instance: VideoModel) {
     const tasks = []
@@ -417,7 +400,8 @@ export class VideoModel extends Model<VideoModel> {
           include: [ AccountModel ]
         },
         VideoFileModel,
-        TagModel
+        TagModel,
+        VideoCommentModel
       ]
     }
 
@@ -536,7 +520,7 @@ export class VideoModel extends Model<VideoModel> {
     }
 
     return VideoModel
-      .scope([ ScopeNames.WITH_RATES, ScopeNames.WITH_SHARES, ScopeNames.WITH_TAGS, ScopeNames.WITH_FILES, ScopeNames.WITH_ACCOUNT ])
+      .scope([ ScopeNames.WITH_TAGS, ScopeNames.WITH_FILES, ScopeNames.WITH_ACCOUNT ])
       .findById(id, options)
   }
 
@@ -561,7 +545,27 @@ export class VideoModel extends Model<VideoModel> {
     }
 
     return VideoModel
-      .scope([ ScopeNames.WITH_RATES, ScopeNames.WITH_SHARES, ScopeNames.WITH_TAGS, ScopeNames.WITH_FILES, ScopeNames.WITH_ACCOUNT ])
+      .scope([ ScopeNames.WITH_TAGS, ScopeNames.WITH_FILES, ScopeNames.WITH_ACCOUNT ])
+      .findOne(options)
+  }
+
+  static loadAndPopulateAll (id: number) {
+    const options = {
+      order: [ [ 'Tags', 'name', 'ASC' ] ],
+      where: {
+        id
+      }
+    }
+
+    return VideoModel
+      .scope([
+        ScopeNames.WITH_RATES,
+        ScopeNames.WITH_SHARES,
+        ScopeNames.WITH_TAGS,
+        ScopeNames.WITH_FILES,
+        ScopeNames.WITH_ACCOUNT,
+        ScopeNames.WITH_COMMENTS
+      ])
       .findOne(options)
   }
 
@@ -774,6 +778,7 @@ export class VideoModel extends Model<VideoModel> {
       channel: this.VideoChannel.toFormattedJSON(),
       account: this.VideoChannel.Account.toFormattedJSON(),
       tags: map<TagModel, string>(this.Tags, 'name'),
+      commentsEnabled: this.commentsEnabled,
       files: []
     }
 
@@ -865,6 +870,17 @@ export class VideoModel extends Model<VideoModel> {
       sharesObject = activityPubCollection(shares)
     }
 
+    let commentsObject
+    if (Array.isArray(this.VideoComments)) {
+      const comments: string[] = []
+
+      for (const videoComment of this.VideoComments) {
+        comments.push(videoComment.url)
+      }
+
+      commentsObject = activityPubCollection(comments)
+    }
+
     const url = []
     for (const file of this.VideoFiles) {
       url.push({
@@ -910,6 +926,7 @@ export class VideoModel extends Model<VideoModel> {
       language,
       views: this.views,
       nsfw: this.nsfw,
+      commentsEnabled: this.commentsEnabled,
       published: this.createdAt.toISOString(),
       updated: this.updatedAt.toISOString(),
       mediaType: 'text/markdown',
@@ -925,6 +942,7 @@ export class VideoModel extends Model<VideoModel> {
       likes: likesObject,
       dislikes: dislikesObject,
       shares: sharesObject,
+      comments: commentsObject,
       attributedTo: [
         {
           type: 'Group',