1
0
Fork 0
mirror of https://github.com/Chocobozzz/PeerTube.git synced 2025-10-03 17:59:37 +02:00

Use indexifembedded for embeds

This commit is contained in:
Chocobozzz 2025-03-31 16:34:41 +02:00
parent 5ce0b0f65d
commit f0f44e1704
No known key found for this signature in database
GPG key ID: 583A612D890159BE
7 changed files with 56 additions and 48 deletions

View file

@ -1,9 +1,9 @@
/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
import { expect } from 'chai'
import { ServerConfig, VideoPlaylistCreateResult } from '@peertube/peertube-models'
import { ServerConfig } from '@peertube/peertube-models'
import { cleanupTests, makeHTMLRequest, PeerTubeServer } from '@peertube/peertube-server-commands'
import { checkIndexTags, prepareClientTests } from '@tests/shared/client.js'
import { expect } from 'chai'
describe('Test embed HTML generation', function () {
let servers: PeerTubeServer[]
@ -18,7 +18,6 @@ describe('Test embed HTML generation', function () {
let passwordProtectedVideoId: string
let playlistIds: (string | number)[] = []
let playlist: VideoPlaylistCreateResult
let privatePlaylistId: string
let unlistedPlaylistId: string
let playlistName: string
@ -27,9 +26,8 @@ describe('Test embed HTML generation', function () {
let instanceConfig: { name: string, shortDescription: string }
before(async function () {
this.timeout(120000);
({
this.timeout(120000)
;({
servers,
videoIds,
privateVideoId,
@ -42,7 +40,6 @@ describe('Test embed HTML generation', function () {
playlistIds,
playlistName,
playlistDescription,
playlist,
unlistedPlaylistId,
privatePlaylistId,
instanceConfig
@ -83,86 +80,82 @@ describe('Test embed HTML generation', function () {
})
describe('Canonical tags', function () {
it('Should use the original video URL for the canonical tag', async function () {
it('Should not use the original video URL for the canonical tag', async function () {
for (const id of videoIds) {
const res = await makeHTMLRequest(servers[0].url, '/videos/embed/' + id)
expect(res.text).to.contain(`<link rel="canonical" href="${servers[0].url}/w/${servers[0].store.video.shortUUID}" />`)
expect(res.text).to.not.contain(`<link rel="canonical" `)
}
})
it('Should use the original playlist URL for the canonical tag', async function () {
it('Should not use the original playlist URL for the canonical tag', async function () {
for (const id of playlistIds) {
const res = await makeHTMLRequest(servers[0].url, '/video-playlists/embed/' + id)
expect(res.text).to.contain(`<link rel="canonical" href="${servers[0].url}/w/p/${playlist.shortUUID}" />`)
expect(res.text).to.not.contain(`<link rel="canonical" `)
}
})
})
describe('Indexation tags', function () {
it('Should not index remote videos', async function () {
it('Should index remote videos', async function () {
for (const id of videoIds) {
{
const res = await makeHTMLRequest(servers[1].url, '/videos/embed/' + id)
expect(res.text).to.contain('<meta name="robots" content="noindex" />')
expect(res.text).to.contain('<meta name="robots" content="noindex, indexifembedded" />')
}
{
const res = await makeHTMLRequest(servers[0].url, '/videos/embed/' + id)
expect(res.text).to.not.contain('<meta name="robots" content="noindex" />')
expect(res.text).to.contain('<meta name="robots" content="noindex, indexifembedded" />')
}
}
})
it('Should not index remote playlists', async function () {
it('Should index remote playlists', async function () {
for (const id of playlistIds) {
{
const res = await makeHTMLRequest(servers[1].url, '/video-playlists/embed/' + id)
expect(res.text).to.contain('<meta name="robots" content="noindex" />')
expect(res.text).to.contain('<meta name="robots" content="noindex, indexifembedded" />')
}
{
const res = await makeHTMLRequest(servers[0].url, '/video-playlists/embed/' + id)
expect(res.text).to.not.contain('<meta name="robots" content="noindex" />')
expect(res.text).to.contain('<meta name="robots" content="noindex, indexifembedded" />')
}
}
})
it('Should add noindex meta tags for unlisted video', async function () {
it('Should not add noindex meta tags for unlisted video', async function () {
{
const res = await makeHTMLRequest(servers[0].url, '/videos/embed/' + videoIds[0])
expect(res.text).to.not.contain('<meta name="robots" content="noindex" />')
expect(res.text).to.contain('<meta name="robots" content="noindex, indexifembedded" />')
}
{
const res = await makeHTMLRequest(servers[0].url, '/videos/embed/' + unlistedVideoId)
expect(res.text).to.contain('unlisted')
expect(res.text).to.contain('<meta name="robots" content="noindex" />')
expect(res.text).to.contain('<meta name="robots" content="noindex, indexifembedded" />')
}
})
it('Should add noindex meta tags for unlisted playlist', async function () {
it('Should not add noindex meta tags for unlisted playlist', async function () {
{
const res = await makeHTMLRequest(servers[0].url, '/video-playlists/embed/' + playlistIds[0])
expect(res.text).to.not.contain('<meta name="robots" content="noindex" />')
expect(res.text).to.contain('<meta name="robots" content="noindex, indexifembedded" />')
}
{
const res = await makeHTMLRequest(servers[0].url, '/video-playlists/embed/' + unlistedPlaylistId)
expect(res.text).to.contain('unlisted')
expect(res.text).to.contain('<meta name="robots" content="noindex" />')
expect(res.text).to.contain('<meta name="robots" content="noindex, indexifembedded" />')
}
})
})
describe('Check leak of private objects', function () {
it('Should not leak video information in embed', async function () {
for (const id of [ privateVideoId, internalVideoId, passwordProtectedVideoId ]) {
const res = await makeHTMLRequest(servers[0].url, '/videos/embed/' + id)

View file

@ -11,7 +11,6 @@ import { PageHtml } from './page-html.js'
import { TagsHtml, TagsOptions } from './tags-html.js'
export class ActorHtml {
static async getAccountHTMLPage (nameWithHost: string, req: express.Request, res: express.Response) {
const accountModelPromise = AccountModel.loadByNameWithHost(nameWithHost)
@ -43,9 +42,10 @@ export class ActorHtml {
return this.getAccountOrChannelHTMLPage({
loader: () => Promise.resolve(account || channel),
getRSSFeeds: () => account
? getDefaultRSSFeeds(WEBSERVER.URL, CONFIG.INSTANCE.NAME)
: getChannelRSSFeeds(WEBSERVER.URL, CONFIG.INSTANCE.NAME, channel),
getRSSFeeds: () =>
account
? getDefaultRSSFeeds(WEBSERVER.URL, CONFIG.INSTANCE.NAME)
: getChannelRSSFeeds(WEBSERVER.URL, CONFIG.INSTANCE.NAME, channel),
req,
res
@ -109,6 +109,7 @@ export class ActorHtml {
},
forbidIndexation: !entity.Actor.isOwned(),
embedIndexation: false,
rssFeeds: getRSSFeeds(entity)
}, {})

View file

@ -12,5 +12,5 @@ export function buildEmptyEmbedHTML (options: {
let htmlResult = TagsHtml.addTitleTag(html)
htmlResult = TagsHtml.addDescriptionTag(htmlResult)
return TagsHtml.addTags(htmlResult, { forbidIndexation: true }, { playlist, video })
return TagsHtml.addTags(htmlResult, { forbidIndexation: true, embedIndexation: true }, { playlist, video })
}

View file

@ -21,7 +21,6 @@ import { ServerConfigManager } from '../../server-config-manager.js'
import { TagsHtml } from './tags-html.js'
export class PageHtml {
private static htmlCache: { [path: string]: string } = {}
static invalidateCache () {
@ -60,6 +59,7 @@ export class PageHtml {
ogType: 'website',
twitterCard: 'summary_large_image',
forbidIndexation: false,
embedIndexation: false,
rssFeeds: getDefaultRSSFeeds(WEBSERVER.URL, CONFIG.INSTANCE.NAME)
}, {})
@ -132,7 +132,6 @@ export class PageHtml {
sameSite: 'none',
maxAge: 1000 * 3600 * 24 * 90 // 3 months
})
} else if (req.cookies.clientLanguage && is18nLocale(req.cookies.clientLanguage)) {
lang = req.cookies.clientLanguage
} else {
@ -140,7 +139,8 @@ export class PageHtml {
}
logger.debug(
'Serving %s HTML language', buildFileLocale(lang),
'Serving %s HTML language',
buildFileLocale(lang),
{ cookie: req.cookies?.clientLanguage, paramLang, acceptLanguage: req.headers['accept-language'] }
)

View file

@ -13,7 +13,6 @@ import { PageHtml } from './page-html.js'
import { TagsHtml } from './tags-html.js'
export class PlaylistHtml {
static async getWatchPlaylistHTML (videoPlaylistIdArg: string, req: express.Request, res: express.Response) {
const videoPlaylistId = toCompleteUUID(videoPlaylistIdArg)
@ -40,6 +39,7 @@ export class PlaylistHtml {
addEmbedInfo: true,
addOG: true,
addTwitterCard: true,
isEmbed: false,
currentQuery: req.query
})
@ -65,6 +65,7 @@ export class PlaylistHtml {
addEmbedInfo: true,
addOG: false,
addTwitterCard: false,
isEmbed: true,
// TODO: Implement it so we can send query params to oembed service
currentQuery: {}
@ -83,9 +84,11 @@ export class PlaylistHtml {
addTwitterCard: boolean
addEmbedInfo: boolean
isEmbed: boolean
currentQuery: Record<string, string>
}) {
const { html, playlist, addEmbedInfo, addOG, addTwitterCard, currentQuery = {} } = options
const { html, playlist, addEmbedInfo, addOG, addTwitterCard, isEmbed, currentQuery = {} } = options
const escapedTruncatedDescription = TagsHtml.buildEscapedTruncatedDescription(playlist.description)
let htmlResult = TagsHtml.addTitleTag(html, playlist.name)
@ -113,7 +116,11 @@ export class PlaylistHtml {
escapedTitle: escapeHTML(playlist.name),
escapedTruncatedDescription,
forbidIndexation: !playlist.isOwned() || playlist.privacy !== VideoPlaylistPrivacy.PUBLIC,
forbidIndexation: isEmbed
? playlist.privacy !== VideoPlaylistPrivacy.PUBLIC && playlist.privacy !== VideoPlaylistPrivacy.UNLISTED
: !playlist.isOwned() || playlist.privacy !== VideoPlaylistPrivacy.PUBLIC,
embedIndexation: isEmbed,
image: playlist.hasThumbnail()
? { url: playlist.getThumbnailUrl(), width: playlist.Thumbnail.width, height: playlist.Thumbnail.height }

View file

@ -9,6 +9,7 @@ import { Hooks } from '../../plugins/hooks.js'
export type TagsOptions = {
forbidIndexation: boolean
embedIndexation: boolean
url?: string
@ -93,7 +94,7 @@ export class TagsHtml {
}
const schemaTags = await this.generateSchemaTagsOptions(tagsValues, context)
const { url, escapedTitle, oembedUrl, forbidIndexation, relMe, rssFeeds } = tagsValues
const { url, escapedTitle, oembedUrl, forbidIndexation, embedIndexation, relMe, rssFeeds } = tagsValues
const oembedLinkTags: { type: string, href: string, escapedTitle: string }[] = []
@ -133,13 +134,12 @@ export class TagsHtml {
}
}
// SEO, use origin URL
if (forbidIndexation !== true && url) {
tagsStr += `<link rel="canonical" href="${url}" />`
}
if (forbidIndexation === true) {
tagsStr += `<meta name="robots" content="noindex" />`
} else if (embedIndexation) {
tagsStr += `<meta name="robots" content="noindex, indexifembedded" />`
} else if (url) { // SEO, use origin URL
tagsStr += `<link rel="canonical" href="${url}" />`
}
for (const rssLink of (rssFeeds || [])) {

View file

@ -15,7 +15,6 @@ import { PageHtml } from './page-html.js'
import { TagsHtml } from './tags-html.js'
export class VideoHtml {
static async getWatchVideoHTML (videoIdArg: string, req: express.Request, res: express.Response) {
const videoId = toCompleteUUID(videoIdArg)
@ -42,7 +41,8 @@ export class VideoHtml {
currentQuery: req.query,
addEmbedInfo: true,
addOG: true,
addTwitterCard: true
addTwitterCard: true,
isEmbed: false
})
}
@ -66,6 +66,7 @@ export class VideoHtml {
addEmbedInfo: true,
addOG: false,
addTwitterCard: false,
isEmbed: true,
// TODO: Implement it so we can send query params to oembed service
currentQuery: {}
@ -84,9 +85,11 @@ export class VideoHtml {
addTwitterCard: boolean
addEmbedInfo: boolean
isEmbed: boolean
currentQuery: Record<string, string>
}) {
const { html, video, addEmbedInfo, addOG, addTwitterCard, currentQuery = {} } = options
const { html, video, addEmbedInfo, addOG, addTwitterCard, isEmbed, currentQuery = {} } = options
const escapedTruncatedDescription = TagsHtml.buildEscapedTruncatedDescription(video.description)
let customHTML = TagsHtml.addTitleTag(html, video.name)
@ -120,7 +123,11 @@ export class VideoHtml {
escapedTitle: escapeHTML(video.name),
escapedTruncatedDescription,
forbidIndexation: video.remote || video.privacy !== VideoPrivacy.PUBLIC,
forbidIndexation: isEmbed
? video.privacy !== VideoPrivacy.PUBLIC && video.privacy !== VideoPrivacy.UNLISTED
: video.remote || video.privacy !== VideoPrivacy.PUBLIC,
embedIndexation: isEmbed,
image: preview
? { url: WEBSERVER.URL + video.getPreviewStaticPath(), width: preview.width, height: preview.height }