[oEmbed] some changes and fixes (#392)

* Fix query selection

* Fix xml format

* Fix link url and author_url

* Refactor repeated components

* Refactor repeated embed iframe string

* Add support for passing referrer queries to src

* Change iframe id from lbry to odysee

* Improve replace logic understanding

* Fix URL

Co-authored-by: Thomas Zarebczan <tzarebczan@users.noreply.github.com>
This commit is contained in:
saltrafael 2021-12-01 12:36:52 -03:00 committed by GitHub
parent 787ebd9588
commit c492204e26
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 136 additions and 110 deletions

View file

@ -89,6 +89,9 @@ const config = {
};
config.SDK_API_PATH = `${config.LBRY_WEB_API}/api/v1`;
config.PROXY_URL = `${config.SDK_API_PATH}/proxy`;
config.URL_DEV = `http://localhost:${config.WEBPACK_WEB_PORT}`;
config.URL_LOCAL = `http://localhost:${config.WEB_SERVER_PORT}`;
config.FAVICON = `/public/favicon-spaceman.png`;

View file

@ -2,7 +2,7 @@
import { Lbryio } from 'lbryinc';
import * as Sentry from '@sentry/browser';
import * as RENDER_MODES from 'constants/file_render_modes';
import { SDK_API_PATH } from './index';
import { SDK_API_PATH } from 'config';
// --- GA ---
// - Events: 500 max (cannot be deleted).

View file

@ -3,7 +3,7 @@ import * as ICONS from 'constants/icons';
import { FormField } from 'component/common/form';
import Button from 'component/button';
import React, { useRef } from 'react';
import { generateEmbedUrl } from 'util/web';
import { generateEmbedUrl, generateEmbedIframeData } from 'util/web';
type Props = {
copyable: string,
@ -22,7 +22,7 @@ export default function EmbedTextArea(props: Props) {
const input = useRef();
const streamUrl = generateEmbedUrl(name, claimId, includeStartTime, startTime, referralCode);
let embedText = `<iframe id="lbry-iframe" width="560" height="315" src="${streamUrl}" allowfullscreen></iframe>`;
const { html: embedText } = generateEmbedIframeData(streamUrl);
function copyToClipboard() {
const topRef = input.current;

View file

@ -35,7 +35,7 @@ import {
doAuthTokenRefresh,
} from 'util/saved-passwords';
import { X_LBRY_AUTH_TOKEN } from 'constants/token';
import { LBRY_WEB_API, DEFAULT_LANGUAGE, LBRY_API_URL } from 'config';
import { PROXY_URL, DEFAULT_LANGUAGE, LBRY_API_URL } from 'config';
// Import 3rd-party styles before ours for the current way we are code-splitting.
import 'scss/third-party.scss';
@ -64,13 +64,7 @@ if (process.env.SDK_API_URL) {
console.warn('SDK_API_URL env var is deprecated. Use SDK_API_HOST instead'); // @eslint-disable-line
}
let sdkAPIHost = process.env.SDK_API_HOST || process.env.SDK_API_URL;
sdkAPIHost = LBRY_WEB_API;
export const SDK_API_PATH = `${sdkAPIHost}/api/v1`;
const proxyURL = `${SDK_API_PATH}/proxy`;
Lbry.setDaemonConnectionString(proxyURL);
Lbry.setDaemonConnectionString(PROXY_URL);
Lbry.setOverride(
'publish',

View file

@ -1,4 +1,4 @@
const { URL, LBRY_WEB_STREAMING_API } = require('../../config');
const { URL, LBRY_WEB_STREAMING_API, THUMBNAIL_CARDS_CDN_URL } = require('../../config');
const CONTINENT_COOKIE = 'continent';
@ -25,6 +25,14 @@ function generateEmbedUrl(claimName, claimId, includeStartTime, startTime, refer
.replace(/\)/g, '%29')}/${claimId}?${urlParams.toString()}`;
}
function generateEmbedIframeData(src) {
const width = '560';
const height = '315';
const html = `<iframe id="odysee-iframe" width="${width}" height="${height}" src="${src}" allowfullscreen></iframe>`;
return { html, width, height };
}
function generateDownloadUrl(claimName, claimId) {
return `${URL}/$/download/${claimName}/${claimId}`;
}
@ -33,5 +41,34 @@ function generateDirectUrl(claimName, claimId) {
return `${URL}/$/stream/${claimName}/${claimId}`;
}
function getThumbnailCdnUrl(url) {
if (
!THUMBNAIL_CARDS_CDN_URL ||
!url ||
(url && (url.includes('https://twitter-card') || url.includes('https://cards.odysee.com')))
) {
return url;
}
if (url) {
const encodedURL = Buffer.from(url).toString('base64');
return `${THUMBNAIL_CARDS_CDN_URL}${encodedURL}.jpg`;
}
}
function getParameterByName(name, url) {
var match = RegExp('[?&]' + name + '=([^&]*)').exec(url);
return match && decodeURIComponent(match[1].replace(/\+/g, ' '));
}
// module.exports needed since the web server imports this function
module.exports = { generateStreamUrl, generateEmbedUrl, generateDownloadUrl, generateDirectUrl, CONTINENT_COOKIE };
module.exports = {
CONTINENT_COOKIE,
generateDirectUrl,
generateDownloadUrl,
generateEmbedIframeData,
generateEmbedUrl,
generateStreamUrl,
getParameterByName,
getThumbnailCdnUrl,
};

View file

@ -1,4 +1,4 @@
import { SDK_API_PATH } from 'ui';
import { SDK_API_PATH } from 'config';
import { useEffect } from 'react';
import { getAuthToken } from 'util/saved-passwords';
import { X_LBRY_AUTH_TOKEN } from 'constants/token';

View file

@ -1,19 +1,24 @@
const {
FAVICON,
LBRY_WEB_API,
OG_HOMEPAGE_TITLE,
OG_IMAGE_URL,
OG_TITLE_SUFFIX,
PROXY_URL,
SITE_CANONICAL_URL,
SITE_DESCRIPTION,
SITE_NAME,
SITE_TITLE,
THUMBNAIL_CARDS_CDN_URL,
URL,
} = require('../../config.js');
const { CATEGORY_METADATA } = require('./category-metadata');
const { generateEmbedUrl, generateStreamUrl, generateDirectUrl } = require('../../ui/util/web');
const {
generateDirectUrl,
generateEmbedUrl,
generateStreamUrl,
getParameterByName,
getThumbnailCdnUrl,
} = require('../../ui/util/web');
const { getJsBundleId } = require('../bundle-id.js');
const { lbryProxy: Lbry } = require('../lbry');
const { parseURI, normalizeClaimUrl } = require('./lbryURI');
@ -24,28 +29,11 @@ const path = require('path');
const removeMd = require('remove-markdown');
const jsBundleId = getJsBundleId();
const SDK_API_PATH = `${LBRY_WEB_API}/api/v1`;
const PROXY_URL = `${SDK_API_PATH}/proxy`;
Lbry.setDaemonConnectionString(PROXY_URL);
const BEGIN_STR = '<!-- VARIABLE_HEAD_BEGIN -->';
const FINAL_STR = '<!-- VARIABLE_HEAD_END -->';
function getThumbnailCdnUrl(url) {
if (
!THUMBNAIL_CARDS_CDN_URL ||
!url ||
(url && (url.includes('https://twitter-card') || url.includes('https://cards.odysee.com')))
) {
return url;
}
if (url) {
const encodedURL = Buffer.from(url).toString('base64');
return `${THUMBNAIL_CARDS_CDN_URL}${encodedURL}.jpg`;
}
}
function insertToHead(fullHtml, htmlToInsert) {
const beginIndex = fullHtml.indexOf(BEGIN_STR);
const finalIndex = fullHtml.indexOf(FINAL_STR);
@ -148,7 +136,7 @@ function buildBasicOgMetadata() {
// Metadata used for urls that need claim information
// Also has option to override defaults
//
function buildClaimOgMetadata(uri, claim, overrideOptions = {}) {
function buildClaimOgMetadata(uri, claim, overrideOptions = {}, referrerQuery) {
// Initial setup for claim based og metadata
const { claimName } = parseURI(uri);
const { meta, value, signing_channel } = claim;
@ -187,6 +175,7 @@ function buildClaimOgMetadata(uri, claim, overrideOptions = {}) {
const title = overrideOptions.title || claimTitle;
const description = overrideOptions.description || claimDescription;
const cleanDescription = removeMd(description);
const claimPath = `${URL}/${claim.canonical_url.replace('lbry://', '').replace('#', ':')}`;
let head = '';
@ -209,17 +198,16 @@ function buildClaimOgMetadata(uri, claim, overrideOptions = {}) {
head += `<meta property="og:type" content="website"/>`;
head += `<meta property="og:title" content="${title}"/>`;
head += `<meta name="twitter:title" content="${title}"/>`;
// below should be canonical_url, but not provided by chainquery yet
head += `<meta property="og:url" content="${URL}/${claim.name}:${claim.claim_id}"/>`;
head += `<meta name="twitter:url" content="${URL}/${claim.name}:${claim.claim_id}"/>`;
head += `<meta property="og:url" content="${claimPath}"/>`;
head += `<meta name="twitter:url" content="${claimPath}"/>`;
head += `<meta property="fb:app_id" content="1673146449633983" />`;
head += `<link rel="canonical" content="${SITE_CANONICAL_URL || URL}/${claim.name}:${claim.claim_id}"/>`;
head += `<link rel="canonical" content="${claimPath}"/>`;
head += `<link rel="alternate" type="application/json+oembed" href="${URL}/$/oembed?url=${encodeURIComponent(
`${URL}/${claim.canonical_url}`
)}&format=json" title="${title}" />`;
claimPath
)}&format=json${referrerQuery ? `&r=${referrerQuery}` : ''}" title="${title}" />`;
head += `<link rel="alternate" type="text/xml+oembed" href="${URL}/$/oembed?url=${encodeURIComponent(
`${URL}/${claim.canonical_url}`
)}&format=xml" title="${title}" />`;
claimPath
)}&format=xml${referrerQuery ? `&r=${referrerQuery}` : ''}" title="${title}" />`;
if (mediaType && (mediaType.startsWith('video/') || mediaType.startsWith('audio/'))) {
const videoUrl = generateEmbedUrl(claim.name, claim.claim_id);
@ -381,9 +369,10 @@ async function getHtml(ctx) {
if (!requestPath.includes('$')) {
const claimUri = normalizeClaimUrl(requestPath.slice(1));
const claim = await resolveClaimOrRedirect(ctx, claimUri);
const referrerQuery = getParameterByName('r', ctx.request.url);
if (claim) {
const ogMetadata = buildClaimOgMetadata(claimUri, claim);
const ogMetadata = buildClaimOgMetadata(claimUri, claim, {}, referrerQuery);
const googleVideoMetadata = buildGoogleVideoMetadata(claimUri, claim);
return insertToHead(html, ogMetadata.concat('\n', googleVideoMetadata));
}

View file

@ -1,49 +1,26 @@
const { URL, SITE_NAME, PROXY_URL, THUMBNAIL_HEIGHT, THUMBNAIL_WIDTH } = require('../../config.js');
const {
URL,
SITE_NAME,
LBRY_WEB_API,
THUMBNAIL_CARDS_CDN_URL,
THUMBNAIL_HEIGHT,
THUMBNAIL_WIDTH,
} = require('../../config.js');
const { generateEmbedUrl } = require('../../ui/util/web');
generateEmbedIframeData,
generateEmbedUrl,
getParameterByName,
getThumbnailCdnUrl,
} = require('../../ui/util/web');
const { lbryProxy: Lbry } = require('../lbry');
const { normalizeURI } = require('./lbryURI');
const SDK_API_PATH = `${LBRY_WEB_API}/api/v1`;
const proxyURL = `${SDK_API_PATH}/proxy`;
Lbry.setDaemonConnectionString(proxyURL);
Lbry.setDaemonConnectionString(PROXY_URL);
// ****************************************************************************
// Fetch claim info
// ****************************************************************************
function getThumbnailCdnUrl(url) {
if (
!THUMBNAIL_CARDS_CDN_URL ||
!url ||
(url && (url.includes('https://twitter-card') || url.includes('https://cards.odysee.com')))
) {
return url;
}
if (url) {
const encodedURL = Buffer.from(url).toString('base64');
return `${THUMBNAIL_CARDS_CDN_URL}${encodedURL}.jpg`;
}
}
async function getClaim(requestUrl) {
const path = requestUrl.replace(URL, '').substring(1);
const uri = requestUrl.replace(`${URL}/`, 'lbry://');
let uri;
let claim;
let error;
try {
uri = normalizeURI(path);
const response = await Lbry.resolve({ urls: [uri] });
if (response && response[uri] && !response[uri].error) {
claim = response[uri];
@ -67,17 +44,17 @@ async function getClaim(requestUrl) {
// Generate
// ****************************************************************************
function generateOEmbedData(claim) {
function generateOEmbedData(claim, referrerQuery) {
const { value, signing_channel: authorClaim } = claim;
const claimTitle = value.title;
const authorName = authorClaim ? authorClaim.value.title || authorClaim.name : 'Anonymous';
const authorUrlPath = authorClaim && authorClaim.canonical_url.replace('lbry://', '');
const authorUrlPath = authorClaim && authorClaim.canonical_url.replace('lbry://', '').replace('#', ':');
const authorUrl = authorClaim ? `${URL}/${authorUrlPath}` : null;
const thumbnailUrl = value && value.thumbnail && value.thumbnail.url && getThumbnailCdnUrl(value.thumbnail.url);
const videoUrl = generateEmbedUrl(claim.name, claim.claim_id);
const videoWidth = value.video && value.video.width;
const videoHeight = value.video && value.video.height;
const videoUrl = generateEmbedUrl(claim.name, claim.claim_id) + (referrerQuery ? `r=${referrerQuery}` : '');
const { html, width, height } = generateEmbedIframeData(videoUrl);
return {
type: 'video',
@ -90,40 +67,68 @@ function generateOEmbedData(claim) {
thumbnail_url: thumbnailUrl,
thumbnail_width: THUMBNAIL_WIDTH,
thumbnail_height: THUMBNAIL_HEIGHT,
html: `<iframe id="lbry-iframe" width="560" height="315" src="${videoUrl}" allowfullscreen></iframe>`,
width: videoWidth,
height: videoHeight,
html: html,
width: width,
height: height,
};
}
function generateXmlData(oEmbedData) {
const {
type,
version,
title,
author_name,
author_url,
provider_name,
provider_url,
thumbnail_url,
thumbnail_width,
thumbnail_height,
html,
width,
height,
} = oEmbedData;
return (
'<?xml version="1.0" encoding="utf-8"?>' +
'<oembed>' +
`<type>${type}</type>` +
`<version>${version}</version>` +
`<title>${title}</title>` +
`<author_name>${author_name}</author_name>` +
`<author_url>${author_url}</author_url>` +
`<provider_name>${provider_name}</provider_name>` +
`<provider_url>${provider_url}</provider_url>` +
`<thumbnail_url>${thumbnail_url}</thumbnail_url>` +
`<thumbnail_width>${thumbnail_width}</thumbnail_width>` +
`<thumbnail_height>${thumbnail_height}</thumbnail_height>` +
`<html>${html}</html>` +
`<width>${width}</width>` +
`<height>${height}</height>` +
'<oembed>'
);
}
async function getOEmbed(ctx) {
const path = ctx.request.url;
const urlQuery = '?url=';
const formatQuery = '&format=';
const requestUrl = ctx.request.url;
const urlQuery = getParameterByName('url', requestUrl);
const requestUrl = decodeURIComponent(
path.substring(
path.indexOf(urlQuery) + urlQuery.length,
path.indexOf('&') > path.indexOf(urlQuery) ? path.indexOf('&') : path.length
)
);
const requestFormat = path.substring(
path.indexOf(formatQuery) + formatQuery.length,
path.indexOf('&') > path.indexOf(formatQuery) ? path.indexOf('&') : path.length
);
const isXml = requestFormat === 'xml';
const { claim, error } = await getClaim(requestUrl);
const { claim, error } = await getClaim(urlQuery);
if (error) return error;
const oEmbedData = generateOEmbedData(claim);
const referrerQuery = getParameterByName('r', requestUrl);
const oEmbedData = generateOEmbedData(claim, referrerQuery);
if (isXml) {
ctx.set('Content-Type', 'text/xml+oembed');
return oEmbedData.xml();
const formatQuery = getParameterByName('format', requestUrl);
if (formatQuery === 'xml') {
ctx.set('Content-Type', 'application/xml');
const xmlData = generateXmlData(oEmbedData);
return xmlData;
}
ctx.set('Content-Type', 'application/json+oembed');
ctx.set('Content-Type', 'application/json');
return oEmbedData;
}

View file

@ -1,12 +1,10 @@
const { generateStreamUrl } = require('../../ui/util/web');
const { lbryProxy: Lbry } = require('../lbry');
const { URL, SITE_NAME, LBRY_WEB_API } = require('../../config.js');
const { URL, SITE_NAME, PROXY_URL } = require('../../config.js');
const Mime = require('mime-types');
const Rss = require('rss');
const SDK_API_PATH = `${LBRY_WEB_API}/api/v1`;
const proxyURL = `${SDK_API_PATH}/proxy`;
Lbry.setDaemonConnectionString(proxyURL);
Lbry.setDaemonConnectionString(PROXY_URL);
const NUM_ENTRIES = 500;