feat(embeds): interactive X/Twitter post embed + Apple Music player
X/Twitter: the static OG card was a snapshot — no video, no galleries/threads. Keep it as the (reliable) facade and add a 'View post' button that loads the official platform.twitter.com interactive embed on click: playable video/GIF, image galleries, quote tweets. The embed self-sizes via a scoped postMessage resize listener (matched to our iframe + the platform.twitter.com origin). Nothing loads from X until the user clicks, and the link still works if X blocks the frame. Apple Music: music.apple.com album/playlist/song links now play inline via the embed.music.apple.com player (compact for a single song, tall for collections), through the existing MediaEmbedCard audio path. Pure parsers/builders unit-tested (13 cases). Desktop CSP frame-src adds platform.twitter.com + embed.music.apple.com (separate commit). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -11,6 +11,8 @@ import {
|
||||
getTwitchTarget,
|
||||
getSpotifyEmbedTarget,
|
||||
isSoundCloudTrack,
|
||||
getAppleMusicEmbed,
|
||||
getTweetId,
|
||||
buildVideoEmbedUrl,
|
||||
spotifyEmbedHeight,
|
||||
parseMediaEmbed,
|
||||
@@ -109,6 +111,31 @@ test('parseMediaEmbed: routes provider + kind, builds embed URLs', () => {
|
||||
assert.equal(parseMediaEmbed('https://example.com/x', HOST), null);
|
||||
});
|
||||
|
||||
test('Apple Music: album vs single song height, embed host swap', () => {
|
||||
const album = getAppleMusicEmbed('https://music.apple.com/us/album/some-album/123456');
|
||||
assert.equal(album?.embedUrl, 'https://embed.music.apple.com/us/album/some-album/123456');
|
||||
assert.equal(album?.height, 450);
|
||||
const song = getAppleMusicEmbed('https://music.apple.com/us/album/some-album/123456?i=789');
|
||||
assert.equal(song?.height, 175);
|
||||
assert.ok(song?.embedUrl.includes('?i=789'));
|
||||
assert.equal(getAppleMusicEmbed('https://music.apple.com/us/browse'), null);
|
||||
assert.equal(getAppleMusicEmbed('https://example.com/album/x/1'), null);
|
||||
});
|
||||
|
||||
test('getTweetId', () => {
|
||||
assert.equal(getTweetId('https://x.com/user/status/1799999999999999999'), '1799999999999999999');
|
||||
assert.equal(getTweetId('https://twitter.com/user/status/12345'), '12345');
|
||||
assert.equal(getTweetId('https://x.com/user'), null);
|
||||
assert.equal(getTweetId('https://youtube.com/watch?v=x'), null);
|
||||
});
|
||||
|
||||
test('parseMediaEmbed: Apple Music → audio', () => {
|
||||
const m = parseMediaEmbed('https://music.apple.com/us/album/x/1?i=2', 'chat.lotusguild.org');
|
||||
assert.equal(m?.provider, 'applemusic');
|
||||
assert.equal(m?.kind, 'audio');
|
||||
assert.equal(m?.height, 175);
|
||||
});
|
||||
|
||||
test('parseMediaEmbed: Twitch embed carries the parent host', () => {
|
||||
const chan = parseMediaEmbed('https://twitch.tv/streamer', HOST);
|
||||
assert.equal(chan?.provider, 'twitch');
|
||||
|
||||
Reference in New Issue
Block a user