diff --git a/client/src/standalone/videos/embed.ts b/client/src/standalone/videos/embed.ts index a7fb087b1..8d1720f75 100644 --- a/client/src/standalone/videos/embed.ts +++ b/client/src/standalone/videos/embed.ts @@ -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) diff --git a/server/controllers/services.ts b/server/controllers/services.ts index ec057235f..d0217c30a 100644 --- a/server/controllers/services.ts +++ b/server/controllers/services.ts @@ -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) { diff --git a/server/lib/client-html.ts b/server/lib/client-html.ts index d8ae73b5d..85fced10d 100644 --- a/server/lib/client-html.ts +++ b/server/lib/client-html.ts @@ -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('', linkTag + '') } - 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({ diff --git a/server/middlewares/validators/oembed.ts b/server/middlewares/validators/oembed.ts index ab4dbb4d1..c9f9ea0c0 100644 --- a/server/middlewares/validators/oembed.ts +++ b/server/middlewares/validators/oembed.ts @@ -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() } + ] // --------------------------------------------------------------------------- diff --git a/server/models/video/video-playlist.ts b/server/models/video/video-playlist.ts index b38cf9c6a..f935bf4f0 100644 --- a/server/models/video/video-playlist.ts +++ b/server/models/video/video-playlist.ts @@ -494,6 +494,10 @@ export class VideoPlaylistModel extends Model { return WEBSERVER.URL + '/videos/watch/playlist/' + this.uuid } + getEmbedStaticPath () { + return '/video-playlists/embed/' + this.uuid + } + setAsRefreshed () { this.changed('updatedAt', true) diff --git a/server/tests/api/check-params/services.ts b/server/tests/api/check-params/services.ts index 457adfaab..e57edd9e4 100644 --- a/server/tests/api/check-params/services.ts +++ b/server/tests/api/check-params/services.ts @@ -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 () { diff --git a/server/tests/api/videos/services.ts b/server/tests/api/videos/services.ts index 5505a845a..897f37c04 100644 --- a/server/tests/api/videos/services.ts +++ b/server/tests/api/videos/services.ts @@ -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 = '' + + 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') diff --git a/server/tests/client.ts b/server/tests/client.ts index f55859b6f..96821eb6f 100644 --- a/server/tests/client.ts +++ b/server/tests/client.ts @@ -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(``) - expect(res.text).to.contain(``) - expect(res.text).to.contain('') - expect(res.text).to.contain(``) - }) + 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 = '` - expect(res.text).to.contain(``) - expect(res.text).to.contain(``) - expect(res.text).to.contain('') - expect(res.text).to.contain(``) - }) - - 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(``) - expect(res.text).to.contain(``) - expect(res.text).to.contain('') - expect(res.text).to.contain(``) - }) - - 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(``) - expect(res.text).to.contain(``) - expect(res.text).to.contain('') - expect(res.text).to.contain(``) - }) - - 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(``) - expect(res.text).to.contain(``) - expect(res.text).to.contain('') - expect(res.text).to.contain(``) - }) - - 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 = '` - - 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('') - expect(res.text).to.contain('') - expect(res.text).to.contain(``) - expect(res.text).to.contain(``) - }) - - 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('') - expect(res.text).to.contain('') - expect(res.text).to.contain(``) - expect(res.text).to.contain(``) - }) - - 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('') - expect(res.text).to.contain('') - expect(res.text).to.contain(``) - expect(res.text).to.contain(``) - }) - - 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('') - expect(res.text).to.contain('') - expect(res.text).to.contain(``) - expect(res.text).to.contain(``) - }) - - 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('') - expect(resVideoRequest.text).to.contain('') - - const resVideoPlaylistRequest = await request(server.url) - .get('/videos/watch/playlist/' + playlistUUID) - .set('Accept', 'text/html') - .expect(200) - - expect(resVideoPlaylistRequest.text).to.contain('') - expect(resVideoPlaylistRequest.text).to.contain('') - - const resAccountRequest = await request(server.url) - .get('/accounts/' + account.name) - .set('Accept', 'text/html') - .expect(200) - - expect(resAccountRequest.text).to.contain('') - expect(resAccountRequest.text).to.contain('') - - const resChannelRequest = await request(server.url) - .get('/video-channels/' + server.videoChannel.name) - .set('Accept', 'text/html') - .expect(200) - - expect(resChannelRequest.text).to.contain('') - expect(resChannelRequest.text).to.contain('') - }) - - 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 = '` + + 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(``) + expect(res.text).to.contain(``) + expect(res.text).to.contain('') + expect(res.text).to.contain(``) + }) + + 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(``) + expect(res.text).to.contain(``) + expect(res.text).to.contain('') + expect(res.text).to.contain(``) + }) + + 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(``) + expect(res.text).to.contain(``) + expect(res.text).to.contain('') + expect(res.text).to.contain(``) + }) + + 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(``) + expect(res.text).to.contain(``) + expect(res.text).to.contain('') + expect(res.text).to.contain(``) + }) + + 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(``) + expect(res.text).to.contain(``) + expect(res.text).to.contain('') + expect(res.text).to.contain(``) + }) + }) + + 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('') + expect(res.text).to.contain('') + expect(res.text).to.contain(``) + expect(res.text).to.contain(``) + }) + + 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('') + expect(res.text).to.contain('') + expect(res.text).to.contain(``) + expect(res.text).to.contain(``) + }) + + 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('') + expect(res.text).to.contain('') + expect(res.text).to.contain(``) + expect(res.text).to.contain(``) + }) + + 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('') + expect(res.text).to.contain('') + expect(res.text).to.contain(``) + expect(res.text).to.contain(``) + }) + + 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('') + expect(resVideoRequest.text).to.contain('') + + const resVideoPlaylistRequest = await request(server.url) + .get('/videos/watch/playlist/' + playlistUUID) + .set('Accept', 'text/html') + .expect(200) + + expect(resVideoPlaylistRequest.text).to.contain('') + expect(resVideoPlaylistRequest.text).to.contain('') + + const resAccountRequest = await request(server.url) + .get('/accounts/' + account.name) + .set('Accept', 'text/html') + .expect(200) + + expect(resAccountRequest.text).to.contain('') + expect(resAccountRequest.text).to.contain('') + + const resChannelRequest = await request(server.url) + .get('/video-channels/' + server.videoChannel.name) + .set('Accept', 'text/html') + .expect(200) + + expect(resChannelRequest.text).to.contain('') + expect(resChannelRequest.text).to.contain('') + }) + }) + + 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 () {