Commit 90f1f3b4 authored by Dan Notestein's avatar Dan Notestein
Browse files

Merge branch '108-old-3speak-told' into 'develop'

Fix 3speak embedded player

Closes #108

See merge request !185
parents 29f791c1 6efc4580
import React from 'react';
/**
* Regular expressions for detecting and validating provider URLs
* @type {{htmlReplacement: RegExp, main: RegExp, sanitize: RegExp}}
*/
const regex = {
sanitize: /^https:\/\/archive\.org\/embed\/(.*)/i,
main: /^https:\/\/archive\.org\/details\/(.*)/i,
};
export default regex;
export function getIframeDimensions(...args) {
// Since we can't tell the difference between video and audio embed players from the URL, lets use the width/height
// provided by archive.org's iframe HTML code.
const [large, , width, height] = args;
if (width && height) {
return {
width,
height,
};
}
return {
width: large ? 640 : 480,
height: large ? 360 : 270,
};
}
/**
* Configuration for HTML iframe's `sandbox` attribute
* @type {useSandbox: boolean, sandboxAttributes: string[]}
*/
export const sandboxConfig = {
useSandbox: false,
sandboxAttributes: [],
};
/**
* Check if the iframe code in the post editor is to an allowed URL
* <iframe src="https://archive.org/embed/namaz-nasil-kilinir" width="640" height="480" frameborder="0" webkitallowfullscreen="true" mozallowfullscreen="true" allowfullscreen></iframe>
* <iframe src="https://archive.org/embed/geometry_dash_1.9" width="500" height="140" frameborder="0" webkitallowfullscreen="true" mozallowfullscreen="true" allowfullscreen></iframe>
* @param url
* @returns {boolean|*}
*/
export function validateIframeUrl(url) {
const match = url.match(regex.sanitize);
if (!match || match.length !== 2) {
return false;
}
return 'https://archive.org/embed/' + match[1];
}
/**
* Rewrites the embedded URL to a normalized format
* @param url
* @returns {string|boolean}
*/
export function normalizeEmbedUrl(url) {
const match = url.match(regex.sanitize);
if (match && match.length >= 2) {
return `https://archive.org/embed/${match[1]}`;
}
return false;
}
/**
* Extract the content ID and other metadata from the URL
* @param data
* @returns {null|{id: *, canonical: string, url: *}}
*/
function extractMetadata(data) {
if (!data) return null;
const m = data.match(regex.main);
if (!m || m.length < 2) return null;
const startTime = m.input.match(/t=(\d+)s?/);
return {
id: m[1],
url: m[0],
startTime: startTime ? startTime[1] : 0,
canonical: `https://archive.org/embed/${m[1]}`,
};
}
/**
* Replaces the URL with a custom Markdown for embedded players
* @param child
* @param links
* @returns {*}
*/
export function embedNode(child, links /*images*/) {
try {
const { data } = child;
const archiveorg = extractMetadata(data);
if (!archiveorg) return child;
child.data = data.replace(
archiveorg.url,
`~~~ embed:${archiveorg.id} archiveorg ~~~`
);
if (links) {
links.add(archiveorg.canonical);
}
if (links) links.add(archiveorg.canonical);
} catch (error) {
console.log(error);
}
return child;
}
/**
* Generates the Markdown/HTML code to override the detected URL with an iFrame
* @param idx
* @param id
* @param width
* @param height
* @param startTime
* @returns {*}
*/
export function genIframeMd(idx, id, width, height) {
const url = `https://archive.org/embed/${id}`;
let sandbox = sandboxConfig.useSandbox;
if (sandbox) {
if (
Object.prototype.hasOwnProperty.call(
sandboxConfig,
'sandboxAttributes'
)
) {
sandbox = sandboxConfig.sandboxAttributes.join(' ');
}
}
const iframeProps = {
src: url,
width,
height,
frameBorder: '0',
webkitallowfullscreen: 'webkitallowfullscreen',
mozallowfullscreen: 'mozallowfullscreen',
allowFullScreen: 'allowFullScreen',
};
if (sandbox) {
iframeProps.sandbox = sandbox;
}
return (
<div key={`archiveorg-${id}-${idx}`} className="videoWrapper">
<iframe
title="Archive.org embedded player"
// eslint-disable-next-line react/jsx-props-no-spreading
{...iframeProps}
/>
</div>
);
}
import React from 'react';
/**
* Regular expressions for detecting and validating provider URLs
* @type {{htmlReplacement: RegExp, main: RegExp, sanitize: RegExp}}
*/
const regex = {
sanitize: /^https:\/\/bandcamp\.com\/EmbeddedPlayer\/(album=.*)/i,
};
export default regex;
/**
* Configuration for HTML iframe's `sandbox` attribute
* @type {useSandbox: boolean, sandboxAttributes: string[]}
*/
export const sandboxConfig = {
useSandbox: false,
sandboxAttributes: [],
};
export function getIframeDimensions(...args) {
const [, idOrUrl] = args;
const baseHeight = 120;
const tracklistHeight = 155;
const largeArtworkHeight = 580;
const hasLargeArtwork = idOrUrl.indexOf('artwork=large') !== -1;
const tracklistEnabled = idOrUrl.indexOf('tracklist=true') !== -1;
let height = baseHeight;
if (hasLargeArtwork) {
height += largeArtworkHeight;
}
if (tracklistEnabled) {
height += tracklistHeight;
if (hasLargeArtwork) {
height += baseHeight;
}
}
return {
width: 700,
height,
};
}
/**
* Check if the iframe code in the post editor is to an allowed URL
* <iframe style="border: 0; width: 100%; height: 120px;" src="https://bandcamp.com/EmbeddedPlayer/album=1820259073/size=large/bgcol=ffffff/linkcol=0687f5/tracklist=true/artwork=small/transparent=true/" seamless><a href="https://bluetech.bandcamp.com/album/tomorrow">Tomorrow by Steve Roach</a></iframe>
* <iframe style="border: 0; width: 100%; height: 120px;" src="https://bandcamp.com/EmbeddedPlayer/album=1820259073/size=large/bgcol=ffffff/linkcol=0687f5/tracklist=true/artwork=small/track=266656541/transparent=true/" seamless><a href="https://bluetech.bandcamp.com/album/tomorrow">Tomorrow by Steve Roach</a></iframe>
* @param url
* @returns {boolean|*}
*/
export function validateIframeUrl(url) {
const match = url.match(regex.sanitize);
if (!match || match.length !== 2) {
return false;
}
return 'https://bandcamp.com/EmbeddedPlayer/' + match[1];
}
/**
* Rewrites the embedded URL to a normalized format
* @param url
* @returns {string|boolean}
*/
export function normalizeEmbedUrl(url) {
const match = url.match(regex.sanitize);
if (match && match.length >= 2) {
return `https://bandcamp.com/EmbeddedPlayer/${match[1]}`;
}
return false;
}
/**
* Replaces the URL with a custom Markdown for embedded players
* @param child
* @param links
* @returns {*}
*/
export function embedNode(child) {
return child;
}
/**
* Generates the Markdown/HTML code to override the detected URL with an iFrame
* @param idx
* @param id
* @param width
* @param height
* @param startTime
* @returns {*}
*/
export function genIframeMd(idx, id, width, height) {
const url = `https://bandcamp.com/EmbeddedPlayer/${id}`;
let sandbox = sandboxConfig.useSandbox;
if (sandbox) {
if (
Object.prototype.hasOwnProperty.call(
sandboxConfig,
'sandboxAttributes'
)
) {
sandbox = sandboxConfig.sandboxAttributes.join(' ');
}
}
const iframeProps = {
src: url,
width,
height,
frameBorder: '0',
webkitallowfullscreen: 'webkitallowfullscreen',
mozallowfullscreen: 'mozallowfullscreen',
allowFullScreen: 'allowFullScreen',
};
if (sandbox) {
iframeProps.sandbox = sandbox;
}
return (
<div key={`bandcamp-${id}-${idx}`} className="videoWrapper">
<iframe
title="bandcamp.com embedded player"
// eslint-disable-next-line react/jsx-props-no-spreading
{...iframeProps}
/>
</div>
);
}
......@@ -13,7 +13,7 @@ export default regex;
*/
export const sandboxConfig = {
useSandbox: true,
sandboxAttributes: [],
sandboxAttributes: ['allow-scripts', 'allow-same-origin', 'allow-popups'],
};
/**
......
# Example Media Players 2020-07-23
## dapplr direct html iframe markup
## archive.org direct html iframe markup
Video:
<iframe src="https://archive.org/embed/namaz-nasil-kilinir" width="640" height="480" frameborder="0" webkitallowfullscreen="true" mozallowfullscreen="true" allowfullscreen></iframe>
Music:
<iframe src="https://archive.org/embed/geometry_dash_1.9" width="500" height="140" frameborder="0" webkitallowfullscreen="true" mozallowfullscreen="true" allowfullscreen></iframe>
## bandcamp direct html iframe markup
Small artwork without tracklist
<iframe style="border: 0; width: 100%; height: 120px;" src="https://bandcamp.com/EmbeddedPlayer/album=1820259073/size=large/bgcol=ffffff/linkcol=0687f5/tracklist=false/artwork=small/transparent=true/" seamless><a href="https://bluetech.bandcamp.com/album/tomorrow">Tomorrow by Steve Roach</a></iframe>
Small artwork with tracklist
<iframe style="border: 0; width: 100%; height: 120px;" src="https://bandcamp.com/EmbeddedPlayer/album=1820259073/size=large/bgcol=ffffff/linkcol=0687f5/tracklist=true/artwork=small/transparent=true/" seamless><a href="https://bluetech.bandcamp.com/album/tomorrow">Tomorrow by Steve Roach</a></iframe>
Large artwork without tracklist
<iframe style="border: 0; width: 100%; height: 120px;" src="https://bandcamp.com/EmbeddedPlayer/album=1820259073/size=large/bgcol=ffffff/linkcol=0687f5/tracklist=false/artwork=large/transparent=true/" seamless><a href="https://bluetech.bandcamp.com/album/tomorrow">Tomorrow by Steve Roach</a></iframe>
Large artwork with tracklist
<iframe style="border: 0; width: 100%; height: 120px;" src="https://bandcamp.com/EmbeddedPlayer/album=1820259073/size=large/bgcol=ffffff/linkcol=0687f5/tracklist=true/artwork=large/transparent=true/" seamless><a href="https://bluetech.bandcamp.com/album/tomorrow">Tomorrow by Steve Roach</a></iframe>
## dapplr direct html iframe markup
<iframe src="https://cdn.dapplr.in/file/dapplr-videos/cryptoanalysis/yuHDBQG8XY3IBMwD0f4je9b1P7u7PAsT.mp4"></iframe>
## dapplr iframe embedded from link
Not supported
## dtube direct html iframe markup
<iframe title="DTube embedded player" src="https://emb.d.tube/#!/cyberspacegod/QmfHTqZWQkJ6uqLsca4wgZffGE3To6YVSzazFD3ReS1NcA"></iframe>
## dtube iframe embedded from link
https://emb.d.tube/#!/cyberspacegod/QmfHTqZWQkJ6uqLsca4wgZffGE3To6YVSzazFD3ReS1NcA
## soundcloud direct html iframe markup
<iframe width="100%" height="450" scrolling="no" frameborder="no" src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/257659076&amp;auto_play=false&amp;hide_related=false&amp;show_comments=true&amp;show_user=true&amp;show_reposts=false&amp;visual=true"></iframe>
## soundcloud iframe embedded from link
Not supported
## mixcloud direct html iframe markup
<iframe width="100%" height="120" src="https://www.mixcloud.com/widget/iframe/?hide_cover=1&feed=%2FMagneticMagazine%2Fambient-meditations-vol-21-anane%2F" frameborder="0" ></iframe>
## threespeak direct html iframe markup
<iframe src="https://3speak.online/embed?v=threespeak/iaarkpvf"></iframe>
## mixcloud iframe embedded from link
https://www.mixcloud.com/MagneticMagazine/ambient-meditations-vol-21-anane
## threespeak iframe embedded from link
## spotify direct html iframe markup
Playlist
<iframe src="https://open.spotify.com/embed/playlist/37i9dQZF1DWSDCcNkUu5tr" width="300" height="380" frameborder="0" allowtransparency="true" allow="encrypted-media"></iframe>
Show
<iframe src="https://open.spotify.com/embed-podcast/show/6C1Q7ITJKvZVosOdC9M1RM" width="100%" height="232" frameborder="0" allowtransparency="true" allow="encrypted-media"></iframe>
Episode
<iframe src="https://open.spotify.com/embed-podcast/episode/49EzBVgb4exGi2AIRKmTGK" width="100%" height="232" frameborder="0" allowtransparency="true" allow="encrypted-media"></iframe>
It's broken
## spotify iframe embedded from link
https://open.spotify.com/episode/523CYPCmCXdv8uBME5yzZ7?si=l2uwhVjERe21N86grg3ZEg
https://3speak.online/watch?v=threespeak/iaarkpvf
## threespeak direct html iframe markup
<iframe src="https://3speak.co/embed?v=threespeak/iaarkpvf"></iframe>
## threespeak iframe embedded from link
https://3speak.co/watsch?v=threespeak/iaarkpvf
## twitch direct html iframe markup
It's broken, because we don't set parameter `parent` in url query. Iframe's attribute `src` should be something like `https://player.twitch.tv/?<channel, video, or collection>&parent=streamernews.example.com`. From their docs: "parent string (required) Domain(s) that will be embedding Twitch. You must have one parent key for each domain your site uses."
<iframe src="https://player.twitch.tv/?channel=tfue" frameborder="0" allowfullscreen="true" scrolling="no" height="378" width="620"></iframe>
## twitter
https://twitter.com/missybahia/status/1281295770298318849
## vimeo direct html iframe markup
<iframe src="https://player.vimeo.com/video/179213493"></iframe>
## vimeo iframe embedded from link
https://player.vimeo.com/video/179213493
## youtube direct html iframe markup
<iframe src="https://www.youtube.com/embed/KOnk7Nbqkhs" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
## youtube iframe embedded from link
https://www.youtube.com/embed/KOnk7Nbqkhs
import {
genIframeMd as genDtubeIframeMd,
validateIframeUrl as validateDtubeIframeUrl,
normalizeEmbedUrl as normalizeDtubeEmbedUrl,
embedNode as embedDtubeNode,
sandboxConfig as sandboxConfigDtube,
} from 'app/components/elements/EmbeddedPlayers/dtube';
import _ from 'lodash';
import * as archiveorg from 'app/components/elements/EmbeddedPlayers/archiveorg';
import * as bandcamp from 'app/components/elements/EmbeddedPlayers/bandcamp';
import * as dapplr from 'app/components/elements/EmbeddedPlayers/dapplr';
import * as dtube from 'app/components/elements/EmbeddedPlayers/dtube';
import * as mixcloud from 'app/components/elements/EmbeddedPlayers/mixcloud';
import * as soundcloud from 'app/components/elements/EmbeddedPlayers/soundcloud';
import * as spotify from 'app/components/elements/EmbeddedPlayers/spotify';
import * as threespeak from 'app/components/elements/EmbeddedPlayers/threespeak';
import * as twitch from 'app/components/elements/EmbeddedPlayers/twitch';
import * as twitter from 'app/components/elements/EmbeddedPlayers/twitter';
import * as vimeo from 'app/components/elements/EmbeddedPlayers/vimeo';
import * as youtube from 'app/components/elements/EmbeddedPlayers/youtube';
const supportedProviders = {
archiveorg,
bandcamp,
dapplr,
dtube,
mixcloud,
soundcloud,
spotify,
threespeak,
twitch,
twitter,
vimeo,
youtube,
};
import {
genIframeMd as genTwitchIframeMd,
validateIframeUrl as validateTwitchIframeUrl,
normalizeEmbedUrl as normalizeTwitchEmbedUrl,
embedNode as embedTwitchNode,
sandboxConfig as sandboxConfigTwitch,
} from 'app/components/elements/EmbeddedPlayers/twitch';
import {
validateIframeUrl as validateSoundcloudIframeUrl,
sandboxConfig as sandboxConfigSoundcloud,
} from 'app/components/elements/EmbeddedPlayers/soundcloud';
import {
validateIframeUrl as validateSpotifyIframeUrl,
genIframeMd as genSpotifyIframeMd,
normalizeEmbedUrl as normalizeSpotifyEmbedUrl,
embedNode as embedSpotifyNode,
sandboxConfig as sandboxConfigSpotify,
} from 'app/components/elements/EmbeddedPlayers/spotify';
import {
validateIframeUrl as validateMixcloudIframeUrl,
genIframeMd as genMixcloudIframeMd,
normalizeEmbedUrl as normalizeMixcloudEmbedUrl,
embedNode as embedMixcloudNode,
getIframeDimensions as getMixcloudIframeDimensions,
sandboxConfig as sandboxConfigMixcloud,
} from 'app/components/elements/EmbeddedPlayers/mixcloud';
import {
genIframeMd as genYoutubeIframeMd,
validateIframeUrl as validateYoutubeIframeUrl,
normalizeEmbedUrl as normalizeYoutubeEmbedUrl,
embedNode as embedYoutubeNode,
sandboxConfig as sandboxConfigYoutube,
} from 'app/components/elements/EmbeddedPlayers/youtube';
import {
genIframeMd as genVimeoIframeMd,
validateIframeUrl as validateVimeoIframeUrl,
normalizeEmbedUrl as normalizeVimeoEmbedUrl,
embedNode as embedVimeoNode,
sandboxConfig as sandboxConfigVimeo,
} from 'app/components/elements/EmbeddedPlayers/vimeo';
import {
genIframeMd as genThreespeakIframeMd,
validateIframeUrl as validateThreespeakIframeUrl,
normalizeEmbedUrl as normalizeThreespeakEmbedUrl,
embedNode as embedThreeSpeakNode,
preprocessHtml as preprocess3SpeakHtml,
sandboxConfig as sandboxConfigThreespeak,
} from 'app/components/elements/EmbeddedPlayers/threespeak';
export default supportedProviders;
import {
genIframeMd as genTwitterIframeMd,
validateIframeUrl as validateTwitterIframeUrl,
normalizeEmbedUrl as normalizeTwitterEmbedUrl,
embedNode as embedTwitterNode,
preprocessHtml as preprocessTwitterHtml,
sandboxConfig as sandboxConfigTwitter,
} from 'app/components/elements/EmbeddedPlayers/twitter';
function callProviderMethod(provider, methodName, ...parms) {
const method = _.get(provider, methodName, null);
if (method && typeof method === 'function') {
return method(...parms);
}
import {
validateIframeUrl as validateDapplrVideoUrl,
sandboxConfig as sandboxConfigDapplr,
} from 'app/components/elements/EmbeddedPlayers/dapplr';
return null;
}
// Set only those attributes in `sandboxAttributes`, that are minimally
// required for a given provider.
......@@ -84,106 +47,15 @@ import {
// the sandbox attribute at all. Also note that the sandbox attribute
// is unsupported in Internet Explorer 9 and earlier.
// See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe.
const supportedProviders = [
{
id: 'dtube',
validateIframeUrlFn: validateDtubeIframeUrl,
normalizeEmbedUrlFn: normalizeDtubeEmbedUrl,
embedNodeFn: embedDtubeNode,
genIframeMdFn: genDtubeIframeMd,
getIframeDimensionsFn: null,
...sandboxConfigDtube,
},
{
id: 'twitch',
validateIframeUrlFn: validateTwitchIframeUrl,
normalizeEmbedUrlFn: normalizeTwitchEmbedUrl,
embedNodeFn: embedTwitchNode,
genIframeMdFn: genTwitchIframeMd,
getIframeDimensionsFn: null,
...sandboxConfigTwitch,
},
{
id: 'soundcloud',
validateIframeUrlFn: validateSoundcloudIframeUrl,
normalizeEmbedUrlFn: null,
embedNodeFn: null,
genIframeMdFn: null,
getIframeDimensionsFn: null,
...sandboxConfigSoundcloud,
},
{
id: 'spotify',
validateIframeUrlFn: validateSpotifyIframeUrl,
normalizeEmbedUrlFn: normalizeSpotifyEmbedUrl,
embedNodeFn: embedSpotifyNode,
genIframeMdFn: genSpotifyIframeMd,
getIframeDimensionsFn: null,
...sandboxConfigSpotify,
},
{
id: 'mixcloud',
validateIframeUrlFn: validateMixcloudIframeUrl,
normalizeEmbedUrlFn: normalizeMixcloudEmbedUrl,
embedNodeFn: embedMixcloudNode,
genIframeMdFn: genMixcloudIframeMd,
getIframeDimensionsFn: getMixcloudIframeDimensions,
...sandboxConfigMixcloud,
},
{
id: 'youtube',
validateIframeUrlFn: validateYoutubeIframeUrl,
normalizeEmbedUrlFn: normalizeYoutubeEmbedUrl,
embedNodeFn: embedYoutubeNode,
genIframeMdFn: genYoutubeIframeMd,
getIframeDimensionsFn: null,
...sandboxConfigYoutube,
},
{
id: 'vimeo',
validateIframeUrlFn: validateVimeoIframeUrl,
normalizeEmbedUrlFn: normalizeVimeoEmbedUrl,
embedNodeFn: embedVimeoNode,
genIframeMdFn: genVimeoIframeMd,
getIframeDimensionsFn: null,
...sandboxConfigVimeo,