Bandcamp embed (#9445)

This commit is contained in:
Samuel Newman
2026-02-13 13:30:10 +00:00
committed by GitHub
parent 795428971d
commit c83dddce57
4 changed files with 67 additions and 14 deletions
+24
View File
@@ -437,6 +437,13 @@ describe('parseEmbedPlayerFromUrl', () => {
'https://www.flickr.com/groups/898944@N23/',
'https://www.flickr.com/groups',
'https://maxblansjaar.bandcamp.com/album/false-comforts',
'https://grmnygrmny.bandcamp.com/track/fluid',
'https://sufjanstevens.bandcamp.com/',
'https://sufjanstevens.bandcamp.com',
'https://bandcamp.com/',
'https://bandcamp.com',
]
const outputs = [
@@ -815,6 +822,23 @@ describe('parseEmbedPlayerFromUrl', () => {
undefined,
undefined,
{
type: 'bandcamp_album',
source: 'bandcamp',
playerUri:
'https://bandcamp.com/EmbeddedPlayer/url=https%3A%2F%2Fmaxblansjaar.bandcamp.com%2Falbum%2Ffalse-comforts/size=large/bgcol=ffffff/linkcol=0687f5/minimal=true/transparent=true/',
},
{
type: 'bandcamp_track',
source: 'bandcamp',
playerUri:
'https://bandcamp.com/EmbeddedPlayer/url=https%3A%2F%2Fgrmnygrmny.bandcamp.com%2Ftrack%2Ffluid/size=large/bgcol=ffffff/linkcol=0687f5/minimal=true/transparent=true/',
},
undefined,
undefined,
undefined,
undefined,
]
it('correctly grabs the correct id from uri', () => {
+9 -14
View File
@@ -9,7 +9,8 @@ import {
externalEmbedLabels,
} from '#/lib/strings/embed-player'
import {useSetExternalEmbedPref} from '#/state/preferences'
import {atoms as a, useBreakpoints, useTheme} from '#/alf'
import {atoms as a, web} from '#/alf'
import {Admonition} from '#/components/Admonition'
import {Button, ButtonText} from '#/components/Button'
import * as Dialog from '#/components/Dialog'
import {Text} from '#/components/Typography'
@@ -24,9 +25,7 @@ export function EmbedConsentDialog({
onAccept: () => void
}) {
const {_} = useLingui()
const t = useTheme()
const setExternalEmbedPref = useSetExternalEmbedPref()
const {gtMobile} = useBreakpoints()
const onShowAllPress = useCallback(() => {
for (const key of embedPlayerSources) {
@@ -52,50 +51,46 @@ export function EmbedConsentDialog({
<Dialog.Handle />
<Dialog.ScrollableInner
label={_(msg`External Media`)}
style={[gtMobile ? {width: 'auto', maxWidth: 400} : a.w_full]}>
style={web({maxWidth: 400})}>
<View style={a.gap_sm}>
<Text style={[a.text_2xl, a.font_semi_bold]}>
<Text style={[a.text_2xl, a.font_bold]}>
<Trans>External Media</Trans>
</Text>
<View style={[a.mt_sm, a.mb_2xl, a.gap_lg]}>
<Text>
<Text style={[a.text_md, a.leading_snug]}>
<Trans>
This content is hosted by {externalEmbedLabels[source]}. Do you
want to enable external media?
</Trans>
</Text>
<Text style={t.atoms.text_contrast_medium}>
<Admonition type="info">
<Trans>
External media may allow websites to collect information about
you and your device. No information is sent or requested until
you press the "play" button.
</Trans>
</Text>
</Admonition>
</View>
</View>
<View style={a.gap_md}>
<Button
style={gtMobile && a.flex_1}
label={_(msg`Enable external media`)}
onPress={onShowAllPress}
onAccessibilityEscape={control.close}
color="primary"
size="large"
variant="solid">
size="large">
<ButtonText>
<Trans>Enable external media</Trans>
</ButtonText>
</Button>
<Button
style={gtMobile && a.flex_1}
label={_(msg`Enable this source only`)}
onPress={onShowPress}
onAccessibilityEscape={control.close}
color="secondary"
size="large"
variant="solid">
size="large">
<ButtonText>
<Trans>Enable {externalEmbedLabels[source]} only</Trans>
</ButtonText>
+33
View File
@@ -24,6 +24,7 @@ export const embedPlayerSources = [
'giphy',
'tenor',
'flickr',
'bandcamp',
] as const
export type EmbedPlayerSource = (typeof embedPlayerSources)[number]
@@ -44,6 +45,8 @@ export type EmbedPlayerType =
| 'giphy_gif'
| 'tenor_gif'
| 'flickr_album'
| 'bandcamp_album'
| 'bandcamp_track'
export const externalEmbedLabels: Record<EmbedPlayerSource, string> = {
youtube: 'YouTube',
@@ -56,6 +59,7 @@ export const externalEmbedLabels: Record<EmbedPlayerSource, string> = {
appleMusic: 'Apple Music',
soundcloud: 'SoundCloud',
flickr: 'Flickr',
bandcamp: 'Bandcamp',
}
export interface EmbedPlayerParams {
@@ -459,6 +463,32 @@ export function parseEmbedPlayerFromUrl(
return undefined
}
}
const bandcampRegex = /^[a-z\d][a-z\d-]{2,}[a-z\d]\.bandcamp\.com$/i
if (bandcampRegex.test(urlp.hostname)) {
const pathComponents = urlp.pathname.split('/')
switch (pathComponents[1]) {
case 'album':
return {
type: 'bandcamp_album',
source: 'bandcamp',
playerUri: `https://bandcamp.com/EmbeddedPlayer/url=${encodeURIComponent(
urlp.href,
)}/size=large/bgcol=ffffff/linkcol=0687f5/minimal=true/transparent=true/`,
}
case 'track':
return {
type: 'bandcamp_track',
source: 'bandcamp',
playerUri: `https://bandcamp.com/EmbeddedPlayer/url=${encodeURIComponent(
urlp.href,
)}/size=large/bgcol=ffffff/linkcol=0687f5/minimal=true/transparent=true/`,
}
default:
return undefined
}
}
}
export function getPlayerAspect({
@@ -498,6 +528,9 @@ export function getPlayerAspect({
return {height: 165}
case 'apple_music_song':
return {height: 150}
case 'bandcamp_album':
case 'bandcamp_track':
return {aspectRatio: 1}
default:
return {aspectRatio: 16 / 9}
}
+1
View File
@@ -106,6 +106,7 @@ const schema = z.object({
appleMusic: z.enum(externalEmbedOptions).optional(),
soundcloud: z.enum(externalEmbedOptions).optional(),
flickr: z.enum(externalEmbedOptions).optional(),
bandcamp: z.enum(externalEmbedOptions).optional(),
})
.optional(),
invites: z.object({