From 546265e9aec514d50d083a4847c82e11a387ff5d Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 6 Aug 2024 11:52:21 +0200 Subject: [PATCH] Support fowarding query params to oembed For now only `start` for videos and `playlistPosition` for playlists are supported --- packages/core-utils/src/common/url.ts | 2 +- packages/tests/src/api/server/services.ts | 12 ++++++ packages/tests/src/client/oembed.ts | 38 ++++++++++++++++++ server/core/controllers/services.ts | 6 ++- server/core/lib/html/shared/playlist-html.ts | 42 +++++++++++++++----- server/core/lib/html/shared/tags-html.ts | 8 ++-- server/core/lib/html/shared/video-html.ts | 28 +++++++++++-- 7 files changed, 118 insertions(+), 18 deletions(-) diff --git a/packages/core-utils/src/common/url.ts b/packages/core-utils/src/common/url.ts index 47eaecdbe..93729e408 100644 --- a/packages/core-utils/src/common/url.ts +++ b/packages/core-utils/src/common/url.ts @@ -19,7 +19,7 @@ function removeQueryParams (url: string) { return objUrl.toString() } -function queryParamsToObject (entries: any) { +function queryParamsToObject (entries: URLSearchParams) { const result: { [ id: string ]: string | number | boolean } = {} for (const [ key, value ] of entries) { diff --git a/packages/tests/src/api/server/services.ts b/packages/tests/src/api/server/services.ts index cd6cb7d7c..b875d80e8 100644 --- a/packages/tests/src/api/server/services.ts +++ b/packages/tests/src/api/server/services.ts @@ -114,6 +114,18 @@ describe('Test services', function () { } }) + it('Should have a oEmbed response with query params', async function () { + const query = '?start=1m2s&stop=2' + const oembedUrl = `http://${server.host}/w/${video.uuid}${query}&unknown=3` + const res = await server.services.getOEmbed({ oembedUrl }) + + const expectedHtml = '' + + expect(res.body.html).to.equal(expectedHtml) + }) + it('Should have a valid oEmbed response with small max height query', async function () { for (const basePath of [ '/videos/watch/', '/w/' ]) { const oembedUrl = 'http://' + server.host + basePath + video.uuid diff --git a/packages/tests/src/client/oembed.ts b/packages/tests/src/client/oembed.ts index ad3467a03..803bd8a67 100644 --- a/packages/tests/src/client/oembed.ts +++ b/packages/tests/src/client/oembed.ts @@ -58,6 +58,44 @@ describe('Test oEmbed HTML tags', function () { } }) + it('Should forward query params to video oEmbed discrovery URL', async function () { + const res = await makeGetRequest({ + url: servers[0].url, + path: '/w/' + videoIds[0], + query: { + toto: 'hello', + start: '1m2s' + }, + accept: 'text/html', + expectedStatus: HttpStatusCode.OK_200 + }) + + const expectedLink = `` + + expect(res.text).to.contain(expectedLink) + }) + + it('Should forward query params to playlist oEmbed discrovery URL', async function () { + const res = await makeGetRequest({ + url: servers[0].url, + path: '/w/p/' + playlistIds[0], + query: { + toto: 'hello', + playlistPosition: '415' + }, + accept: 'text/html', + expectedStatus: HttpStatusCode.OK_200 + }) + + const expectedLink = `` + + expect(res.text).to.contain(expectedLink) + }) + after(async function () { await cleanupTests(servers) }) diff --git a/server/core/controllers/services.ts b/server/core/controllers/services.ts index e4dd297a5..f84f2cffe 100644 --- a/server/core/controllers/services.ts +++ b/server/core/controllers/services.ts @@ -79,7 +79,11 @@ function buildPlayerURLQuery (inputQueryUrl: string) { 'subtitle', 'bigPlayBackgroundColor', 'mode', - 'foregroundColor' + 'foregroundColor', + 'playbackRate', + 'api', + 'waitPasswordFromEmbedAPI', + 'playlistPosition' ]) const params = new URLSearchParams() diff --git a/server/core/lib/html/shared/playlist-html.ts b/server/core/lib/html/shared/playlist-html.ts index e5df277ea..8e994cffa 100644 --- a/server/core/lib/html/shared/playlist-html.ts +++ b/server/core/lib/html/shared/playlist-html.ts @@ -1,16 +1,16 @@ -import { escapeHTML } from '@peertube/peertube-core-utils' +import { addQueryParams, escapeHTML } from '@peertube/peertube-core-utils' import { HttpStatusCode, VideoPlaylistPrivacy } from '@peertube/peertube-models' import { toCompleteUUID } from '@server/helpers/custom-validators/misc.js' +import { Memoize } from '@server/helpers/memoize.js' +import { VideoPlaylistModel } from '@server/models/video/video-playlist.js' +import { MVideoPlaylist, MVideoPlaylistFull } from '@server/types/models/index.js' import express from 'express' import validator from 'validator' import { CONFIG } from '../../../initializers/config.js' import { MEMOIZE_TTL, WEBSERVER } from '../../../initializers/constants.js' -import { Memoize } from '@server/helpers/memoize.js' -import { VideoPlaylistModel } from '@server/models/video/video-playlist.js' -import { MVideoPlaylistFull } from '@server/types/models/index.js' -import { TagsHtml } from './tags-html.js' -import { PageHtml } from './page-html.js' import { CommonEmbedHtml } from './common-embed-html.js' +import { PageHtml } from './page-html.js' +import { TagsHtml } from './tags-html.js' export class PlaylistHtml { @@ -39,7 +39,9 @@ export class PlaylistHtml { playlist: videoPlaylist, addEmbedInfo: true, addOG: true, - addTwitterCard: true + addTwitterCard: true, + + currentQuery: req.query }) } @@ -62,7 +64,10 @@ export class PlaylistHtml { playlist, addEmbedInfo: true, addOG: false, - addTwitterCard: false + addTwitterCard: false, + + // TODO: Implement it so we can send query params to oembed service + currentQuery: {} }) } @@ -77,8 +82,10 @@ export class PlaylistHtml { addOG: boolean addTwitterCard: boolean addEmbedInfo: boolean + + currentQuery: Record }) { - const { html, playlist, addEmbedInfo, addOG, addTwitterCard } = options + const { html, playlist, addEmbedInfo, addOG, addTwitterCard, currentQuery = {} } = options const escapedTruncatedDescription = TagsHtml.buildEscapedTruncatedDescription(playlist.description) let htmlResult = TagsHtml.addTitleTag(html, playlist.name) @@ -117,7 +124,22 @@ export class PlaylistHtml { schemaType, ogType, twitterCard, - embed + + embed, + oembedUrl: this.getOEmbedUrl(playlist, currentQuery) }, { playlist }) } + + private static getOEmbedUrl (playlist: MVideoPlaylist, currentQuery: Record) { + const base = WEBSERVER.URL + playlist.getWatchStaticPath() + + const additionalQuery: Record = {} + const allowedParams = new Set([ 'playlistPosition' ]) + + for (const [ key, value ] of Object.entries(currentQuery)) { + if (allowedParams.has(key)) additionalQuery[key] = value + } + + return addQueryParams(base, additionalQuery) + } } diff --git a/server/core/lib/html/shared/tags-html.ts b/server/core/lib/html/shared/tags-html.ts index 95ab5b7e4..92349204d 100644 --- a/server/core/lib/html/shared/tags-html.ts +++ b/server/core/lib/html/shared/tags-html.ts @@ -41,6 +41,8 @@ type Tags = { duration?: string views?: number } + + oembedUrl?: string } type HookContext = { @@ -74,14 +76,14 @@ export class TagsHtml { const twitterCardMetaTags = this.generateTwitterCardMetaTagsOptions(tagsValues) const schemaTags = await this.generateSchemaTagsOptions(tagsValues, context) - const { url, escapedTitle, embed, indexationPolicy } = tagsValues + const { url, escapedTitle, oembedUrl, indexationPolicy } = tagsValues const oembedLinkTags: { type: string, href: string, escapedTitle: string }[] = [] - if (embed) { + if (oembedUrl) { oembedLinkTags.push({ type: 'application/json+oembed', - href: WEBSERVER.URL + '/services/oembed?url=' + encodeURIComponent(url), + href: WEBSERVER.URL + '/services/oembed?url=' + encodeURIComponent(oembedUrl), escapedTitle }) } diff --git a/server/core/lib/html/shared/video-html.ts b/server/core/lib/html/shared/video-html.ts index ccffc683d..e1b285a9c 100644 --- a/server/core/lib/html/shared/video-html.ts +++ b/server/core/lib/html/shared/video-html.ts @@ -1,4 +1,4 @@ -import { escapeHTML } from '@peertube/peertube-core-utils' +import { addQueryParams, escapeHTML } from '@peertube/peertube-core-utils' import { HttpStatusCode, VideoPrivacy } from '@peertube/peertube-models' import { toCompleteUUID } from '@server/helpers/custom-validators/misc.js' import { Memoize } from '@server/helpers/memoize.js' @@ -39,6 +39,7 @@ export class VideoHtml { return this.buildVideoHTML({ html, video, + currentQuery: req.query, addEmbedInfo: true, addOG: true, addTwitterCard: true @@ -64,7 +65,10 @@ export class VideoHtml { video, addEmbedInfo: true, addOG: false, - addTwitterCard: false + addTwitterCard: false, + + // TODO: Implement it so we can send query params to oembed service + currentQuery: {} }) } @@ -79,8 +83,10 @@ export class VideoHtml { addOG: boolean addTwitterCard: boolean addEmbedInfo: boolean + + currentQuery: Record }) { - const { html, video, addEmbedInfo, addOG, addTwitterCard } = options + const { html, video, addEmbedInfo, addOG, addTwitterCard, currentQuery = {} } = options const escapedTruncatedDescription = TagsHtml.buildEscapedTruncatedDescription(video.description) let customHTML = TagsHtml.addTitleTag(html, video.name) @@ -107,6 +113,7 @@ export class VideoHtml { return TagsHtml.addTags(customHTML, { url: WEBSERVER.URL + video.getWatchStaticPath(), + escapedSiteName: escapeHTML(CONFIG.INSTANCE.NAME), escapedTitle: escapeHTML(video.name), escapedTruncatedDescription, @@ -118,9 +125,24 @@ export class VideoHtml { image: { url: WEBSERVER.URL + video.getPreviewStaticPath() }, embed, + oembedUrl: this.getOEmbedUrl(video, currentQuery), + ogType, twitterCard, schemaType }, { video }) } + + private static getOEmbedUrl (video: MVideo, currentQuery: Record) { + const base = WEBSERVER.URL + video.getWatchStaticPath() + + const additionalQuery: Record = {} + const allowedParams = new Set([ 'start' ]) + + for (const [ key, value ] of Object.entries(currentQuery)) { + if (allowedParams.has(key)) additionalQuery[key] = value + } + + return addQueryParams(base, additionalQuery) + } }