]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blobdiff - server/models/video/video.ts
Fetch outbox to grab old activities
[github/Chocobozzz/PeerTube.git] / server / models / video / video.ts
index dd73dd7caa7a813607232069a9c2a4a3d621a1d8..3b7e83779a0437fdc3785a00ebac9fecab2b1998 100644 (file)
@@ -9,7 +9,6 @@ import { VideoTorrentObject } from '../../../shared/models/activitypub/objects/v
 import {
   createTorrentPromise,
   generateImageFromVideoFile,
-  getActivityPubUrl,
   getVideoFileHeight,
   isVideoCategoryValid,
   isVideoDescriptionValid,
@@ -26,6 +25,7 @@ import {
   unlinkPromise,
   writeFilePromise
 } from '../../helpers'
+import { isVideoUrlValid } from '../../helpers/custom-validators/videos'
 import {
   API_VERSION,
   CONFIG,
@@ -45,8 +45,7 @@ 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'
+import { sendDeleteVideo } from '../../lib/index'
 
 const Buffer = safeBuffer.Buffer
 
@@ -79,18 +78,20 @@ let getLanguageLabel: VideoMethods.GetLanguageLabel
 let generateThumbnailFromData: VideoMethods.GenerateThumbnailFromData
 let list: VideoMethods.List
 let listForApi: VideoMethods.ListForApi
+let listAllAndSharedByAccountForOutbox: VideoMethods.ListAllAndSharedByAccountForOutbox
 let listUserVideosForApi: VideoMethods.ListUserVideosForApi
 let loadByHostAndUUID: VideoMethods.LoadByHostAndUUID
 let listOwnedAndPopulateAccountAndTags: VideoMethods.ListOwnedAndPopulateAccountAndTags
 let listOwnedByAccount: VideoMethods.ListOwnedByAccount
 let load: VideoMethods.Load
+let loadByUrlAndPopulateAccount: VideoMethods.LoadByUrlAndPopulateAccount
 let loadByUUID: VideoMethods.LoadByUUID
 let loadByUUIDOrURL: VideoMethods.LoadByUUIDOrURL
 let loadLocalVideoByUUID: VideoMethods.LoadLocalVideoByUUID
 let loadAndPopulateAccount: VideoMethods.LoadAndPopulateAccount
-let loadAndPopulateAccountAndPodAndTags: VideoMethods.LoadAndPopulateAccountAndPodAndTags
-let loadByUUIDAndPopulateAccountAndPodAndTags: VideoMethods.LoadByUUIDAndPopulateAccountAndPodAndTags
-let searchAndPopulateAccountAndPodAndTags: VideoMethods.SearchAndPopulateAccountAndPodAndTags
+let loadAndPopulateAccountAndServerAndTags: VideoMethods.LoadAndPopulateAccountAndServerAndTags
+let loadByUUIDAndPopulateAccountAndServerAndTags: VideoMethods.LoadByUUIDAndPopulateAccountAndServerAndTags
+let searchAndPopulateAccountAndServerAndTags: VideoMethods.SearchAndPopulateAccountAndServerAndTags
 let removeThumbnail: VideoMethods.RemoveThumbnail
 let removePreview: VideoMethods.RemovePreview
 let removeFile: VideoMethods.RemoveFile
@@ -253,9 +254,6 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da
         },
         {
           fields: [ 'channelId' ]
-        },
-        {
-          fields: [ 'parentId' ]
         }
       ],
       hooks: {
@@ -269,19 +267,21 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da
 
     generateThumbnailFromData,
     list,
+    listAllAndSharedByAccountForOutbox,
     listForApi,
     listUserVideosForApi,
     listOwnedAndPopulateAccountAndTags,
     listOwnedByAccount,
     load,
+    loadByUrlAndPopulateAccount,
     loadAndPopulateAccount,
-    loadAndPopulateAccountAndPodAndTags,
+    loadAndPopulateAccountAndServerAndTags,
     loadByHostAndUUID,
     loadByUUIDOrURL,
     loadByUUID,
     loadLocalVideoByUUID,
-    loadByUUIDAndPopulateAccountAndPodAndTags,
-    searchAndPopulateAccountAndPodAndTags
+    loadByUUIDAndPopulateAccountAndServerAndTags,
+    searchAndPopulateAccountAndServerAndTags
   ]
   const instanceMethods = [
     createPreview,
@@ -329,14 +329,6 @@ function associate (models) {
     onDelete: 'cascade'
   })
 
-  Video.belongsTo(models.VideoChannel, {
-    foreignKey: {
-      name: 'parentId',
-      allowNull: true
-    },
-    onDelete: 'cascade'
-  })
-
   Video.belongsToMany(models.Tag, {
     foreignKey: 'videoId',
     through: models.VideoTag,
@@ -358,6 +350,14 @@ function associate (models) {
     },
     onDelete: 'cascade'
   })
+
+  Video.hasMany(models.VideoShare, {
+    foreignKey: {
+      name: 'videoId',
+      allowNull: false
+    },
+    onDelete: 'cascade'
+  })
 }
 
 function afterDestroy (video: VideoInstance) {
@@ -477,13 +477,13 @@ getPreviewPath = function (this: VideoInstance) {
 }
 
 toFormattedJSON = function (this: VideoInstance) {
-  let podHost
+  let serverHost
 
-  if (this.VideoChannel.Account.Pod) {
-    podHost = this.VideoChannel.Account.Pod.host
+  if (this.VideoChannel.Account.Server) {
+    serverHost = this.VideoChannel.Account.Server.host
   } else {
     // It means it's our video
-    podHost = CONFIG.WEBSERVER.HOST
+    serverHost = CONFIG.WEBSERVER.HOST
   }
 
   const json = {
@@ -498,7 +498,7 @@ toFormattedJSON = function (this: VideoInstance) {
     languageLabel: this.getLanguageLabel(),
     nsfw: this.nsfw,
     description: this.getTruncatedDescription(),
-    podHost,
+    serverHost,
     isLocal: this.isOwned(),
     account: this.VideoChannel.Account.name,
     duration: this.duration,
@@ -519,7 +519,7 @@ toFormattedJSON = function (this: VideoInstance) {
 toFormattedDetailsJSON = function (this: VideoInstance) {
   const formattedJson = this.toFormattedJSON()
 
-  // Maybe our pod is not up to date and there are new privacy settings since our version
+  // Maybe our server is not up to date and there are new privacy settings since our version
   let privacyLabel = VIDEO_PRIVACIES[this.privacy]
   if (!privacyLabel) privacyLabel = 'Unknown'
 
@@ -559,6 +559,7 @@ toFormattedDetailsJSON = function (this: VideoInstance) {
 
 toActivityPubObject = function (this: VideoInstance) {
   const { baseUrlHttp, baseUrlWs } = getBaseUrls(this)
+  if (!this.Tags) this.Tags = []
 
   const tag = this.Tags.map(t => ({
     type: 'Hashtag' as 'Hashtag',
@@ -569,7 +570,7 @@ toActivityPubObject = function (this: VideoInstance) {
   for (const file of this.VideoFiles) {
     url.push({
       type: 'Link',
-      mimeType: 'video/' + file.extname,
+      mimeType: 'video/' + file.extname.replace('.', ''),
       url: getVideoFileUrl(this, file, baseUrlHttp),
       width: file.resolution,
       size: file.size
@@ -592,7 +593,7 @@ toActivityPubObject = function (this: VideoInstance) {
 
   const videoObject: VideoTorrentObject = {
     type: 'Video' as 'Video',
-    id: getActivityPubUrl('video', this.uuid),
+    id: this.url,
     name: this.name,
     // https://www.w3.org/TR/activitystreams-vocabulary/#dfn-duration
     duration: 'PT' + this.duration + 'S',
@@ -612,8 +613,8 @@ toActivityPubObject = function (this: VideoInstance) {
     },
     views: this.views,
     nsfw: this.nsfw,
-    published: this.createdAt,
-    updated: this.updatedAt,
+    published: this.createdAt.toISOString(),
+    updated: this.updatedAt.toISOString(),
     mediaType: 'text/markdown',
     content: this.getTruncatedDescription(),
     icon: {
@@ -623,7 +624,7 @@ toActivityPubObject = function (this: VideoInstance) {
       width: THUMBNAILS_SIZE.width,
       height: THUMBNAILS_SIZE.height
     },
-    url
+    url // FIXME: needed?
   }
 
   return videoObject
@@ -721,7 +722,7 @@ getDescriptionPath = function (this: VideoInstance) {
 getCategoryLabel = function (this: VideoInstance) {
   let categoryLabel = VIDEO_CATEGORIES[this.category]
 
-  // Maybe our pod is not up to date and there are new categories since our version
+  // Maybe our server is not up to date and there are new categories since our version
   if (!categoryLabel) categoryLabel = 'Misc'
 
   return categoryLabel
@@ -730,7 +731,7 @@ getCategoryLabel = function (this: VideoInstance) {
 getLicenceLabel = function (this: VideoInstance) {
   let licenceLabel = VIDEO_LICENCES[this.licence]
 
-  // Maybe our pod is not up to date and there are new licences since our version
+  // Maybe our server is not up to date and there are new licences since our version
   if (!licenceLabel) licenceLabel = 'Unknown'
 
   return licenceLabel
@@ -784,6 +785,54 @@ list = function () {
   return Video.findAll(query)
 }
 
+listAllAndSharedByAccountForOutbox = function (accountId: number, start: number, count: number) {
+  const queryVideo = 'SELECT "Video"."id" FROM "Videos" AS "Video" ' +
+                'INNER JOIN "VideoChannels" AS "VideoChannel" ON "VideoChannel"."id" = "Video"."channelId" ' +
+                'WHERE "VideoChannel"."accountId" = ' + accountId
+  const queryVideoShare = 'SELECT "Video"."id" FROM "VideoShares" AS "VideoShare" ' +
+                          'INNER JOIN "Videos" AS "Video" ON "Video"."id" = "VideoShare"."videoId" ' +
+                          'INNER JOIN "VideoChannels" AS "VideoChannel" ON "VideoChannel"."id" = "Video"."channelId" ' +
+                          'WHERE "VideoShare"."accountId" = ' + accountId
+  const rawQuery = `(${queryVideo}) UNION (${queryVideoShare}) LIMIT ${count} OFFSET ${start}`
+
+  const query = {
+    distinct: true,
+    offset: start,
+    limit: count,
+    order: [ getSort('createdAt'), [ Video['sequelize'].models.Tag, 'name', 'ASC' ] ],
+    where: {
+      id: {
+        [Sequelize.Op.in]: Sequelize.literal('(' + rawQuery + ')')
+      }
+    },
+    include: [
+      {
+        model: Video['sequelize'].models.VideoShare,
+        required: false
+      },
+      {
+        model: Video['sequelize'].models.VideoChannel,
+        required: true,
+        include: [
+          {
+            model: Video['sequelize'].models.Account,
+            required: true
+          }
+        ]
+      },
+      Video['sequelize'].models.Tag,
+      Video['sequelize'].models.VideoFile
+    ]
+  }
+
+  return Video.findAndCountAll(query).then(({ rows, count }) => {
+    return {
+      data: rows,
+      total: count
+    }
+  })
+}
+
 listUserVideosForApi = function (userId: number, start: number, count: number, sort: string) {
   const query = {
     distinct: true,
@@ -825,12 +874,14 @@ listForApi = function (start: number, count: number, sort: string) {
     include: [
       {
         model: Video['sequelize'].models.VideoChannel,
+        required: true,
         include: [
           {
             model: Video['sequelize'].models.Account,
+            required: true,
             include: [
               {
-                model: Video['sequelize'].models.Pod,
+                model: Video['sequelize'].models.Server,
                 required: false
               }
             ]
@@ -866,7 +917,7 @@ loadByHostAndUUID = function (fromHost: string, uuid: string, t?: Sequelize.Tran
             model: Video['sequelize'].models.Account,
             include: [
               {
-                model: Video['sequelize'].models.Pod,
+                model: Video['sequelize'].models.Server,
                 required: true,
                 where: {
                   host: fromHost
@@ -945,6 +996,25 @@ loadByUUID = function (uuid: string, t?: Sequelize.Transaction) {
   return Video.findOne(query)
 }
 
+loadByUrlAndPopulateAccount = function (url: string, t?: Sequelize.Transaction) {
+  const query: Sequelize.FindOptions<VideoAttributes> = {
+    where: {
+      url
+    },
+    include: [
+      Video['sequelize'].models.VideoFile,
+      {
+        model: Video['sequelize'].models.VideoChannel,
+        include: [ Video['sequelize'].models.Account ]
+      }
+    ]
+  }
+
+  if (t !== undefined) query.transaction = t
+
+  return Video.findOne(query)
+}
+
 loadByUUIDOrURL = function (uuid: string, url: string, t?: Sequelize.Transaction) {
   const query: Sequelize.FindOptions<VideoAttributes> = {
     where: {
@@ -989,7 +1059,7 @@ loadAndPopulateAccount = function (id: number) {
   return Video.findById(id, options)
 }
 
-loadAndPopulateAccountAndPodAndTags = function (id: number) {
+loadAndPopulateAccountAndServerAndTags = function (id: number) {
   const options = {
     include: [
       {
@@ -997,7 +1067,7 @@ loadAndPopulateAccountAndPodAndTags = function (id: number) {
         include: [
           {
             model: Video['sequelize'].models.Account,
-            include: [ { model: Video['sequelize'].models.Pod, required: false } ]
+            include: [ { model: Video['sequelize'].models.Server, required: false } ]
           }
         ]
       },
@@ -1009,7 +1079,7 @@ loadAndPopulateAccountAndPodAndTags = function (id: number) {
   return Video.findById(id, options)
 }
 
-loadByUUIDAndPopulateAccountAndPodAndTags = function (uuid: string) {
+loadByUUIDAndPopulateAccountAndServerAndTags = function (uuid: string) {
   const options = {
     where: {
       uuid
@@ -1020,7 +1090,7 @@ loadByUUIDAndPopulateAccountAndPodAndTags = function (uuid: string) {
         include: [
           {
             model: Video['sequelize'].models.Account,
-            include: [ { model: Video['sequelize'].models.Pod, required: false } ]
+            include: [ { model: Video['sequelize'].models.Server, required: false } ]
           }
         ]
       },
@@ -1032,15 +1102,15 @@ loadByUUIDAndPopulateAccountAndPodAndTags = function (uuid: string) {
   return Video.findOne(options)
 }
 
-searchAndPopulateAccountAndPodAndTags = function (value: string, field: string, start: number, count: number, sort: string) {
-  const podInclude: Sequelize.IncludeOptions = {
-    model: Video['sequelize'].models.Pod,
+searchAndPopulateAccountAndServerAndTags = function (value: string, field: string, start: number, count: number, sort: string) {
+  const serverInclude: Sequelize.IncludeOptions = {
+    model: Video['sequelize'].models.Server,
     required: false
   }
 
   const accountInclude: Sequelize.IncludeOptions = {
     model: Video['sequelize'].models.Account,
-    include: [ podInclude ]
+    include: [ serverInclude ]
   }
 
   const videoChannelInclude: Sequelize.IncludeOptions = {
@@ -1071,13 +1141,13 @@ searchAndPopulateAccountAndPodAndTags = function (value: string, field: string,
        )`
     )
   } else if (field === 'host') {
-    // FIXME: Include our pod? (not stored in the database)
-    podInclude.where = {
+    // FIXME: Include our server? (not stored in the database)
+    serverInclude.where = {
       host: {
         [Sequelize.Op.iLike]: '%' + value + '%'
       }
     }
-    podInclude.required = true
+    serverInclude.required = true
   } else if (field === 'account') {
     accountInclude.where = {
       name: {
@@ -1123,8 +1193,8 @@ function getBaseUrls (video: VideoInstance) {
     baseUrlHttp = CONFIG.WEBSERVER.URL
     baseUrlWs = CONFIG.WEBSERVER.WS + '://' + CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT
   } else {
-    baseUrlHttp = REMOTE_SCHEME.HTTP + '://' + video.VideoChannel.Account.Pod.host
-    baseUrlWs = REMOTE_SCHEME.WS + '://' + video.VideoChannel.Account.Pod.host
+    baseUrlHttp = REMOTE_SCHEME.HTTP + '://' + video.VideoChannel.Account.Server.host
+    baseUrlWs = REMOTE_SCHEME.WS + '://' + video.VideoChannel.Account.Server.host
   }
 
   return { baseUrlHttp, baseUrlWs }