3rd party embed player (#2217)
* Implement embed player for YT, spotify, and twitch * fix: handle blur event * fix: use video dimensions for twitch * fix: remove hack (?) * fix: remove origin whitelist (?) * fix: prevent ads from opening in browser * fix: handle embeds that don't have a thumb * feat: handle dark/light mode * fix: ts warning * fix: adjust height of no-thumb label * fix: adjust height of no-thumb label * fix: remove debug log, set collapsable to false for player view * fix: fix dimensions "flash" * chore: remove old youtube link test * tests: add tests * fix: thumbless embed position when loading * fix: remove background from webview * cleanup embeds (almost) * more refactoring - Use separate layers for player and overlay to prevent weird sizing issues - Be sure the image is not visible under the player - Clean up some * cleanup styles * parse youtube shorts urls * remove debug * add soundcloud tracks and sets (playlists) * move logic into `ExternalLinkEmbed` * border radius for yt player on native * fix styling on web * allow scrolling in webview on android * remove unnecessary check * autoplay yt on web * fix tests after adding autoplay * move `useNavigation` to top of component --------- Co-authored-by: Paul Frazee <pfrazee@gmail.com>
This commit is contained in:
parent
7ab188dc1f
commit
fedb94dd70
12 changed files with 597 additions and 135 deletions
147
src/lib/strings/embed-player.ts
Normal file
147
src/lib/strings/embed-player.ts
Normal file
|
@ -0,0 +1,147 @@
|
|||
export type EmbedPlayerParams =
|
||||
| {type: 'youtube_video'; videoId: string; playerUri: string}
|
||||
| {type: 'twitch_live'; channelId: string; playerUri: string}
|
||||
| {type: 'spotify_album'; albumId: string; playerUri: string}
|
||||
| {
|
||||
type: 'spotify_playlist'
|
||||
playlistId: string
|
||||
playerUri: string
|
||||
}
|
||||
| {type: 'spotify_song'; songId: string; playerUri: string}
|
||||
| {type: 'soundcloud_track'; user: string; track: string; playerUri: string}
|
||||
| {type: 'soundcloud_set'; user: string; set: string; playerUri: string}
|
||||
|
||||
export function parseEmbedPlayerFromUrl(
|
||||
url: string,
|
||||
): EmbedPlayerParams | undefined {
|
||||
let urlp
|
||||
try {
|
||||
urlp = new URL(url)
|
||||
} catch (e) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
// youtube
|
||||
if (urlp.hostname === 'youtu.be') {
|
||||
const videoId = urlp.pathname.split('/')[1]
|
||||
if (videoId) {
|
||||
return {
|
||||
type: 'youtube_video',
|
||||
videoId,
|
||||
playerUri: `https://www.youtube.com/embed/${videoId}?autoplay=1`,
|
||||
}
|
||||
}
|
||||
}
|
||||
if (urlp.hostname === 'www.youtube.com' || urlp.hostname === 'youtube.com') {
|
||||
const [_, page, shortVideoId] = urlp.pathname.split('/')
|
||||
const videoId =
|
||||
page === 'shorts' ? shortVideoId : (urlp.searchParams.get('v') as string)
|
||||
|
||||
if (videoId) {
|
||||
return {
|
||||
type: 'youtube_video',
|
||||
videoId,
|
||||
playerUri: `https://www.youtube.com/embed/${videoId}?autoplay=1`,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// twitch
|
||||
if (urlp.hostname === 'twitch.tv' || urlp.hostname === 'www.twitch.tv') {
|
||||
const parts = urlp.pathname.split('/')
|
||||
if (parts.length === 2 && parts[1]) {
|
||||
return {
|
||||
type: 'twitch_live',
|
||||
channelId: parts[1],
|
||||
playerUri: `https://player.twitch.tv/?volume=0.5&!muted&autoplay&channel=${parts[1]}&parent=localhost`,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// spotify
|
||||
if (urlp.hostname === 'open.spotify.com') {
|
||||
const [_, type, id] = urlp.pathname.split('/')
|
||||
if (type && id) {
|
||||
if (type === 'playlist') {
|
||||
return {
|
||||
type: 'spotify_playlist',
|
||||
playlistId: id,
|
||||
playerUri: `https://open.spotify.com/embed/playlist/${id}`,
|
||||
}
|
||||
}
|
||||
if (type === 'album') {
|
||||
return {
|
||||
type: 'spotify_album',
|
||||
albumId: id,
|
||||
playerUri: `https://open.spotify.com/embed/album/${id}`,
|
||||
}
|
||||
}
|
||||
if (type === 'track') {
|
||||
return {
|
||||
type: 'spotify_song',
|
||||
songId: id,
|
||||
playerUri: `https://open.spotify.com/embed/track/${id}`,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// soundcloud
|
||||
if (
|
||||
urlp.hostname === 'soundcloud.com' ||
|
||||
urlp.hostname === 'www.soundcloud.com'
|
||||
) {
|
||||
const [_, user, trackOrSets, set] = urlp.pathname.split('/')
|
||||
|
||||
if (user && trackOrSets) {
|
||||
if (trackOrSets === 'sets' && set) {
|
||||
return {
|
||||
type: 'soundcloud_set',
|
||||
user,
|
||||
set: set,
|
||||
playerUri: `https://w.soundcloud.com/player/?url=${url}&auto_play=true&visual=false&hide_related=true`,
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'soundcloud_track',
|
||||
user,
|
||||
track: trackOrSets,
|
||||
playerUri: `https://w.soundcloud.com/player/?url=${url}&auto_play=true&visual=false&hide_related=true`,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function getPlayerHeight({
|
||||
type,
|
||||
width,
|
||||
hasThumb,
|
||||
}: {
|
||||
type: EmbedPlayerParams['type']
|
||||
width: number
|
||||
hasThumb: boolean
|
||||
}) {
|
||||
if (!hasThumb) return (width / 16) * 9
|
||||
|
||||
switch (type) {
|
||||
case 'youtube_video':
|
||||
case 'twitch_live':
|
||||
return (width / 16) * 9
|
||||
case 'spotify_album':
|
||||
return 380
|
||||
case 'spotify_playlist':
|
||||
return 360
|
||||
case 'spotify_song':
|
||||
if (width <= 300) {
|
||||
return 180
|
||||
}
|
||||
return 232
|
||||
case 'soundcloud_track':
|
||||
return 165
|
||||
case 'soundcloud_set':
|
||||
return 360
|
||||
default:
|
||||
return width
|
||||
}
|
||||
}
|
|
@ -139,35 +139,6 @@ export function feedUriToHref(url: string): string {
|
|||
}
|
||||
}
|
||||
|
||||
export function getYoutubeVideoId(link: string): string | undefined {
|
||||
let url
|
||||
try {
|
||||
url = new URL(link)
|
||||
} catch (e) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
if (
|
||||
url.hostname !== 'www.youtube.com' &&
|
||||
url.hostname !== 'youtube.com' &&
|
||||
url.hostname !== 'youtu.be'
|
||||
) {
|
||||
return undefined
|
||||
}
|
||||
if (url.hostname === 'youtu.be') {
|
||||
const videoId = url.pathname.split('/')[1]
|
||||
if (!videoId) {
|
||||
return undefined
|
||||
}
|
||||
return videoId
|
||||
}
|
||||
const videoId = url.searchParams.get('v') as string
|
||||
if (!videoId) {
|
||||
return undefined
|
||||
}
|
||||
return videoId
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the label in the post text matches the host of the link facet.
|
||||
*
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue