Truncate correctly video descriptions
authorChocobozzz <me@florianbigard.com>
Wed, 28 Mar 2018 09:00:02 +0000 (11:00 +0200)
committerChocobozzz <me@florianbigard.com>
Wed, 28 Mar 2018 09:00:02 +0000 (11:00 +0200)
server/helpers/core-utils.ts
server/helpers/custom-validators/activitypub/videos.ts
server/lib/activitypub/process/process.ts
server/models/video/video.ts

index 65f18d6442ac34da6d8719e15a15cc18dfc838cb..a3dfe27b5a0c3125d1b358ae7a1470d686cbe500 100644 (file)
@@ -12,6 +12,7 @@ import { isAbsolute, join } from 'path'
 import * as pem from 'pem'
 import * as rimraf from 'rimraf'
 import { URL } from 'url'
+import { truncate } from 'lodash'
 
 function sanitizeUrl (url: string) {
   const urlObject = new URL(url)
@@ -78,6 +79,22 @@ function buildPath (path: string) {
   return join(root(), path)
 }
 
+// Consistent with .length, lodash truncate function is not
+function peertubeTruncate (str: string, maxLength: number) {
+  const options = {
+    length: maxLength
+  }
+  const truncatedStr = truncate(str, options)
+
+  // The truncated string is okay, we can return it
+  if (truncatedStr.length <= maxLength) return truncatedStr
+
+  // Lodash takes into account all UTF characters, whereas String.prototype.length does not: some characters have a length of 2
+  // We always use the .length so we need to truncate more if needed
+  options.length -= truncatedStr.length - maxLength
+  return truncate(str, options)
+}
+
 function promisify0<A> (func: (cb: (err: any, result: A) => void) => void): () => Promise<A> {
   return function promisified (): Promise<A> {
     return new Promise<A>((resolve: (arg: A) => void, reject: (err: any) => void) => {
@@ -145,6 +162,7 @@ export {
   sanitizeUrl,
   sanitizeHost,
   buildPath,
+  peertubeTruncate,
 
   promisify0,
   promisify1,
index 10588423a930bf79c6f65b4a9fc9ed54945aa013..3af587a32a214c80fc844cac2488cf0b23ef9d7d 100644 (file)
@@ -1,5 +1,6 @@
 import * as validator from 'validator'
-import { ACTIVITY_PUB } from '../../../initializers'
+import { ACTIVITY_PUB, CONSTRAINTS_FIELDS } from '../../../initializers'
+import { peertubeTruncate } from '../../core-utils'
 import { exists, isBooleanValid, isDateValid, isUUIDValid } from '../misc'
 import {
   isVideoAbuseReasonValid,
@@ -56,6 +57,7 @@ function isVideoTorrentObjectValid (video: any) {
     isBooleanValid(video.commentsEnabled) &&
     isDateValid(video.published) &&
     isDateValid(video.updated) &&
+    setTruncatedContent(video) &&
     (!video.content || isRemoteVideoContentValid(video.mediaType, video.content)) &&
     isRemoteVideoIconValid(video.icon) &&
     setValidRemoteVideoUrls(video) &&
@@ -111,6 +113,14 @@ function setValidRemoteVideoUrls (video: any) {
   return true
 }
 
+function setTruncatedContent (video: any) {
+  if (video.content) {
+    video.content = peertubeTruncate(video.content, CONSTRAINTS_FIELDS.VIDEOS.TRUNCATED_DESCRIPTION.max)
+  }
+
+  return true
+}
+
 function isRemoteVideoUrlValid (url: any) {
   return url.type === 'Link' &&
     (
index 10d8ba1892dbe12708c9075d0569ee3d6847ea51..da91675ced3d7622f5fdb7505837a775aa043b4a 100644 (file)
@@ -43,7 +43,7 @@ async function processActivities (activities: Activity[], signatureActor?: Actor
     try {
       await activityProcessor(activity, inboxActor)
     } catch (err) {
-      logger.warn('Cannot process activity %s.', activity.type, { error: err.stack })
+      logger.warn('Cannot process activity %s.', activity.type, { err })
     }
   }
 }
index a28b5209b56a1fba95af02afbbadfe354f17e82c..7c56c65a6ea4a21e32c032e99b1d3b819e684e11 100644 (file)
@@ -1,5 +1,5 @@
 import * as Bluebird from 'bluebird'
-import { map, maxBy, truncate } from 'lodash'
+import { map, maxBy } from 'lodash'
 import * as magnetUtil from 'magnet-uri'
 import * as parseTorrent from 'parse-torrent'
 import { join } from 'path'
@@ -31,7 +31,10 @@ import { VideoTorrentObject } from '../../../shared/models/activitypub/objects'
 import { Video, VideoDetails } from '../../../shared/models/videos'
 import { VideoFilter } from '../../../shared/models/videos/video-query.type'
 import { activityPubCollection } from '../../helpers/activitypub'
-import { createTorrentPromise, renamePromise, statPromise, unlinkPromise, writeFilePromise } from '../../helpers/core-utils'
+import {
+  createTorrentPromise, peertubeTruncate, renamePromise, statPromise, unlinkPromise,
+  writeFilePromise
+} from '../../helpers/core-utils'
 import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
 import { isBooleanValid } from '../../helpers/custom-validators/misc'
 import {
@@ -1191,19 +1194,7 @@ export class VideoModel extends Model<VideoModel> {
     if (!this.description) return null
 
     const maxLength = CONSTRAINTS_FIELDS.VIDEOS.TRUNCATED_DESCRIPTION.max
-
-    const options = {
-      length: maxLength
-    }
-    const truncatedDescription = truncate(this.description, options)
-
-    // The truncated string is okay, we can return it
-    if (truncatedDescription.length <= maxLength) return truncatedDescription
-
-    // Lodash takes into account all UTF characters, whereas String.prototype.length does not: some characters have a length of 2
-    // We always use the .length so we need to truncate more if needed
-    options.length -= maxLength - truncatedDescription.length
-    return truncate(this.description, options)
+    return peertubeTruncate(this.description, maxLength)
   }
 
   optimizeOriginalVideofile = async function () {