]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blobdiff - server/models/video/video.ts
Lazy description and previews to video form
[github/Chocobozzz/PeerTube.git] / server / models / video / video.ts
index ec14939c2618f14a6dfed8d2a00867f476f07de7..1877c506ae63bf07acbf85e5f359df39eb85c816 100644 (file)
@@ -6,7 +6,7 @@ import * as parseTorrent from 'parse-torrent'
 import { join } from 'path'
 import * as Sequelize from 'sequelize'
 import * as Promise from 'bluebird'
-import { maxBy } from 'lodash'
+import { maxBy, truncate } from 'lodash'
 
 import { TagInstance } from './tag-interface'
 import {
@@ -35,7 +35,10 @@ import {
   VIDEO_CATEGORIES,
   VIDEO_LICENCES,
   VIDEO_LANGUAGES,
-  THUMBNAILS_SIZE
+  THUMBNAILS_SIZE,
+  PREVIEWS_SIZE,
+  CONSTRAINTS_FIELDS,
+  API_VERSION
 } from '../../initializers'
 import { removeVideoToFriends } from '../../lib'
 import { VideoResolution } from '../../../shared'
@@ -48,7 +51,6 @@ import {
 
   VideoMethods
 } from './video-interface'
-import { PREVIEWS_SIZE } from '../../initializers/constants'
 
 let Video: Sequelize.Model<VideoInstance, VideoAttributes>
 let getOriginalFile: VideoMethods.GetOriginalFile
@@ -71,6 +73,8 @@ 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 generateThumbnailFromData: VideoMethods.GenerateThumbnailFromData
 let list: VideoMethods.List
@@ -153,7 +157,7 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da
         }
       },
       description: {
-        type: DataTypes.STRING,
+        type: DataTypes.STRING(CONSTRAINTS_FIELDS.VIDEOS.DESCRIPTION.max),
         allowNull: false,
         validate: {
           descriptionValid: value => {
@@ -276,7 +280,9 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da
     optimizeOriginalVideofile,
     transcodeOriginalVideofile,
     getOriginalFileHeight,
-    getEmbedPath
+    getEmbedPath,
+    getTruncatedDescription,
+    getDescriptionPath
   ]
   addMethodsToModel(Video, classMethods, instanceMethods)
 
@@ -317,7 +323,7 @@ function associate (models) {
   })
 }
 
-function afterDestroy (video: VideoInstance, options: { transaction: Sequelize.Transaction }) {
+function afterDestroy (video: VideoInstance) {
   const tasks = []
 
   tasks.push(
@@ -331,7 +337,7 @@ function afterDestroy (video: VideoInstance, options: { transaction: Sequelize.T
 
     tasks.push(
       video.removePreview(),
-      removeVideoToFriends(removeVideoToFriendsParams, options.transaction)
+      removeVideoToFriends(removeVideoToFriendsParams)
     )
 
     // Remove physical files and torrents
@@ -473,7 +479,7 @@ toFormattedJSON = function (this: VideoInstance) {
     language: this.language,
     languageLabel,
     nsfw: this.nsfw,
-    description: this.description,
+    description: this.getTruncatedDescription(),
     podHost,
     isLocal: this.isOwned(),
     author: this.VideoChannel.Author.name,
@@ -493,59 +499,17 @@ toFormattedJSON = function (this: VideoInstance) {
 }
 
 toFormattedDetailsJSON = function (this: VideoInstance) {
-  let podHost
-
-  if (this.VideoChannel.Author.Pod) {
-    podHost = this.VideoChannel.Author.Pod.host
-  } else {
-    // It means it's our video
-    podHost = CONFIG.WEBSERVER.HOST
-  }
+  const formattedJson = this.toFormattedJSON()
 
-  // Maybe our pod is not up to date and there are new categories since our version
-  let categoryLabel = VIDEO_CATEGORIES[this.category]
-  if (!categoryLabel) categoryLabel = 'Misc'
-
-  // Maybe our pod is not up to date and there are new licences since our version
-  let licenceLabel = VIDEO_LICENCES[this.licence]
-  if (!licenceLabel) licenceLabel = 'Unknown'
-
-  // Language is an optional attribute
-  let languageLabel = VIDEO_LANGUAGES[this.language]
-  if (!languageLabel) languageLabel = 'Unknown'
-
-  const json = {
-    id: this.id,
-    uuid: this.uuid,
-    name: this.name,
-    category: this.category,
-    categoryLabel,
-    licence: this.licence,
-    licenceLabel,
-    language: this.language,
-    languageLabel,
-    nsfw: this.nsfw,
-    description: this.description,
-    podHost,
-    isLocal: this.isOwned(),
-    author: this.VideoChannel.Author.name,
-    duration: this.duration,
-    views: this.views,
-    likes: this.likes,
-    dislikes: this.dislikes,
-    tags: map<TagInstance, string>(this.Tags, 'name'),
-    thumbnailPath: this.getThumbnailPath(),
-    previewPath: this.getPreviewPath(),
-    embedPath: this.getEmbedPath(),
-    createdAt: this.createdAt,
-    updatedAt: this.updatedAt,
+  const detailsJson = {
+    descriptionPath: this.getDescriptionPath(),
     channel: this.VideoChannel.toFormattedJSON(),
     files: []
   }
 
   // Format and sort video files
   const { baseUrlHttp, baseUrlWs } = getBaseUrls(this)
-  json.files = this.VideoFiles
+  detailsJson.files = this.VideoFiles
                    .map(videoFile => {
                      let resolutionLabel = videoFile.resolution + 'p'
 
@@ -566,7 +530,7 @@ toFormattedDetailsJSON = function (this: VideoInstance) {
                      return -1
                    })
 
-  return json
+  return Object.assign(formattedJson, detailsJson)
 }
 
 toAddRemoteJSON = function (this: VideoInstance) {
@@ -581,7 +545,7 @@ toAddRemoteJSON = function (this: VideoInstance) {
       licence: this.licence,
       language: this.language,
       nsfw: this.nsfw,
-      description: this.description,
+      truncatedDescription: this.getTruncatedDescription(),
       channelUUID: this.VideoChannel.uuid,
       duration: this.duration,
       thumbnailData: thumbnailData.toString('binary'),
@@ -615,7 +579,7 @@ toUpdateRemoteJSON = function (this: VideoInstance) {
     licence: this.licence,
     language: this.language,
     nsfw: this.nsfw,
-    description: this.description,
+    truncatedDescription: this.getTruncatedDescription(),
     duration: this.duration,
     tags: map<TagInstance, string>(this.Tags, 'name'),
     createdAt: this.createdAt,
@@ -638,6 +602,14 @@ toUpdateRemoteJSON = function (this: VideoInstance) {
   return json
 }
 
+getTruncatedDescription = function (this: VideoInstance) {
+  const options = {
+    length: CONSTRAINTS_FIELDS.VIDEOS.TRUNCATED_DESCRIPTION.max
+  }
+
+  return truncate(this.description, options)
+}
+
 optimizeOriginalVideofile = function (this: VideoInstance) {
   const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR
   const newExtname = '.mp4'
@@ -730,6 +702,10 @@ getOriginalFileHeight = function (this: VideoInstance) {
   return getVideoFileHeight(originalFilePath)
 }
 
+getDescriptionPath = function (this: VideoInstance) {
+  return `/api/${API_VERSION}/videos/${this.uuid}/description`
+}
+
 removeThumbnail = function (this: VideoInstance) {
   const thumbnailPath = join(CONFIG.STORAGE.THUMBNAILS_DIR, this.getThumbnailName())
   return unlinkPromise(thumbnailPath)
@@ -1012,7 +988,7 @@ searchAndPopulateAuthorAndPodAndTags = function (value: string, field: string, s
     }
   } else if (field === 'tags') {
     const escapedValue = Video['sequelize'].escape('%' + value + '%')
-    query.where['id'].$in = Video['sequelize'].literal(
+    query.where['id'][Sequelize.Op.in] = Video['sequelize'].literal(
       `(SELECT "VideoTags"."videoId"
         FROM "Tags"
         INNER JOIN "VideoTags" ON "Tags"."id" = "VideoTags"."tagId"
@@ -1023,19 +999,19 @@ searchAndPopulateAuthorAndPodAndTags = function (value: string, field: string, s
     // FIXME: Include our pod? (not stored in the database)
     podInclude.where = {
       host: {
-        $iLike: '%' + value + '%'
+        [Sequelize.Op.iLike]: '%' + value + '%'
       }
     }
     podInclude.required = true
   } else if (field === 'author') {
     authorInclude.where = {
       name: {
-        $iLike: '%' + value + '%'
+        [Sequelize.Op.iLike]: '%' + value + '%'
       }
     }
   } else {
     query.where[field] = {
-      $iLike: '%' + value + '%'
+      [Sequelize.Op.iLike]: '%' + value + '%'
     }
   }
 
@@ -1056,7 +1032,7 @@ searchAndPopulateAuthorAndPodAndTags = function (value: string, field: string, s
 function createBaseVideosWhere () {
   return {
     id: {
-      $notIn: Video['sequelize'].literal(
+      [Sequelize.Op.notIn]: Video['sequelize'].literal(
         '(SELECT "BlacklistedVideos"."videoId" FROM "BlacklistedVideos")'
       )
     }