Federate entire description

Introduce an explicit field truncatedDescription
description in video list is deprecated
description in video get will contain the entire description
pull/5441/head
Chocobozzz 2022-11-14 12:06:31 +01:00
parent 44e702ded4
commit f713f36bdf
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
8 changed files with 58 additions and 32 deletions

View File

@ -1,7 +1,7 @@
<div class="video-info-description"> <div class="video-info-description">
<div <div
class="video-info-description-html" class="video-info-description-html"
[innerHTML]="videoHTMLDescription" [innerHTML]="getHTMLDescription()"
(timestampClicked)="onTimestampClicked($event)" (timestampClicked)="onTimestampClicked($event)"
myTimestampRouteTransformer myTimestampRouteTransformer
></div> ></div>

View File

@ -15,8 +15,10 @@ export class VideoDescriptionComponent implements OnChanges {
descriptionLoading = false descriptionLoading = false
completeDescriptionShown = false completeDescriptionShown = false
completeVideoDescription: string
shortVideoDescription: string completeVideoDescriptionLoaded = false
videoHTMLTruncatedDescription = ''
videoHTMLDescription = '' videoHTMLDescription = ''
constructor ( constructor (
@ -28,22 +30,19 @@ export class VideoDescriptionComponent implements OnChanges {
ngOnChanges () { ngOnChanges () {
this.descriptionLoading = false this.descriptionLoading = false
this.completeDescriptionShown = false this.completeDescriptionShown = false
this.completeVideoDescription = undefined
this.setVideoDescriptionHTML() this.setVideoDescriptionHTML()
} }
showMoreDescription () { showMoreDescription () {
if (this.completeVideoDescription === undefined) { if (!this.completeVideoDescriptionLoaded) {
return this.loadCompleteDescription() return this.loadCompleteDescription()
} }
this.updateVideoDescription(this.completeVideoDescription)
this.completeDescriptionShown = true this.completeDescriptionShown = true
} }
showLessDescription () { showLessDescription () {
this.updateVideoDescription(this.shortVideoDescription)
this.completeDescriptionShown = false this.completeDescriptionShown = false
} }
@ -56,10 +55,10 @@ export class VideoDescriptionComponent implements OnChanges {
this.completeDescriptionShown = true this.completeDescriptionShown = true
this.descriptionLoading = false this.descriptionLoading = false
this.shortVideoDescription = this.video.description this.video.description = description
this.completeVideoDescription = description
this.updateVideoDescription(this.completeVideoDescription) this.setVideoDescriptionHTML()
.catch(err => logger.error(err))
}, },
error: err => { error: err => {
@ -73,15 +72,25 @@ export class VideoDescriptionComponent implements OnChanges {
this.timestampClicked.emit(timestamp) this.timestampClicked.emit(timestamp)
} }
private updateVideoDescription (description: string) { getHTMLDescription () {
this.video.description = description if (this.completeDescriptionShown) {
this.setVideoDescriptionHTML() return this.videoHTMLDescription
.catch(err => logger.error(err)) }
return this.videoHTMLTruncatedDescription
} }
private async setVideoDescriptionHTML () { private async setVideoDescriptionHTML () {
{
const html = await this.markdownService.textMarkdownToHTML({ markdown: this.video.description }) const html = await this.markdownService.textMarkdownToHTML({ markdown: this.video.description })
this.videoHTMLDescription = this.markdownService.processVideoTimestamps(this.video.shortUUID, html) this.videoHTMLDescription = this.markdownService.processVideoTimestamps(this.video.shortUUID, html)
} }
{
const html = await this.markdownService.textMarkdownToHTML({ markdown: this.video.truncatedDescription })
this.videoHTMLTruncatedDescription = this.markdownService.processVideoTimestamps(this.video.shortUUID, html)
}
}
} }

View File

@ -34,6 +34,7 @@ export class Video implements VideoServerModel {
language: VideoConstant<string> language: VideoConstant<string>
privacy: VideoConstant<VideoPrivacy> privacy: VideoConstant<VideoPrivacy>
truncatedDescription: string
description: string description: string
duration: number duration: number
@ -134,6 +135,8 @@ export class Video implements VideoServerModel {
this.privacy = hash.privacy this.privacy = hash.privacy
this.waitTranscoding = hash.waitTranscoding this.waitTranscoding = hash.waitTranscoding
this.state = hash.state this.state = hash.state
this.truncatedDescription = hash.truncatedDescription
this.description = hash.description this.description = hash.description
this.isLive = hash.isLive this.isLive = hash.isLive

View File

@ -7,11 +7,11 @@ import { peertubeTruncate } from '../../core-utils'
import { isArray, isBooleanValid, isDateValid, isUUIDValid } from '../misc' import { isArray, isBooleanValid, isDateValid, isUUIDValid } from '../misc'
import { isLiveLatencyModeValid } from '../video-lives' import { isLiveLatencyModeValid } from '../video-lives'
import { import {
isVideoDescriptionValid,
isVideoDurationValid, isVideoDurationValid,
isVideoNameValid, isVideoNameValid,
isVideoStateValid, isVideoStateValid,
isVideoTagValid, isVideoTagValid,
isVideoTruncatedDescriptionValid,
isVideoViewsValid isVideoViewsValid
} from '../videos' } from '../videos'
import { isActivityPubUrlValid, isActivityPubVideoDurationValid, isBaseActivityValid, setValidAttributedTo } from './misc' import { isActivityPubUrlValid, isActivityPubVideoDurationValid, isBaseActivityValid, setValidAttributedTo } from './misc'
@ -32,7 +32,7 @@ function sanitizeAndCheckVideoTorrentObject (video: any) {
logger.debug('Video has invalid urls', { video }) logger.debug('Video has invalid urls', { video })
return false return false
} }
if (!setRemoteVideoTruncatedContent(video)) { if (!setRemoteVideoContent(video)) {
logger.debug('Video has invalid content', { video }) logger.debug('Video has invalid content', { video })
return false return false
} }
@ -168,7 +168,7 @@ function isRemoteStringIdentifierValid (data: any) {
} }
function isRemoteVideoContentValid (mediaType: string, content: string) { function isRemoteVideoContentValid (mediaType: string, content: string) {
return mediaType === 'text/markdown' && isVideoTruncatedDescriptionValid(content) return mediaType === 'text/markdown' && isVideoDescriptionValid(content)
} }
function setValidRemoteIcon (video: any) { function setValidRemoteIcon (video: any) {
@ -194,9 +194,9 @@ function setValidRemoteVideoUrls (video: any) {
return true return true
} }
function setRemoteVideoTruncatedContent (video: any) { function setRemoteVideoContent (video: any) {
if (video.content) { if (video.content) {
video.content = peertubeTruncate(video.content, { length: CONSTRAINTS_FIELDS.VIDEOS.TRUNCATED_DESCRIPTION.max }) video.content = peertubeTruncate(video.content, { length: CONSTRAINTS_FIELDS.VIDEOS.DESCRIPTION.max })
} }
return true return true

View File

@ -45,10 +45,6 @@ function isVideoDurationValid (value: string) {
return exists(value) && validator.isInt(value + '', VIDEOS_CONSTRAINTS_FIELDS.DURATION) return exists(value) && validator.isInt(value + '', VIDEOS_CONSTRAINTS_FIELDS.DURATION)
} }
function isVideoTruncatedDescriptionValid (value: string) {
return exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.TRUNCATED_DESCRIPTION)
}
function isVideoDescriptionValid (value: string) { function isVideoDescriptionValid (value: string) {
return value === null || (exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.DESCRIPTION)) return value === null || (exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.DESCRIPTION))
} }
@ -151,7 +147,6 @@ export {
isVideoCategoryValid, isVideoCategoryValid,
isVideoLicenceValid, isVideoLicenceValid,
isVideoLanguageValid, isVideoLanguageValid,
isVideoTruncatedDescriptionValid,
isVideoDescriptionValid, isVideoDescriptionValid,
isVideoFileInfoHashValid, isVideoFileInfoHashValid,
isVideoNameValid, isVideoNameValid,

View File

@ -103,6 +103,7 @@ function videoModelToFormattedJSON (video: MVideoFormattable, options: VideoForm
}, },
nsfw: video.nsfw, nsfw: video.nsfw,
truncatedDescription: video.getTruncatedDescription(),
description: options && options.completeDescription === true description: options && options.completeDescription === true
? video.description ? video.description
: video.getTruncatedDescription(), : video.getTruncatedDescription(),
@ -181,6 +182,7 @@ function videoModelToFormattedDetailsJSON (video: MVideoFormattableDetails): Vid
const span = tracer.startSpan('peertube.VideoModel.toFormattedDetailsJSON') const span = tracer.startSpan('peertube.VideoModel.toFormattedDetailsJSON')
const videoJSON = video.toFormattedJSON({ const videoJSON = video.toFormattedJSON({
completeDescription: true,
additionalAttributes: { additionalAttributes: {
scheduledUpdate: true, scheduledUpdate: true,
blacklistInfo: true, blacklistInfo: true,

View File

@ -14,8 +14,12 @@ describe('Test video description', function () {
let servers: PeerTubeServer[] = [] let servers: PeerTubeServer[] = []
let videoUUID = '' let videoUUID = ''
let videoId: number let videoId: number
const longDescription = 'my super description for server 1'.repeat(50) const longDescription = 'my super description for server 1'.repeat(50)
// 30 characters * 6 -> 240 characters
const truncatedDescription = 'my super description for server 1'.repeat(7) + 'my super descrip...'
before(async function () { before(async function () {
this.timeout(40000) this.timeout(40000)
@ -45,15 +49,22 @@ describe('Test video description', function () {
videoUUID = data[0].uuid videoUUID = data[0].uuid
}) })
it('Should have a truncated description on each server', async function () { it('Should have a truncated description on each server when listing videos', async function () {
for (const server of servers) {
const { data } = await server.videos.list()
const video = data.find(v => v.uuid === videoUUID)
expect(video.description).to.equal(truncatedDescription)
expect(video.truncatedDescription).to.equal(truncatedDescription)
}
})
it('Should not have a truncated description on each server when getting videos', async function () {
for (const server of servers) { for (const server of servers) {
const video = await server.videos.get({ id: videoUUID }) const video = await server.videos.get({ id: videoUUID })
// 30 characters * 6 -> 240 characters expect(video.description).to.equal(longDescription)
const truncatedDescription = 'my super description for server 1'.repeat(7) + expect(video.truncatedDescription).to.equal(truncatedDescription)
'my super descrip...'
expect(video.description).to.equal(truncatedDescription)
} }
}) })

View File

@ -20,7 +20,11 @@ export interface Video {
licence: VideoConstant<number> licence: VideoConstant<number>
language: VideoConstant<string> language: VideoConstant<string>
privacy: VideoConstant<VideoPrivacy> privacy: VideoConstant<VideoPrivacy>
// Deprecated in 5.0 in favour of truncatedDescription
description: string description: string
truncatedDescription: string
duration: number duration: number
isLocal: boolean isLocal: boolean
name: string name: string
@ -70,7 +74,9 @@ export interface Video {
} }
export interface VideoDetails extends Video { export interface VideoDetails extends Video {
// Deprecated in 5.0
descriptionPath: string descriptionPath: string
support: string support: string
channel: VideoChannel channel: VideoChannel
account: Account account: Account