feat(embeds): Vimeo live events play inline (#200)
CI / Build & Quality Checks (pull_request) Successful in 4m38s
CI / Trigger Desktop Build (pull_request) Skipped
CI / Docker image build & smoke test (pull_request) Skipped
CI / Secret scan (gitleaks) (pull_request) Successful in 17s
CI / Playwright smoke (e2e) (pull_request) Successful in 24m27s
CI / Build & Quality Checks (pull_request) Successful in 4m38s
CI / Trigger Desktop Build (pull_request) Skipped
CI / Docker image build & smoke test (pull_request) Skipped
CI / Secret scan (gitleaks) (pull_request) Successful in 17s
CI / Playwright smoke (e2e) (pull_request) Successful in 24m27s
vimeo.com/event/{id} (and /event/{id}/{hash} for unlisted events) now get
the same click-to-play card as Vimeo videos, embedding Vimeo's live-event
player (vimeo.com/event/{id}/embed[/{hash}], dnt=1). The issue assumed
this needed vimeo.com in the CSP, but frame-src is already https: on web
and desktop.
Refs #200
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
878a34712a
commit
ed5bf997b4
@@ -33,6 +33,7 @@ import {
|
||||
spotifyEmbedHeight,
|
||||
parseMediaEmbed,
|
||||
isTwitterNoResults,
|
||||
getVimeoEventParts,
|
||||
} from './videoEmbed';
|
||||
|
||||
const HOST = 'chat.lotusguild.org';
|
||||
@@ -429,3 +430,34 @@ test('isTwitterNoResults: ignores normal embed traffic and junk', () => {
|
||||
assert.equal(isTwitterNoResults(null), false);
|
||||
assert.equal(isTwitterNoResults({ type: 'MEASURE' }), false);
|
||||
});
|
||||
|
||||
test('[#200] Vimeo live events embed from vimeo.com/event/{id}/embed', () => {
|
||||
assert.deepEqual(getVimeoEventParts('https://vimeo.com/event/4321567'), {
|
||||
id: '4321567',
|
||||
hash: undefined,
|
||||
});
|
||||
assert.deepEqual(getVimeoEventParts('https://vimeo.com/event/4321567/a1b2c3d4e5'), {
|
||||
id: '4321567',
|
||||
hash: 'a1b2c3d4e5',
|
||||
});
|
||||
assert.equal(
|
||||
getVimeoEventParts('https://vimeo.com/event/4321567/embed/a1b2c3d4e5')?.hash,
|
||||
'a1b2c3d4e5',
|
||||
);
|
||||
assert.equal(getVimeoEventParts('https://vimeo.com/event/4321567/chat'), null);
|
||||
assert.equal(getVimeoEventParts('https://vimeo.com/123456789'), null);
|
||||
assert.equal(getVimeoEventParts('https://evil.example/event/4321567'), null);
|
||||
assert.equal(
|
||||
parseMediaEmbed('https://vimeo.com/event/4321567', 'h')?.embedUrl,
|
||||
'https://vimeo.com/event/4321567/embed?autoplay=1&dnt=1',
|
||||
);
|
||||
assert.equal(
|
||||
parseMediaEmbed('https://vimeo.com/event/4321567/a1b2c3d4e5', 'h')?.embedUrl,
|
||||
'https://vimeo.com/event/4321567/embed/a1b2c3d4e5?autoplay=1&dnt=1',
|
||||
);
|
||||
// An ordinary video still goes to the player.
|
||||
assert.match(
|
||||
parseMediaEmbed('https://vimeo.com/123456789', 'h')?.embedUrl ?? '',
|
||||
/^https:\/\/player\.vimeo\.com\/video\/123456789/,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -80,6 +80,25 @@ export function getVimeoParts(url: string): { id: string; hash?: string } | null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* [Gitea #200] Vimeo live events: vimeo.com/event/{id}, or /event/{id}/{hash}
|
||||
* when unlisted. Embedded from vimeo.com itself (not player.vimeo.com); the
|
||||
* web and desktop CSP `frame-src https:` already allow it.
|
||||
*/
|
||||
export function getVimeoEventParts(url: string): { id: string; hash?: string } | null {
|
||||
try {
|
||||
const { hostname, pathname } = new URL(url);
|
||||
if (hostname !== 'vimeo.com' && hostname !== 'www.vimeo.com') return null;
|
||||
const m = pathname.match(/^\/event\/(\d+)(?:\/(?:embed\/)?([0-9a-f]{6,}))?\/?$/);
|
||||
return m ? { id: m[1], hash: m[2] } : null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export const buildVimeoEventEmbedUrl = (id: string, hash?: string): string =>
|
||||
`https://vimeo.com/event/${enc(id)}/embed${hash ? `/${enc(hash)}` : ''}?autoplay=1&dnt=1`;
|
||||
|
||||
export function getVimeoVideoId(url: string): string | null {
|
||||
return getVimeoParts(url)?.id ?? null;
|
||||
}
|
||||
@@ -713,6 +732,14 @@ export function parseMediaEmbed(url: string, host: string): MediaEmbed | null {
|
||||
embedUrl: buildVideoEmbedUrl('youtube', ytId),
|
||||
};
|
||||
|
||||
const vimeoEvent = getVimeoEventParts(url);
|
||||
if (vimeoEvent)
|
||||
return {
|
||||
provider: 'vimeo',
|
||||
kind: 'landscape',
|
||||
embedUrl: buildVimeoEventEmbedUrl(vimeoEvent.id, vimeoEvent.hash),
|
||||
};
|
||||
|
||||
const vimeo = getVimeoParts(url);
|
||||
if (vimeo)
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user