mirror of
https://github.com/Chocobozzz/PeerTube.git
synced 2024-11-10 23:48:57 +03:00
Handle playlist oembed
This commit is contained in:
parent
a75292db78
commit
6fad8e51c4
@ -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)
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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({
|
||||
|
@ -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()
|
||||
}
|
||||
|
||||
]
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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 () {
|
||||
|
@ -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')
|
||||
|
@ -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 () {
|
||||
|
Loading…
Reference in New Issue
Block a user