aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--client/src/app/+videos/+video-watch/shared/metadata/video-description.component.html2
-rw-r--r--client/src/app/+videos/+video-watch/shared/metadata/video-description.component.ts39
-rw-r--r--client/src/app/shared/shared-main/video/video.model.ts3
-rw-r--r--server/helpers/custom-validators/activitypub/videos.ts10
-rw-r--r--server/helpers/custom-validators/videos.ts5
-rw-r--r--server/models/video/formatter/video-format-utils.ts2
-rw-r--r--server/tests/api/videos/video-description.ts23
-rw-r--r--shared/models/videos/video.model.ts6
8 files changed, 58 insertions, 32 deletions
diff --git a/client/src/app/+videos/+video-watch/shared/metadata/video-description.component.html b/client/src/app/+videos/+video-watch/shared/metadata/video-description.component.html
index fa4dbb3ca..d847daff7 100644
--- a/client/src/app/+videos/+video-watch/shared/metadata/video-description.component.html
+++ b/client/src/app/+videos/+video-watch/shared/metadata/video-description.component.html
@@ -1,7 +1,7 @@
1<div class="video-info-description"> 1<div class="video-info-description">
2 <div 2 <div
3 class="video-info-description-html" 3 class="video-info-description-html"
4 [innerHTML]="videoHTMLDescription" 4 [innerHTML]="getHTMLDescription()"
5 (timestampClicked)="onTimestampClicked($event)" 5 (timestampClicked)="onTimestampClicked($event)"
6 myTimestampRouteTransformer 6 myTimestampRouteTransformer
7 ></div> 7 ></div>
diff --git a/client/src/app/+videos/+video-watch/shared/metadata/video-description.component.ts b/client/src/app/+videos/+video-watch/shared/metadata/video-description.component.ts
index 72b09a274..d01080611 100644
--- a/client/src/app/+videos/+video-watch/shared/metadata/video-description.component.ts
+++ b/client/src/app/+videos/+video-watch/shared/metadata/video-description.component.ts
@@ -15,8 +15,10 @@ export class VideoDescriptionComponent implements OnChanges {
15 15
16 descriptionLoading = false 16 descriptionLoading = false
17 completeDescriptionShown = false 17 completeDescriptionShown = false
18 completeVideoDescription: string 18
19 shortVideoDescription: string 19 completeVideoDescriptionLoaded = false
20
21 videoHTMLTruncatedDescription = ''
20 videoHTMLDescription = '' 22 videoHTMLDescription = ''
21 23
22 constructor ( 24 constructor (
@@ -28,22 +30,19 @@ export class VideoDescriptionComponent implements OnChanges {
28 ngOnChanges () { 30 ngOnChanges () {
29 this.descriptionLoading = false 31 this.descriptionLoading = false
30 this.completeDescriptionShown = false 32 this.completeDescriptionShown = false
31 this.completeVideoDescription = undefined
32 33
33 this.setVideoDescriptionHTML() 34 this.setVideoDescriptionHTML()
34 } 35 }
35 36
36 showMoreDescription () { 37 showMoreDescription () {
37 if (this.completeVideoDescription === undefined) { 38 if (!this.completeVideoDescriptionLoaded) {
38 return this.loadCompleteDescription() 39 return this.loadCompleteDescription()
39 } 40 }
40 41
41 this.updateVideoDescription(this.completeVideoDescription)
42 this.completeDescriptionShown = true 42 this.completeDescriptionShown = true
43 } 43 }
44 44
45 showLessDescription () { 45 showLessDescription () {
46 this.updateVideoDescription(this.shortVideoDescription)
47 this.completeDescriptionShown = false 46 this.completeDescriptionShown = false
48 } 47 }
49 48
@@ -56,10 +55,10 @@ export class VideoDescriptionComponent implements OnChanges {
56 this.completeDescriptionShown = true 55 this.completeDescriptionShown = true
57 this.descriptionLoading = false 56 this.descriptionLoading = false
58 57
59 this.shortVideoDescription = this.video.description 58 this.video.description = description
60 this.completeVideoDescription = description
61 59
62 this.updateVideoDescription(this.completeVideoDescription) 60 this.setVideoDescriptionHTML()
61 .catch(err => logger.error(err))
63 }, 62 },
64 63
65 error: err => { 64 error: err => {
@@ -73,15 +72,25 @@ export class VideoDescriptionComponent implements OnChanges {
73 this.timestampClicked.emit(timestamp) 72 this.timestampClicked.emit(timestamp)
74 } 73 }
75 74
76 private updateVideoDescription (description: string) { 75 getHTMLDescription () {
77 this.video.description = description 76 if (this.completeDescriptionShown) {
78 this.setVideoDescriptionHTML() 77 return this.videoHTMLDescription
79 .catch(err => logger.error(err)) 78 }
79
80 return this.videoHTMLTruncatedDescription
80 } 81 }
81 82
82 private async setVideoDescriptionHTML () { 83 private async setVideoDescriptionHTML () {
83 const html = await this.markdownService.textMarkdownToHTML({ markdown: this.video.description }) 84 {
85 const html = await this.markdownService.textMarkdownToHTML({ markdown: this.video.description })
84 86
85 this.videoHTMLDescription = this.markdownService.processVideoTimestamps(this.video.shortUUID, html) 87 this.videoHTMLDescription = this.markdownService.processVideoTimestamps(this.video.shortUUID, html)
88 }
89
90 {
91 const html = await this.markdownService.textMarkdownToHTML({ markdown: this.video.truncatedDescription })
92
93 this.videoHTMLTruncatedDescription = this.markdownService.processVideoTimestamps(this.video.shortUUID, html)
94 }
86 } 95 }
87} 96}
diff --git a/client/src/app/shared/shared-main/video/video.model.ts b/client/src/app/shared/shared-main/video/video.model.ts
index c9c6b979c..6fdffb394 100644
--- a/client/src/app/shared/shared-main/video/video.model.ts
+++ b/client/src/app/shared/shared-main/video/video.model.ts
@@ -34,6 +34,7 @@ export class Video implements VideoServerModel {
34 language: VideoConstant<string> 34 language: VideoConstant<string>
35 privacy: VideoConstant<VideoPrivacy> 35 privacy: VideoConstant<VideoPrivacy>
36 36
37 truncatedDescription: string
37 description: string 38 description: string
38 39
39 duration: number 40 duration: number
@@ -134,6 +135,8 @@ export class Video implements VideoServerModel {
134 this.privacy = hash.privacy 135 this.privacy = hash.privacy
135 this.waitTranscoding = hash.waitTranscoding 136 this.waitTranscoding = hash.waitTranscoding
136 this.state = hash.state 137 this.state = hash.state
138
139 this.truncatedDescription = hash.truncatedDescription
137 this.description = hash.description 140 this.description = hash.description
138 141
139 this.isLive = hash.isLive 142 this.isLive = hash.isLive
diff --git a/server/helpers/custom-validators/activitypub/videos.ts b/server/helpers/custom-validators/activitypub/videos.ts
index 2a2f008b9..97b3577af 100644
--- a/server/helpers/custom-validators/activitypub/videos.ts
+++ b/server/helpers/custom-validators/activitypub/videos.ts
@@ -7,11 +7,11 @@ import { peertubeTruncate } from '../../core-utils'
7import { isArray, isBooleanValid, isDateValid, isUUIDValid } from '../misc' 7import { isArray, isBooleanValid, isDateValid, isUUIDValid } from '../misc'
8import { isLiveLatencyModeValid } from '../video-lives' 8import { isLiveLatencyModeValid } from '../video-lives'
9import { 9import {
10 isVideoDescriptionValid,
10 isVideoDurationValid, 11 isVideoDurationValid,
11 isVideoNameValid, 12 isVideoNameValid,
12 isVideoStateValid, 13 isVideoStateValid,
13 isVideoTagValid, 14 isVideoTagValid,
14 isVideoTruncatedDescriptionValid,
15 isVideoViewsValid 15 isVideoViewsValid
16} from '../videos' 16} from '../videos'
17import { isActivityPubUrlValid, isActivityPubVideoDurationValid, isBaseActivityValid, setValidAttributedTo } from './misc' 17import { isActivityPubUrlValid, isActivityPubVideoDurationValid, isBaseActivityValid, setValidAttributedTo } from './misc'
@@ -32,7 +32,7 @@ function sanitizeAndCheckVideoTorrentObject (video: any) {
32 logger.debug('Video has invalid urls', { video }) 32 logger.debug('Video has invalid urls', { video })
33 return false 33 return false
34 } 34 }
35 if (!setRemoteVideoTruncatedContent(video)) { 35 if (!setRemoteVideoContent(video)) {
36 logger.debug('Video has invalid content', { video }) 36 logger.debug('Video has invalid content', { video })
37 return false 37 return false
38 } 38 }
@@ -168,7 +168,7 @@ function isRemoteStringIdentifierValid (data: any) {
168} 168}
169 169
170function isRemoteVideoContentValid (mediaType: string, content: string) { 170function isRemoteVideoContentValid (mediaType: string, content: string) {
171 return mediaType === 'text/markdown' && isVideoTruncatedDescriptionValid(content) 171 return mediaType === 'text/markdown' && isVideoDescriptionValid(content)
172} 172}
173 173
174function setValidRemoteIcon (video: any) { 174function setValidRemoteIcon (video: any) {
@@ -194,9 +194,9 @@ function setValidRemoteVideoUrls (video: any) {
194 return true 194 return true
195} 195}
196 196
197function setRemoteVideoTruncatedContent (video: any) { 197function setRemoteVideoContent (video: any) {
198 if (video.content) { 198 if (video.content) {
199 video.content = peertubeTruncate(video.content, { length: CONSTRAINTS_FIELDS.VIDEOS.TRUNCATED_DESCRIPTION.max }) 199 video.content = peertubeTruncate(video.content, { length: CONSTRAINTS_FIELDS.VIDEOS.DESCRIPTION.max })
200 } 200 }
201 201
202 return true 202 return true
diff --git a/server/helpers/custom-validators/videos.ts b/server/helpers/custom-validators/videos.ts
index 3ebfe2937..9e8177f77 100644
--- a/server/helpers/custom-validators/videos.ts
+++ b/server/helpers/custom-validators/videos.ts
@@ -45,10 +45,6 @@ function isVideoDurationValid (value: string) {
45 return exists(value) && validator.isInt(value + '', VIDEOS_CONSTRAINTS_FIELDS.DURATION) 45 return exists(value) && validator.isInt(value + '', VIDEOS_CONSTRAINTS_FIELDS.DURATION)
46} 46}
47 47
48function isVideoTruncatedDescriptionValid (value: string) {
49 return exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.TRUNCATED_DESCRIPTION)
50}
51
52function isVideoDescriptionValid (value: string) { 48function isVideoDescriptionValid (value: string) {
53 return value === null || (exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.DESCRIPTION)) 49 return value === null || (exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.DESCRIPTION))
54} 50}
@@ -151,7 +147,6 @@ export {
151 isVideoCategoryValid, 147 isVideoCategoryValid,
152 isVideoLicenceValid, 148 isVideoLicenceValid,
153 isVideoLanguageValid, 149 isVideoLanguageValid,
154 isVideoTruncatedDescriptionValid,
155 isVideoDescriptionValid, 150 isVideoDescriptionValid,
156 isVideoFileInfoHashValid, 151 isVideoFileInfoHashValid,
157 isVideoNameValid, 152 isVideoNameValid,
diff --git a/server/models/video/formatter/video-format-utils.ts b/server/models/video/formatter/video-format-utils.ts
index 76745f4b5..240619f69 100644
--- a/server/models/video/formatter/video-format-utils.ts
+++ b/server/models/video/formatter/video-format-utils.ts
@@ -103,6 +103,7 @@ function videoModelToFormattedJSON (video: MVideoFormattable, options: VideoForm
103 }, 103 },
104 nsfw: video.nsfw, 104 nsfw: video.nsfw,
105 105
106 truncatedDescription: video.getTruncatedDescription(),
106 description: options && options.completeDescription === true 107 description: options && options.completeDescription === true
107 ? video.description 108 ? video.description
108 : video.getTruncatedDescription(), 109 : video.getTruncatedDescription(),
@@ -181,6 +182,7 @@ function videoModelToFormattedDetailsJSON (video: MVideoFormattableDetails): Vid
181 const span = tracer.startSpan('peertube.VideoModel.toFormattedDetailsJSON') 182 const span = tracer.startSpan('peertube.VideoModel.toFormattedDetailsJSON')
182 183
183 const videoJSON = video.toFormattedJSON({ 184 const videoJSON = video.toFormattedJSON({
185 completeDescription: true,
184 additionalAttributes: { 186 additionalAttributes: {
185 scheduledUpdate: true, 187 scheduledUpdate: true,
186 blacklistInfo: true, 188 blacklistInfo: true,
diff --git a/server/tests/api/videos/video-description.ts b/server/tests/api/videos/video-description.ts
index a4b3ff6e7..c4185882a 100644
--- a/server/tests/api/videos/video-description.ts
+++ b/server/tests/api/videos/video-description.ts
@@ -14,8 +14,12 @@ describe('Test video description', function () {
14 let servers: PeerTubeServer[] = [] 14 let servers: PeerTubeServer[] = []
15 let videoUUID = '' 15 let videoUUID = ''
16 let videoId: number 16 let videoId: number
17
17 const longDescription = 'my super description for server 1'.repeat(50) 18 const longDescription = 'my super description for server 1'.repeat(50)
18 19
20 // 30 characters * 6 -> 240 characters
21 const truncatedDescription = 'my super description for server 1'.repeat(7) + 'my super descrip...'
22
19 before(async function () { 23 before(async function () {
20 this.timeout(40000) 24 this.timeout(40000)
21 25
@@ -45,15 +49,22 @@ describe('Test video description', function () {
45 videoUUID = data[0].uuid 49 videoUUID = data[0].uuid
46 }) 50 })
47 51
48 it('Should have a truncated description on each server', async function () { 52 it('Should have a truncated description on each server when listing videos', async function () {
49 for (const server of servers) { 53 for (const server of servers) {
50 const video = await server.videos.get({ id: videoUUID }) 54 const { data } = await server.videos.list()
51 55 const video = data.find(v => v.uuid === videoUUID)
52 // 30 characters * 6 -> 240 characters
53 const truncatedDescription = 'my super description for server 1'.repeat(7) +
54 'my super descrip...'
55 56
56 expect(video.description).to.equal(truncatedDescription) 57 expect(video.description).to.equal(truncatedDescription)
58 expect(video.truncatedDescription).to.equal(truncatedDescription)
59 }
60 })
61
62 it('Should not have a truncated description on each server when getting videos', async function () {
63 for (const server of servers) {
64 const video = await server.videos.get({ id: videoUUID })
65
66 expect(video.description).to.equal(longDescription)
67 expect(video.truncatedDescription).to.equal(truncatedDescription)
57 } 68 }
58 }) 69 })
59 70
diff --git a/shared/models/videos/video.model.ts b/shared/models/videos/video.model.ts
index d9765dbd6..06ffb327c 100644
--- a/shared/models/videos/video.model.ts
+++ b/shared/models/videos/video.model.ts
@@ -20,7 +20,11 @@ export interface Video {
20 licence: VideoConstant<number> 20 licence: VideoConstant<number>
21 language: VideoConstant<string> 21 language: VideoConstant<string>
22 privacy: VideoConstant<VideoPrivacy> 22 privacy: VideoConstant<VideoPrivacy>
23
24 // Deprecated in 5.0 in favour of truncatedDescription
23 description: string 25 description: string
26 truncatedDescription: string
27
24 duration: number 28 duration: number
25 isLocal: boolean 29 isLocal: boolean
26 name: string 30 name: string
@@ -70,7 +74,9 @@ export interface Video {
70} 74}
71 75
72export interface VideoDetails extends Video { 76export interface VideoDetails extends Video {
77 // Deprecated in 5.0
73 descriptionPath: string 78 descriptionPath: string
79
74 support: string 80 support: string
75 channel: VideoChannel 81 channel: VideoChannel
76 account: Account 82 account: Account