Federate entire description
Introduce an explicit field truncatedDescription description in video list is deprecated description in video get will contain the entire description
This commit is contained in:
parent
44e702ded4
commit
f713f36bdf
|
@ -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>
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue