Handle playlist oembed

pull/3047/head
Chocobozzz 2020-08-05 15:35:58 +02:00 committed by Chocobozzz
parent a75292db78
commit 6fad8e51c4
8 changed files with 482 additions and 243 deletions

View File

@ -438,7 +438,7 @@ export class PeerTubeEmbed {
return videoInfo
})
const [ videoInfo, serverTranslations, captionsResponse, config, PeertubePlayerManagerModule ] = await Promise.all([
const [ videoInfoTmp, serverTranslations, captionsResponse, config, PeertubePlayerManagerModule ] = await Promise.all([
videoInfoPromise,
this.translationsPromise,
captionsPromise,
@ -446,6 +446,8 @@ export class PeerTubeEmbed {
this.PeertubePlayerManagerModulePromise
])
const videoInfo: VideoDetails = videoInfoTmp
const PeertubePlayerManager = PeertubePlayerManagerModule.PeertubePlayerManager
const videoCaptions = await this.buildCaptions(serverTranslations, captionsResponse)

View File

@ -1,7 +1,8 @@
import * as express from 'express'
import { EMBED_SIZE, PREVIEWS_SIZE, WEBSERVER } from '../initializers/constants'
import { EMBED_SIZE, PREVIEWS_SIZE, WEBSERVER, THUMBNAILS_SIZE } from '../initializers/constants'
import { asyncMiddleware, oembedValidator } from '../middlewares'
import { accountNameWithHostGetValidator } from '../middlewares/validators'
import { MChannelSummary } from '@server/types/models'
const servicesRouter = express.Router()
@ -23,23 +24,73 @@ export {
// ---------------------------------------------------------------------------
function generateOEmbed (req: express.Request, res: express.Response) {
if (res.locals.videoAll) return generateVideoOEmbed(req, res)
return generatePlaylistOEmbed(req, res)
}
function generatePlaylistOEmbed (req: express.Request, res: express.Response) {
const playlist = res.locals.videoPlaylistSummary
const json = buildOEmbed({
channel: playlist.VideoChannel,
title: playlist.name,
embedPath: playlist.getEmbedStaticPath(),
previewPath: playlist.getThumbnailStaticPath(),
previewSize: THUMBNAILS_SIZE,
req
})
return res.json(json)
}
function generateVideoOEmbed (req: express.Request, res: express.Response) {
const video = res.locals.videoAll
const json = buildOEmbed({
channel: video.VideoChannel,
title: video.name,
embedPath: video.getEmbedStaticPath(),
previewPath: video.getPreviewStaticPath(),
previewSize: PREVIEWS_SIZE,
req
})
return res.json(json)
}
function buildOEmbed (options: {
req: express.Request
title: string
channel: MChannelSummary
previewPath: string | null
embedPath: string
previewSize: {
height: number
width: number
}
}) {
const { req, previewSize, previewPath, title, channel, embedPath } = options
const webserverUrl = WEBSERVER.URL
const maxHeight = parseInt(req.query.maxheight, 10)
const maxWidth = parseInt(req.query.maxwidth, 10)
const embedUrl = webserverUrl + video.getEmbedStaticPath()
let thumbnailUrl = webserverUrl + video.getPreviewStaticPath()
const embedUrl = webserverUrl + embedPath
let embedWidth = EMBED_SIZE.width
let embedHeight = EMBED_SIZE.height
let thumbnailUrl = previewPath
? webserverUrl + previewPath
: undefined
if (maxHeight < embedHeight) embedHeight = maxHeight
if (maxWidth < embedWidth) embedWidth = maxWidth
// Our thumbnail is too big for the consumer
if (
(maxHeight !== undefined && maxHeight < PREVIEWS_SIZE.height) ||
(maxWidth !== undefined && maxWidth < PREVIEWS_SIZE.width)
(maxHeight !== undefined && maxHeight < previewSize.height) ||
(maxWidth !== undefined && maxWidth < previewSize.width)
) {
thumbnailUrl = undefined
}
@ -53,20 +104,20 @@ function generateOEmbed (req: express.Request, res: express.Response) {
html,
width: embedWidth,
height: embedHeight,
title: video.name,
author_name: video.VideoChannel.Account.name,
author_url: video.VideoChannel.Account.Actor.url,
title: title,
author_name: channel.name,
author_url: channel.Actor.url,
provider_name: 'PeerTube',
provider_url: webserverUrl
}
if (thumbnailUrl !== undefined) {
json.thumbnail_url = thumbnailUrl
json.thumbnail_width = PREVIEWS_SIZE.width
json.thumbnail_height = PREVIEWS_SIZE.height
json.thumbnail_width = previewSize.width
json.thumbnail_height = previewSize.height
}
return res.json(json)
return json
}
function redirectToAccountUrl (req: express.Request, res: express.Response, next: express.NextFunction) {

View File

@ -23,6 +23,33 @@ import { CONFIG } from '../initializers/config'
import { logger } from '../helpers/logger'
import { MAccountActor, MChannelActor } from '../types/models'
type Tags = {
ogType: string
twitterCard: string
schemaType: string
list?: {
numberOfItems: number
}
title: string
url: string
description: string
embed?: {
url: string
createdAt: string
duration?: string
views?: number
}
image: {
url: string
width?: number
height?: number
}
}
export class ClientHtml {
private static htmlCache: { [path: string]: string } = {}
@ -118,15 +145,20 @@ export class ClientHtml {
url: videoPlaylist.getThumbnailUrl()
}
const embed = {
url: WEBSERVER.URL + videoPlaylist.getEmbedStaticPath(),
createdAt: videoPlaylist.createdAt.toISOString()
}
const list = {
numberOfItems: videoPlaylist.get('videosLength')
numberOfItems: videoPlaylist.get('videosLength') as number
}
const ogType = 'video'
const twitterCard = 'summary'
const twitterCard = CONFIG.SERVICES.TWITTER.WHITELISTED ? 'player' : 'summary'
const schemaType = 'ItemList'
customHtml = ClientHtml.addTags(customHtml, { url, title, description, image, list, ogType, twitterCard, schemaType })
customHtml = ClientHtml.addTags(customHtml, { url, embed, title, description, image, list, ogType, twitterCard, schemaType })
return customHtml
}
@ -268,7 +300,7 @@ export class ClientHtml {
return htmlStringPage.replace('</head>', linkTag + '</head>')
}
private static generateOpenGraphMetaTags (tags) {
private static generateOpenGraphMetaTags (tags: Tags) {
const metaTags = {
'og:type': tags.ogType,
'og:title': tags.title,
@ -294,7 +326,7 @@ export class ClientHtml {
return metaTags
}
private static generateStandardMetaTags (tags) {
private static generateStandardMetaTags (tags: Tags) {
return {
name: tags.title,
description: tags.description,
@ -302,7 +334,7 @@ export class ClientHtml {
}
}
private static generateTwitterCardMetaTags (tags) {
private static generateTwitterCardMetaTags (tags: Tags) {
const metaTags = {
'twitter:card': tags.twitterCard,
'twitter:site': CONFIG.SERVICES.TWITTER.USERNAME,
@ -319,7 +351,7 @@ export class ClientHtml {
return metaTags
}
private static generateSchemaTags (tags) {
private static generateSchemaTags (tags: Tags) {
const schema = {
'@context': 'http://schema.org',
'@type': tags.schemaType,
@ -337,8 +369,10 @@ export class ClientHtml {
if (tags.embed) {
schema['embedUrl'] = tags.embed.url
schema['uploadDate'] = tags.embed.createdAt
schema['duration'] = tags.embed.duration
schema['iterationCount'] = tags.embed.views
if (tags.embed.duration) schema['duration'] = tags.embed.duration
if (tags.embed.views) schema['iterationCount'] = tags.embed.views
schema['thumbnailUrl'] = tags.image.url
schema['contentUrl'] = tags.url
}
@ -346,7 +380,7 @@ export class ClientHtml {
return schema
}
private static addTags (htmlStringPage: string, tagsValues: any) {
private static addTags (htmlStringPage: string, tagsValues: Tags) {
const openGraphMetaTags = this.generateOpenGraphMetaTags(tagsValues)
const standardMetaTags = this.generateStandardMetaTags(tagsValues)
const twitterCardMetaTags = this.generateTwitterCardMetaTags(tagsValues)
@ -354,7 +388,7 @@ export class ClientHtml {
const { url, title, embed } = tagsValues
const oembedLinkTags = []
const oembedLinkTags: { type: string, href: string, title: string }[] = []
if (embed) {
oembedLinkTags.push({

View File

@ -1,15 +1,19 @@
import * as express from 'express'
import { query } from 'express-validator'
import { join } from 'path'
import { fetchVideo } from '@server/helpers/video'
import { VideoPlaylistModel } from '@server/models/video/video-playlist'
import { VideoPlaylistPrivacy, VideoPrivacy } from '@shared/models'
import { isTestInstance } from '../../helpers/core-utils'
import { isIdOrUUIDValid } from '../../helpers/custom-validators/misc'
import { logger } from '../../helpers/logger'
import { areValidationErrors } from './utils'
import { WEBSERVER } from '../../initializers/constants'
import { doesVideoExist } from '../../helpers/middlewares'
import { areValidationErrors } from './utils'
const urlShouldStartWith = WEBSERVER.SCHEME + '://' + join(WEBSERVER.HOST, 'videos', 'watch') + '/'
const videoWatchRegex = new RegExp('([^/]+)$')
const startVideoPlaylistsURL = WEBSERVER.SCHEME + '://' + join(WEBSERVER.HOST, 'videos', 'watch', 'playlist') + '/'
const startVideosURL = WEBSERVER.SCHEME + '://' + join(WEBSERVER.HOST, 'videos', 'watch') + '/'
const watchRegex = new RegExp('([^/]+)$')
const isURLOptions = {
require_host: true,
require_tld: true
@ -33,32 +37,63 @@ const oembedValidator = [
if (req.query.format !== undefined && req.query.format !== 'json') {
return res.status(501)
.json({ error: 'Requested format is not implemented on server.' })
.end()
.json({ error: 'Requested format is not implemented on server.' })
}
const url = req.query.url as string
const startIsOk = url.startsWith(urlShouldStartWith)
const matches = videoWatchRegex.exec(url)
const isPlaylist = url.startsWith(startVideoPlaylistsURL)
const isVideo = isPlaylist ? false : url.startsWith(startVideosURL)
const startIsOk = isVideo || isPlaylist
const matches = watchRegex.exec(url)
if (startIsOk === false || matches === null) {
return res.status(400)
.json({ error: 'Invalid url.' })
.end()
.json({ error: 'Invalid url.' })
}
const videoId = matches[1]
if (isIdOrUUIDValid(videoId) === false) {
const elementId = matches[1]
if (isIdOrUUIDValid(elementId) === false) {
return res.status(400)
.json({ error: 'Invalid video id.' })
.end()
.json({ error: 'Invalid video or playlist id.' })
}
if (!await doesVideoExist(videoId, res)) return
if (isVideo) {
const video = await fetchVideo(elementId, 'all')
if (!video) {
return res.status(404)
.json({ error: 'Video not found' })
}
if (video.privacy !== VideoPrivacy.PUBLIC) {
return res.status(403)
.json({ error: 'Video is not public' })
}
res.locals.videoAll = video
return next()
}
// Is playlist
const videoPlaylist = await VideoPlaylistModel.loadWithAccountAndChannelSummary(elementId, undefined)
if (!videoPlaylist) {
return res.status(404)
.json({ error: 'Video playlist not found' })
}
if (videoPlaylist.privacy !== VideoPlaylistPrivacy.PUBLIC) {
return res.status(403)
.json({ error: 'Playlist is not public' })
}
res.locals.videoPlaylistSummary = videoPlaylist
return next()
}
]
// ---------------------------------------------------------------------------

View File

@ -494,6 +494,10 @@ export class VideoPlaylistModel extends Model<VideoPlaylistModel> {
return WEBSERVER.URL + '/videos/watch/playlist/' + this.uuid
}
getEmbedStaticPath () {
return '/video-playlists/embed/' + this.uuid
}
setAsRefreshed () {
this.changed('updatedAt', true)

View File

@ -8,11 +8,15 @@ import {
makeGetRequest,
ServerInfo,
setAccessTokensToServers,
uploadVideo
uploadVideo,
createVideoPlaylist,
setDefaultVideoChannel
} from '../../../../shared/extra-utils'
import { VideoPlaylistPrivacy } from '@shared/models'
describe('Test services API validators', function () {
let server: ServerInfo
let playlistUUID: string
// ---------------------------------------------------------------
@ -21,9 +25,26 @@ describe('Test services API validators', function () {
server = await flushAndRunServer(1)
await setAccessTokensToServers([ server ])
await setDefaultVideoChannel([ server ])
const res = await uploadVideo(server.url, server.accessToken, { name: 'my super name' })
server.video = res.body.video
{
const res = await uploadVideo(server.url, server.accessToken, { name: 'my super name' })
server.video = res.body.video
}
{
const res = await createVideoPlaylist({
url: server.url,
token: server.accessToken,
playlistAttrs: {
displayName: 'super playlist',
privacy: VideoPlaylistPrivacy.PUBLIC,
videoChannelId: server.videoChannel.id
}
})
playlistUUID = res.body.videoPlaylist.uuid
}
})
describe('Test oEmbed API validators', function () {
@ -38,12 +59,12 @@ describe('Test services API validators', function () {
await checkParamEmbed(server, embedUrl)
})
it('Should fail with an invalid video id', async function () {
it('Should fail with an invalid element id', async function () {
const embedUrl = `http://localhost:${server.port}/videos/watch/blabla`
await checkParamEmbed(server, embedUrl)
})
it('Should fail with an unknown video', async function () {
it('Should fail with an unknown element', async function () {
const embedUrl = `http://localhost:${server.port}/videos/watch/88fc0165-d1f0-4a35-a51a-3b47f668689c`
await checkParamEmbed(server, embedUrl, 404)
})
@ -78,7 +99,7 @@ describe('Test services API validators', function () {
await checkParamEmbed(server, embedUrl, 501, { format: 'xml' })
})
it('Should succeed with the correct params', async function () {
it('Should succeed with the correct params with a video', async function () {
const embedUrl = `http://localhost:${server.port}/videos/watch/${server.video.uuid}`
const query = {
format: 'json',
@ -88,6 +109,17 @@ describe('Test services API validators', function () {
await checkParamEmbed(server, embedUrl, 200, query)
})
it('Should succeed with the correct params with a playlist', async function () {
const embedUrl = `http://localhost:${server.port}/videos/watch/playlist/${playlistUUID}`
const query = {
format: 'json',
maxheight: 400,
maxwidth: 400
}
await checkParamEmbed(server, embedUrl, 200, query)
})
})
after(async function () {

View File

@ -1,14 +1,25 @@
/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
import * as chai from 'chai'
import 'mocha'
import { getOEmbed, getVideosList, ServerInfo, setAccessTokensToServers, uploadVideo } from '../../../../shared/extra-utils/index'
import * as chai from 'chai'
import {
getOEmbed,
getVideosList,
ServerInfo,
setAccessTokensToServers,
setDefaultVideoChannel,
uploadVideo,
createVideoPlaylist,
addVideoInPlaylist
} from '../../../../shared/extra-utils'
import { cleanupTests, flushAndRunServer } from '../../../../shared/extra-utils/server/servers'
import { VideoPlaylistPrivacy } from '@shared/models'
const expect = chai.expect
describe('Test services', function () {
let server: ServerInfo = null
let playlistUUID: string
before(async function () {
this.timeout(30000)
@ -16,17 +27,43 @@ describe('Test services', function () {
server = await flushAndRunServer(1)
await setAccessTokensToServers([ server ])
await setDefaultVideoChannel([ server ])
const videoAttributes = {
name: 'my super name'
{
const videoAttributes = {
name: 'my super name'
}
await uploadVideo(server.url, server.accessToken, videoAttributes)
const res = await getVideosList(server.url)
server.video = res.body.data[0]
}
await uploadVideo(server.url, server.accessToken, videoAttributes)
const res = await getVideosList(server.url)
server.video = res.body.data[0]
{
const res = await createVideoPlaylist({
url: server.url,
token: server.accessToken,
playlistAttrs: {
displayName: 'The Life and Times of Scrooge McDuck',
privacy: VideoPlaylistPrivacy.PUBLIC,
videoChannelId: server.videoChannel.id
}
})
playlistUUID = res.body.videoPlaylist.uuid
await addVideoInPlaylist({
url: server.url,
token: server.accessToken,
playlistId: res.body.videoPlaylist.id,
elementAttrs: {
videoId: server.video.id
}
})
}
})
it('Should have a valid oEmbed response', async function () {
it('Should have a valid oEmbed video response', async function () {
const oembedUrl = 'http://localhost:' + server.port + '/videos/watch/' + server.video.uuid
const res = await getOEmbed(server.url, oembedUrl)
@ -37,7 +74,7 @@ describe('Test services', function () {
expect(res.body.html).to.equal(expectedHtml)
expect(res.body.title).to.equal(server.video.name)
expect(res.body.author_name).to.equal(server.video.account.name)
expect(res.body.author_name).to.equal(server.videoChannel.displayName)
expect(res.body.width).to.equal(560)
expect(res.body.height).to.equal(315)
expect(res.body.thumbnail_url).to.equal(expectedThumbnailUrl)
@ -45,6 +82,24 @@ describe('Test services', function () {
expect(res.body.thumbnail_height).to.equal(480)
})
it('Should have a valid playlist oEmbed response', async function () {
const oembedUrl = 'http://localhost:' + server.port + '/videos/watch/playlist/' + playlistUUID
const res = await getOEmbed(server.url, oembedUrl)
const expectedHtml = '<iframe width="560" height="315" sandbox="allow-same-origin allow-scripts" ' +
`src="http://localhost:${server.port}/video-playlists/embed/${playlistUUID}" ` +
'frameborder="0" allowfullscreen></iframe>'
expect(res.body.html).to.equal(expectedHtml)
expect(res.body.title).to.equal('The Life and Times of Scrooge McDuck')
expect(res.body.author_name).to.equal(server.videoChannel.displayName)
expect(res.body.width).to.equal(560)
expect(res.body.height).to.equal(315)
expect(res.body.thumbnail_url).exist
expect(res.body.thumbnail_width).to.equal(223)
expect(res.body.thumbnail_height).to.equal(122)
})
it('Should have a valid oEmbed response with small max height query', async function () {
const oembedUrl = 'http://localhost:' + server.port + '/videos/watch/' + server.video.uuid
const format = 'json'
@ -58,7 +113,7 @@ describe('Test services', function () {
expect(res.body.html).to.equal(expectedHtml)
expect(res.body.title).to.equal(server.video.name)
expect(res.body.author_name).to.equal(server.video.account.name)
expect(res.body.author_name).to.equal(server.videoChannel.displayName)
expect(res.body.height).to.equal(50)
expect(res.body.width).to.equal(50)
expect(res.body).to.not.have.property('thumbnail_url')

View File

@ -94,204 +94,230 @@ describe('Test a client controllers', function () {
account = resAccountRequest.body
})
it('Should have valid Open Graph tags on the watch page with video id', async function () {
const res = await request(server.url)
.get('/videos/watch/' + server.video.id)
.set('Accept', 'text/html')
.expect(200)
describe('oEmbed', function () {
it('Should have valid oEmbed discovery tags for videos', async function () {
const path = '/videos/watch/' + server.video.uuid
const res = await request(server.url)
.get(path)
.set('Accept', 'text/html')
.expect(200)
expect(res.text).to.contain(`<meta property="og:title" content="${videoName}" />`)
expect(res.text).to.contain(`<meta property="og:description" content="${videoDescription}" />`)
expect(res.text).to.contain('<meta property="og:type" content="video" />')
expect(res.text).to.contain(`<meta property="og:url" content="${server.url}/videos/watch/${server.video.uuid}" />`)
})
const port = server.port
it('Should have valid Open Graph tags on the watch page with video uuid', async function () {
const res = await request(server.url)
.get('/videos/watch/' + server.video.uuid)
.set('Accept', 'text/html')
.expect(200)
const expectedLink = '<link rel="alternate" type="application/json+oembed" href="http://localhost:' + port + '/services/oembed?' +
`url=http%3A%2F%2Flocalhost%3A${port}%2Fvideos%2Fwatch%2F${server.video.uuid}" ` +
`title="${server.video.name}" />`
expect(res.text).to.contain(`<meta property="og:title" content="${videoName}" />`)
expect(res.text).to.contain(`<meta property="og:description" content="${videoDescription}" />`)
expect(res.text).to.contain('<meta property="og:type" content="video" />')
expect(res.text).to.contain(`<meta property="og:url" content="${server.url}/videos/watch/${server.video.uuid}" />`)
})
it('Should have valid Open Graph tags on the watch playlist page', async function () {
const res = await request(server.url)
.get('/videos/watch/playlist/' + playlistUUID)
.set('Accept', 'text/html')
.expect(200)
expect(res.text).to.contain(`<meta property="og:title" content="${playlistName}" />`)
expect(res.text).to.contain(`<meta property="og:description" content="${playlistDescription}" />`)
expect(res.text).to.contain('<meta property="og:type" content="video" />')
expect(res.text).to.contain(`<meta property="og:url" content="${server.url}/videos/watch/playlist/${playlistUUID}" />`)
})
it('Should have valid Open Graph tags on the account page', async function () {
const res = await request(server.url)
.get('/accounts/' + server.user.username)
.set('Accept', 'text/html')
.expect(200)
expect(res.text).to.contain(`<meta property="og:title" content="${account.displayName}" />`)
expect(res.text).to.contain(`<meta property="og:description" content="${account.description}" />`)
expect(res.text).to.contain('<meta property="og:type" content="website" />')
expect(res.text).to.contain(`<meta property="og:url" content="${server.url}/accounts/${server.user.username}" />`)
})
it('Should have valid Open Graph tags on the channel page', async function () {
const res = await request(server.url)
.get('/video-channels/' + server.videoChannel.name)
.set('Accept', 'text/html')
.expect(200)
expect(res.text).to.contain(`<meta property="og:title" content="${server.videoChannel.displayName}" />`)
expect(res.text).to.contain(`<meta property="og:description" content="${channelDescription}" />`)
expect(res.text).to.contain('<meta property="og:type" content="website" />')
expect(res.text).to.contain(`<meta property="og:url" content="${server.url}/video-channels/${server.videoChannel.name}" />`)
})
it('Should have valid oEmbed discovery tags', async function () {
const path = '/videos/watch/' + server.video.uuid
const res = await request(server.url)
.get(path)
.set('Accept', 'text/html')
.expect(200)
const port = server.port
const expectedLink = '<link rel="alternate" type="application/json+oembed" href="http://localhost:' + port + '/services/oembed?' +
`url=http%3A%2F%2Flocalhost%3A${port}%2Fvideos%2Fwatch%2F${server.video.uuid}" ` +
`title="${server.video.name}" />`
expect(res.text).to.contain(expectedLink)
})
it('Should have valid twitter card on the watch video page', async function () {
const res = await request(server.url)
.get('/videos/watch/' + server.video.uuid)
.set('Accept', 'text/html')
.expect(200)
expect(res.text).to.contain('<meta property="twitter:card" content="summary_large_image" />')
expect(res.text).to.contain('<meta property="twitter:site" content="@Chocobozzz" />')
expect(res.text).to.contain(`<meta property="twitter:title" content="${videoName}" />`)
expect(res.text).to.contain(`<meta property="twitter:description" content="${videoDescription}" />`)
})
it('Should have valid twitter card on the watch playlist page', async function () {
const res = await request(server.url)
.get('/videos/watch/playlist/' + playlistUUID)
.set('Accept', 'text/html')
.expect(200)
expect(res.text).to.contain('<meta property="twitter:card" content="summary" />')
expect(res.text).to.contain('<meta property="twitter:site" content="@Chocobozzz" />')
expect(res.text).to.contain(`<meta property="twitter:title" content="${playlistName}" />`)
expect(res.text).to.contain(`<meta property="twitter:description" content="${playlistDescription}" />`)
})
it('Should have valid twitter card on the account page', async function () {
const res = await request(server.url)
.get('/accounts/' + account.name)
.set('Accept', 'text/html')
.expect(200)
expect(res.text).to.contain('<meta property="twitter:card" content="summary" />')
expect(res.text).to.contain('<meta property="twitter:site" content="@Chocobozzz" />')
expect(res.text).to.contain(`<meta property="twitter:title" content="${account.name}" />`)
expect(res.text).to.contain(`<meta property="twitter:description" content="${account.description}" />`)
})
it('Should have valid twitter card on the channel page', async function () {
const res = await request(server.url)
.get('/video-channels/' + server.videoChannel.name)
.set('Accept', 'text/html')
.expect(200)
expect(res.text).to.contain('<meta property="twitter:card" content="summary" />')
expect(res.text).to.contain('<meta property="twitter:site" content="@Chocobozzz" />')
expect(res.text).to.contain(`<meta property="twitter:title" content="${server.videoChannel.displayName}" />`)
expect(res.text).to.contain(`<meta property="twitter:description" content="${channelDescription}" />`)
})
it('Should have valid twitter card if Twitter is whitelisted', async function () {
const res1 = await getCustomConfig(server.url, server.accessToken)
const config = res1.body
config.services.twitter = {
username: '@Kuja',
whitelisted: true
}
await updateCustomConfig(server.url, server.accessToken, config)
const resVideoRequest = await request(server.url)
.get('/videos/watch/' + server.video.uuid)
.set('Accept', 'text/html')
.expect(200)
expect(resVideoRequest.text).to.contain('<meta property="twitter:card" content="player" />')
expect(resVideoRequest.text).to.contain('<meta property="twitter:site" content="@Kuja" />')
const resVideoPlaylistRequest = await request(server.url)
.get('/videos/watch/playlist/' + playlistUUID)
.set('Accept', 'text/html')
.expect(200)
expect(resVideoPlaylistRequest.text).to.contain('<meta property="twitter:card" content="summary" />')
expect(resVideoPlaylistRequest.text).to.contain('<meta property="twitter:site" content="@Kuja" />')
const resAccountRequest = await request(server.url)
.get('/accounts/' + account.name)
.set('Accept', 'text/html')
.expect(200)
expect(resAccountRequest.text).to.contain('<meta property="twitter:card" content="summary" />')
expect(resAccountRequest.text).to.contain('<meta property="twitter:site" content="@Kuja" />')
const resChannelRequest = await request(server.url)
.get('/video-channels/' + server.videoChannel.name)
.set('Accept', 'text/html')
.expect(200)
expect(resChannelRequest.text).to.contain('<meta property="twitter:card" content="summary" />')
expect(resChannelRequest.text).to.contain('<meta property="twitter:site" content="@Kuja" />')
})
it('Should have valid index html tags (title, description...)', async function () {
const res = await makeHTMLRequest(server.url, '/videos/trending')
const description = 'PeerTube, an ActivityPub-federated video streaming platform using P2P directly in your web browser.'
checkIndexTags(res.text, 'PeerTube', description, '')
})
it('Should update the customized configuration and have the correct index html tags', async function () {
await updateCustomSubConfig(server.url, server.accessToken, {
instance: {
name: 'PeerTube updated',
shortDescription: 'my short description',
description: 'my super description',
terms: 'my super terms',
defaultClientRoute: '/videos/recently-added',
defaultNSFWPolicy: 'blur',
customizations: {
javascript: 'alert("coucou")',
css: 'body { background-color: red; }'
}
}
expect(res.text).to.contain(expectedLink)
})
const res = await makeHTMLRequest(server.url, '/videos/trending')
it('Should have valid oEmbed discovery tags for a playlist', async function () {
const res = await request(server.url)
.get('/videos/watch/playlist/' + playlistUUID)
.set('Accept', 'text/html')
.expect(200)
checkIndexTags(res.text, 'PeerTube updated', 'my short description', 'body { background-color: red; }')
const port = server.port
const expectedLink = '<link rel="alternate" type="application/json+oembed" href="http://localhost:' + port + '/services/oembed?' +
`url=http%3A%2F%2Flocalhost%3A${port}%2Fvideos%2Fwatch%2Fplaylist%2F${playlistUUID}" ` +
`title="${playlistName}" />`
expect(res.text).to.contain(expectedLink)
})
})
it('Should have valid index html updated tags (title, description...)', async function () {
const res = await makeHTMLRequest(server.url, '/videos/trending')
describe('Open Graph', function () {
checkIndexTags(res.text, 'PeerTube updated', 'my short description', 'body { background-color: red; }')
it('Should have valid Open Graph tags on the account page', async function () {
const res = await request(server.url)
.get('/accounts/' + server.user.username)
.set('Accept', 'text/html')
.expect(200)
expect(res.text).to.contain(`<meta property="og:title" content="${account.displayName}" />`)
expect(res.text).to.contain(`<meta property="og:description" content="${account.description}" />`)
expect(res.text).to.contain('<meta property="og:type" content="website" />')
expect(res.text).to.contain(`<meta property="og:url" content="${server.url}/accounts/${server.user.username}" />`)
})
it('Should have valid Open Graph tags on the channel page', async function () {
const res = await request(server.url)
.get('/video-channels/' + server.videoChannel.name)
.set('Accept', 'text/html')
.expect(200)
expect(res.text).to.contain(`<meta property="og:title" content="${server.videoChannel.displayName}" />`)
expect(res.text).to.contain(`<meta property="og:description" content="${channelDescription}" />`)
expect(res.text).to.contain('<meta property="og:type" content="website" />')
expect(res.text).to.contain(`<meta property="og:url" content="${server.url}/video-channels/${server.videoChannel.name}" />`)
})
it('Should have valid Open Graph tags on the watch page with video id', async function () {
const res = await request(server.url)
.get('/videos/watch/' + server.video.id)
.set('Accept', 'text/html')
.expect(200)
expect(res.text).to.contain(`<meta property="og:title" content="${videoName}" />`)
expect(res.text).to.contain(`<meta property="og:description" content="${videoDescription}" />`)
expect(res.text).to.contain('<meta property="og:type" content="video" />')
expect(res.text).to.contain(`<meta property="og:url" content="${server.url}/videos/watch/${server.video.uuid}" />`)
})
it('Should have valid Open Graph tags on the watch page with video uuid', async function () {
const res = await request(server.url)
.get('/videos/watch/' + server.video.uuid)
.set('Accept', 'text/html')
.expect(200)
expect(res.text).to.contain(`<meta property="og:title" content="${videoName}" />`)
expect(res.text).to.contain(`<meta property="og:description" content="${videoDescription}" />`)
expect(res.text).to.contain('<meta property="og:type" content="video" />')
expect(res.text).to.contain(`<meta property="og:url" content="${server.url}/videos/watch/${server.video.uuid}" />`)
})
it('Should have valid Open Graph tags on the watch playlist page', async function () {
const res = await request(server.url)
.get('/videos/watch/playlist/' + playlistUUID)
.set('Accept', 'text/html')
.expect(200)
expect(res.text).to.contain(`<meta property="og:title" content="${playlistName}" />`)
expect(res.text).to.contain(`<meta property="og:description" content="${playlistDescription}" />`)
expect(res.text).to.contain('<meta property="og:type" content="video" />')
expect(res.text).to.contain(`<meta property="og:url" content="${server.url}/videos/watch/playlist/${playlistUUID}" />`)
})
})
describe('Twitter card', async function () {
it('Should have valid twitter card on the watch video page', async function () {
const res = await request(server.url)
.get('/videos/watch/' + server.video.uuid)
.set('Accept', 'text/html')
.expect(200)
expect(res.text).to.contain('<meta property="twitter:card" content="summary_large_image" />')
expect(res.text).to.contain('<meta property="twitter:site" content="@Chocobozzz" />')
expect(res.text).to.contain(`<meta property="twitter:title" content="${videoName}" />`)
expect(res.text).to.contain(`<meta property="twitter:description" content="${videoDescription}" />`)
})
it('Should have valid twitter card on the watch playlist page', async function () {
const res = await request(server.url)
.get('/videos/watch/playlist/' + playlistUUID)
.set('Accept', 'text/html')
.expect(200)
expect(res.text).to.contain('<meta property="twitter:card" content="summary" />')
expect(res.text).to.contain('<meta property="twitter:site" content="@Chocobozzz" />')
expect(res.text).to.contain(`<meta property="twitter:title" content="${playlistName}" />`)
expect(res.text).to.contain(`<meta property="twitter:description" content="${playlistDescription}" />`)
})
it('Should have valid twitter card on the account page', async function () {
const res = await request(server.url)
.get('/accounts/' + account.name)
.set('Accept', 'text/html')
.expect(200)
expect(res.text).to.contain('<meta property="twitter:card" content="summary" />')
expect(res.text).to.contain('<meta property="twitter:site" content="@Chocobozzz" />')
expect(res.text).to.contain(`<meta property="twitter:title" content="${account.name}" />`)
expect(res.text).to.contain(`<meta property="twitter:description" content="${account.description}" />`)
})
it('Should have valid twitter card on the channel page', async function () {
const res = await request(server.url)
.get('/video-channels/' + server.videoChannel.name)
.set('Accept', 'text/html')
.expect(200)
expect(res.text).to.contain('<meta property="twitter:card" content="summary" />')
expect(res.text).to.contain('<meta property="twitter:site" content="@Chocobozzz" />')
expect(res.text).to.contain(`<meta property="twitter:title" content="${server.videoChannel.displayName}" />`)
expect(res.text).to.contain(`<meta property="twitter:description" content="${channelDescription}" />`)
})
it('Should have valid twitter card if Twitter is whitelisted', async function () {
const res1 = await getCustomConfig(server.url, server.accessToken)
const config = res1.body
config.services.twitter = {
username: '@Kuja',
whitelisted: true
}
await updateCustomConfig(server.url, server.accessToken, config)
const resVideoRequest = await request(server.url)
.get('/videos/watch/' + server.video.uuid)
.set('Accept', 'text/html')
.expect(200)
expect(resVideoRequest.text).to.contain('<meta property="twitter:card" content="player" />')
expect(resVideoRequest.text).to.contain('<meta property="twitter:site" content="@Kuja" />')
const resVideoPlaylistRequest = await request(server.url)
.get('/videos/watch/playlist/' + playlistUUID)
.set('Accept', 'text/html')
.expect(200)
expect(resVideoPlaylistRequest.text).to.contain('<meta property="twitter:card" content="player" />')
expect(resVideoPlaylistRequest.text).to.contain('<meta property="twitter:site" content="@Kuja" />')
const resAccountRequest = await request(server.url)
.get('/accounts/' + account.name)
.set('Accept', 'text/html')
.expect(200)
expect(resAccountRequest.text).to.contain('<meta property="twitter:card" content="summary" />')
expect(resAccountRequest.text).to.contain('<meta property="twitter:site" content="@Kuja" />')
const resChannelRequest = await request(server.url)
.get('/video-channels/' + server.videoChannel.name)
.set('Accept', 'text/html')
.expect(200)
expect(resChannelRequest.text).to.contain('<meta property="twitter:card" content="summary" />')
expect(resChannelRequest.text).to.contain('<meta property="twitter:site" content="@Kuja" />')
})
})
describe('Index HTML', function () {
it('Should have valid index html tags (title, description...)', async function () {
const res = await makeHTMLRequest(server.url, '/videos/trending')
const description = 'PeerTube, an ActivityPub-federated video streaming platform using P2P directly in your web browser.'
checkIndexTags(res.text, 'PeerTube', description, '')
})
it('Should update the customized configuration and have the correct index html tags', async function () {
await updateCustomSubConfig(server.url, server.accessToken, {
instance: {
name: 'PeerTube updated',
shortDescription: 'my short description',
description: 'my super description',
terms: 'my super terms',
defaultClientRoute: '/videos/recently-added',
defaultNSFWPolicy: 'blur',
customizations: {
javascript: 'alert("coucou")',
css: 'body { background-color: red; }'
}
}
})
const res = await makeHTMLRequest(server.url, '/videos/trending')
checkIndexTags(res.text, 'PeerTube updated', 'my short description', 'body { background-color: red; }')
})
it('Should have valid index html updated tags (title, description...)', async function () {
const res = await makeHTMLRequest(server.url, '/videos/trending')
checkIndexTags(res.text, 'PeerTube updated', 'my short description', 'body { background-color: red; }')
})
})
after(async function () {