feat(embeds): inline YouTube/Vimeo players + media-forward video tiles
Video link tiles (YouTube, Shorts, Vimeo) now play in place instead of only opening a browser tab. Adds a media-forward 16:9 (9:16 for Shorts) tile with a privacy-friendly click-to-play facade: the homeserver's cached og:image thumbnail + a play button, and only on click does it swap in the cookie-less youtube-nocookie / player.vimeo iframe — so nothing loads from Google/Vimeo until the user presses play. Gated by a new 'Inline Media Players' setting (default on); when off it falls back to a link that opens the video in a new tab. Also sources YouTube thumbnails from the homeserver og:image instead of img.youtube.com, which fixes the existing broken YouTube thumbnails on the web build (nginx img-src has no YouTube host) and removes the pre-click Google request. Pure URL parsing + embed-URL building moved to utils/videoEmbed.ts (unit-tested). Note: the desktop app's Tauri CSP frame-src must allow the video hosts (separate commit in cinny-desktop). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -197,6 +197,80 @@ export const MediaPlayButton = style([
|
||||
},
|
||||
]);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Inline video embed (YouTube / Vimeo) — media-forward click-to-play facade
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export const EmbedMediaLandscape = style([
|
||||
DefaultReset,
|
||||
{
|
||||
position: 'relative',
|
||||
width: '100%',
|
||||
aspectRatio: '16 / 9',
|
||||
overflow: 'hidden',
|
||||
backgroundColor: color.Surface.Container,
|
||||
},
|
||||
]);
|
||||
|
||||
export const EmbedMediaPortrait = style([
|
||||
DefaultReset,
|
||||
{
|
||||
position: 'relative',
|
||||
width: toRem(220),
|
||||
maxWidth: '100%',
|
||||
aspectRatio: '9 / 16',
|
||||
margin: '0 auto',
|
||||
overflow: 'hidden',
|
||||
backgroundColor: color.Surface.Container,
|
||||
},
|
||||
]);
|
||||
|
||||
// Fills the aspect box; used for both the <button> facade and the <a> fallback.
|
||||
export const EmbedFacade = style([
|
||||
DefaultReset,
|
||||
{
|
||||
position: 'absolute',
|
||||
inset: 0,
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
display: 'block',
|
||||
padding: 0,
|
||||
border: 'none',
|
||||
background: 'none',
|
||||
cursor: 'pointer',
|
||||
|
||||
':hover': {
|
||||
filter: 'brightness(0.85)',
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
export const EmbedIframe = style([
|
||||
DefaultReset,
|
||||
{
|
||||
position: 'absolute',
|
||||
inset: 0,
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
border: 0,
|
||||
display: 'block',
|
||||
},
|
||||
]);
|
||||
|
||||
export const EmbedPlaceholder = style([
|
||||
DefaultReset,
|
||||
{
|
||||
position: 'absolute',
|
||||
inset: 0,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: '#111111',
|
||||
color: '#ffffff',
|
||||
fontSize: toRem(28),
|
||||
},
|
||||
]);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Shared square artwork thumbnail (Spotify, IMDb poster, Discord icon)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user