GoogleVideo: fix contentUrl and add more metadata (#1659)

* GoogleVideo: add more metadata

* GoogleVideo: fix `contentUrl`

`contentUrl` needs to be the url of the video file itself, not the page or some redirect.  Copied the way the url is generated in the RSS code.
This commit is contained in:
infinite-persistence 2022-06-10 19:22:09 +08:00 committed by GitHub
parent 86d311f45d
commit 2a83c7d8ec
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 75 additions and 5 deletions

View file

@ -363,7 +363,7 @@ async function getHtml(ctx) {
if (claim) { if (claim) {
const ogMetadata = await buildClaimOgMetadata(claimUri, claim); const ogMetadata = await buildClaimOgMetadata(claimUri, claim);
const googleVideoMetadata = buildGoogleVideoMetadata(claimUri, claim); const googleVideoMetadata = await buildGoogleVideoMetadata(claimUri, claim);
return insertToHead(html, ogMetadata.concat('\n', googleVideoMetadata)); return insertToHead(html, ogMetadata.concat('\n', googleVideoMetadata));
} }
@ -387,7 +387,7 @@ async function getHtml(ctx) {
if (claim) { if (claim) {
const ogMetadata = await buildClaimOgMetadata(claimUri, claim, {}, referrerQuery); const ogMetadata = await buildClaimOgMetadata(claimUri, claim, {}, referrerQuery);
const googleVideoMetadata = buildGoogleVideoMetadata(claimUri, claim); const googleVideoMetadata = await buildGoogleVideoMetadata(claimUri, claim);
return insertToHead(html, ogMetadata.concat('\n', googleVideoMetadata)); return insertToHead(html, ogMetadata.concat('\n', googleVideoMetadata));
} }
} }

View file

@ -1,10 +1,22 @@
const Mime = require('mime-types');
const moment = require('moment'); const moment = require('moment');
const removeMd = require('remove-markdown'); const removeMd = require('remove-markdown');
// TODO: fix relative path for server // TODO: fix relative path for server
const { fetchStreamUrl } = require('../fetchStreamUrl');
const { parseURI } = require('../lbryURI'); const { parseURI } = require('../lbryURI');
const { OG_IMAGE_URL, SITE_NAME, URL } = require('../../../config.js'); const { OG_IMAGE_URL, SITE_NAME, URL } = require('../../../config.js');
const { generateDirectUrl, generateEmbedUrl, getThumbnailCdnUrl, escapeHtmlProperty } = require('../../../ui/util/web'); const { generateEmbedUrl, getThumbnailCdnUrl, escapeHtmlProperty } = require('../../../ui/util/web');
// ****************************************************************************
// Utils
// ****************************************************************************
function lbryToOdyseeUrl(claim) {
if (claim.canonical_url) {
return `${URL}/${claim.canonical_url.replace('lbry://', '').replace(/#/g, ':')}`;
}
}
function truncateDescription(description, maxChars = 200) { function truncateDescription(description, maxChars = 200) {
// Get list of single-codepoint strings // Get list of single-codepoint strings
@ -15,11 +27,57 @@ function truncateDescription(description, maxChars = 200) {
return chars.length > maxChars ? truncated + '...' : truncated; return chars.length > maxChars ? truncated + '...' : truncated;
} }
// ****************************************************************************
// ****************************************************************************
const Generate = {
author: (claim) => {
const channelName = claim?.signing_channel?.value?.title || claim?.signing_channel?.name;
const channelUrl = lbryToOdyseeUrl(claim.signing_channel);
if (channelName && channelUrl) {
return { '@type': 'Person', name: channelName, url: channelUrl };
}
},
height: (claim) => {
return claim?.value?.video?.height;
},
keywords: (claim) => {
const tags = claim?.value?.tags;
if (tags) {
// Some claims, probably created from cli, have a crazy amount of tags.
// Limit that to 10.
return tags.slice(0, 10).join(',');
}
},
potentialAction: (claim) => {
// https://developers.google.com/search/docs/advanced/structured-data/video?hl=en#seek
if ((claim?.value?.video || claim?.value?.audio) && claim.canonical_url) {
return {
'@type': 'SeekToAction',
target: `${lbryToOdyseeUrl(claim)}?t={seek_to_second_number}`,
'startOffset-input': 'required name=seek_to_second_number',
};
}
},
thumbnail: (url) => {
// We don't have 'width' and 'height' from the claim :(
return { '@type': 'ImageObject', url };
},
width: (claim) => {
return claim?.value?.video?.width;
},
};
// **************************************************************************** // ****************************************************************************
// buildGoogleVideoMetadata // buildGoogleVideoMetadata
// **************************************************************************** // ****************************************************************************
function buildGoogleVideoMetadata(uri, claim) { async function buildGoogleVideoMetadata(uri, claim) {
const { claimName } = parseURI(uri); const { claimName } = parseURI(uri);
const { meta, value } = claim; const { meta, value } = claim;
const media = value && value.video; const media = value && value.video;
@ -41,6 +99,10 @@ function buildGoogleVideoMetadata(uri, claim) {
const claimThumbnail = escapeHtmlProperty(thumbnail) || getThumbnailCdnUrl(OG_IMAGE_URL) || `${URL}/public/v2-og.png`; const claimThumbnail = escapeHtmlProperty(thumbnail) || getThumbnailCdnUrl(OG_IMAGE_URL) || `${URL}/public/v2-og.png`;
const fileExt = value.source && value.source.media_type && '.' + Mime.extension(value.source.media_type);
const claimStreamUrl =
(await fetchStreamUrl(claim.name, claim.claim_id)).replace('/v4/', '/v3/') + (fileExt || '.mp4'); // v3 = mp4 always, v4 may redirect to m3u8;
// https://developers.google.com/search/docs/data-types/video // https://developers.google.com/search/docs/data-types/video
const googleVideoMetadata = { const googleVideoMetadata = {
// --- Must --- // --- Must ---
@ -52,8 +114,16 @@ function buildGoogleVideoMetadata(uri, claim) {
uploadDate: `${new Date(releaseTime * 1000).toISOString()}`, uploadDate: `${new Date(releaseTime * 1000).toISOString()}`,
// --- Recommended --- // --- Recommended ---
duration: mediaDuration ? moment.duration(mediaDuration * 1000).toISOString() : undefined, duration: mediaDuration ? moment.duration(mediaDuration * 1000).toISOString() : undefined,
contentUrl: generateDirectUrl(claim.name, claim.claim_id), url: lbryToOdyseeUrl(claim),
contentUrl: claimStreamUrl,
embedUrl: generateEmbedUrl(claim.name, claim.claim_id), embedUrl: generateEmbedUrl(claim.name, claim.claim_id),
// --- Misc ---
author: Generate.author(claim),
thumbnail: Generate.thumbnail(claimThumbnail),
keywords: Generate.keywords(claim),
width: Generate.width(claim),
height: Generate.height(claim),
potentialAction: Generate.potentialAction(claim),
}; };
if ( if (