fix(embeds): parsing over/under-match + broken thumbnails + wide layout
Bugs found by a 3-agent audit of the inline-embed system (core posture — sandbox, postMessage origin+source, XSS, noreferrer, oEmbed — verified sound); fixes reviewed by 2 agents on the staged diff (both SHIP). Parsing (videoEmbed.ts, + tests): - Twitch/Kick/SoundCloud/Streamable reserved-path exclusion — their own utility pages (twitch.tv/directory, kick.com/browse, soundcloud.com/discover/…, streamable.com/login, bare /videos) no longer render as broken player embeds. - SoundCloud: `/<artist>/<tab>` profile-tab listings excluded; `/<artist>/sets/<slug>` real sets still detected. - Vimeo: unlisted-hash capture constrained to lowercase-hex, so a normal video's trailing segment (/likes, /settings, a slug) isn't captured as a bogus `h=` param that Vimeo then rejects. Rendering (UrlPreviewCard.tsx, RenderMessageContent.tsx): - Spotify/Steam/Discord/IMDb route og:image through mxcUrlToHttp like every other card — a raw og:image is an mxc:// URI (broken <img> on standard Synapse) or an off-homeserver request that defeats the click-to-play facade. - `wide` card class now follows the RESOLVED embed (incl. the og:url short-link fallback), so an og:url-resolved player gets the wide layout, not a cramped one. - Twitter host detection (isTwitter/isTwitterTweet) aligned with getTweetId — mobile.twitter.com and legacy /statuses/ now route to the Twitter card/embed. - De-dupe preview URLs so a message repeating a link doesn't render sibling cards with identical React keys. Gates: tsc 0, eslint 0, prettier clean, 910 tests, build ok. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -57,6 +57,10 @@ test('Vimeo (incl. unlisted hash + channel/group/album forms)', () => {
|
||||
assert.equal(getVimeoParts('https://vimeo.com/channels/staffpicks/76979871')?.id, '76979871');
|
||||
assert.equal(getVimeoParts('https://vimeo.com/groups/motion/videos/12345')?.id, '12345');
|
||||
assert.equal(getVimeoParts('https://vimeo.com/album/99/video/54321')?.id, '54321');
|
||||
// a normal video with a trailing sub-path segment must NOT capture it as a hash
|
||||
assert.equal(getVimeoParts('https://vimeo.com/123456789/likes')?.hash, undefined);
|
||||
assert.equal(getVimeoParts('https://vimeo.com/123456789/settings')?.hash, undefined);
|
||||
assert.equal(getVimeoParts('https://vimeo.com/123456789/likes')?.id, '123456789');
|
||||
});
|
||||
|
||||
test('extractEmbedHeight: Instagram / Reddit / Twitter shapes', () => {
|
||||
@@ -113,6 +117,7 @@ test('Dailymotion + Streamable', () => {
|
||||
assert.equal(getDailymotionId('https://dai.ly/x8abcde'), 'x8abcde');
|
||||
assert.equal(getStreamableId('https://streamable.com/abc12'), 'abc12');
|
||||
assert.equal(getStreamableId('https://streamable.com/e/abc12'), null); // already an embed path
|
||||
assert.equal(getStreamableId('https://streamable.com/login'), null); // reserved page
|
||||
});
|
||||
|
||||
test('Twitch: channel / video / clip', () => {
|
||||
@@ -132,6 +137,10 @@ test('Twitch: channel / video / clip', () => {
|
||||
type: 'clip',
|
||||
value: 'CoolSlug',
|
||||
});
|
||||
// reserved utility pages are not channels
|
||||
assert.equal(getTwitchTarget('https://twitch.tv/directory'), null);
|
||||
assert.equal(getTwitchTarget('https://twitch.tv/settings'), null);
|
||||
assert.equal(getTwitchTarget('https://twitch.tv/videos'), null); // bare /videos, not a channel
|
||||
});
|
||||
|
||||
test('Spotify target + height', () => {
|
||||
@@ -153,6 +162,9 @@ test('SoundCloud track detection', () => {
|
||||
assert.equal(isSoundCloudTrack('https://soundcloud.com/artist'), false); // bare profile
|
||||
// on.soundcloud.com short links intentionally not handled (need oEmbed resolve)
|
||||
assert.equal(isSoundCloudTrack('https://on.soundcloud.com/abc123'), false);
|
||||
assert.equal(isSoundCloudTrack('https://soundcloud.com/discover/xyz'), false); // site section
|
||||
assert.equal(isSoundCloudTrack('https://soundcloud.com/artist/sets'), false); // profile-tab listing
|
||||
assert.equal(isSoundCloudTrack('https://soundcloud.com/artist/sets/my-set'), true); // a real set
|
||||
});
|
||||
|
||||
test('buildVideoEmbedUrl: cookie-less YouTube + Vimeo', () => {
|
||||
@@ -287,6 +299,8 @@ test('Bluesky / Loom / Kick', () => {
|
||||
|
||||
assert.equal(getKickChannel('https://kick.com/somestreamer'), 'somestreamer');
|
||||
assert.equal(getKickChannel('https://kick.com/streamer/videos/123'), null); // VOD → no embed
|
||||
assert.equal(getKickChannel('https://kick.com/browse'), null); // nav page, not a channel
|
||||
assert.equal(getKickChannel('https://kick.com/following'), null);
|
||||
assert.ok(
|
||||
parseMediaEmbed('https://kick.com/streamer', 'h')?.embedUrl.includes(
|
||||
'player.kick.com/streamer?autoplay=true',
|
||||
|
||||
Reference in New Issue
Block a user