cast info

This commit is contained in:
2026-01-15 07:07:00 +01:00
parent 0541b0b776
commit dac2b0e8dc
6 changed files with 104 additions and 6 deletions

18
cast-receiver/README.md Normal file
View File

@@ -0,0 +1,18 @@
# Radio Player - Custom Cast Receiver
This folder contains a minimal Google Cast Web Receiver that displays a purple gradient background, station artwork, title and subtitle. It accepts `customData` hints sent from the sender (your app) for `backgroundImage`, `backgroundGradient` and `appName`.
Hosting requirements
- The receiver must be served over HTTPS and be publicly accessible.
- Recommended: host under GitHub Pages (`gh-pages` branch or `/docs` folder) or any static host (Netlify, Vercel, S3 + CloudFront).
Registering with Google Cast Console
1. Go to the Cast SDK Developer Console and create a new Application.
2. Choose "Custom Receiver" and provide the public HTTPS URL to `index.html` (e.g. `https://example.com/cast-receiver/index.html`).
3. Note the generated Application ID.
Sender changes
- After obtaining the Application ID, update your sender (sidecar) to launch that app ID instead of the DefaultMediaReceiver. The sidecar already supports passing `metadata.appId` when launching.
Testing locally
- You can serve this folder locally during development, but Chromecast devices require public HTTPS endpoints to use a registered app.

23
cast-receiver/index.html Normal file
View File

@@ -0,0 +1,23 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Radio Player Receiver</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div id="bg" class="bg"></div>
<div id="app" class="app">
<div class="artwork"><img id="art" alt="Artwork"></div>
<div class="meta">
<div id="appName" class="app-name">Radio Player</div>
<h1 id="title">Radio Player</h1>
<h2 id="subtitle"></h2>
</div>
</div>
<script src="https://www.gstatic.com/cast/sdk/libs/caf_receiver/v3/cast_receiver_framework.js"></script>
<script src="receiver.js"></script>
</body>
</html>

50
cast-receiver/receiver.js Normal file
View File

@@ -0,0 +1,50 @@
// Minimal CAF receiver that applies customData theming and shows media metadata.
const context = cast.framework.CastReceiverContext.getInstance();
const playerManager = context.getPlayerManager();
function applyBranding(customData, metadata) {
try {
const bgEl = document.getElementById('bg');
const art = document.getElementById('art');
const title = document.getElementById('title');
const subtitle = document.getElementById('subtitle');
const appName = document.getElementById('appName');
if (customData) {
if (customData.backgroundImage) {
bgEl.style.backgroundImage = `url(${customData.backgroundImage})`;
bgEl.style.backgroundSize = 'cover';
bgEl.style.backgroundPosition = 'center';
} else if (customData.backgroundGradient) {
bgEl.style.background = customData.backgroundGradient;
}
if (customData.appName) appName.textContent = customData.appName;
}
if (metadata) {
if (metadata.title) title.textContent = metadata.title;
const sub = metadata.subtitle || metadata.artist || '';
subtitle.textContent = sub;
if (metadata.images && metadata.images.length) {
art.src = metadata.images[0].url || '';
}
}
} catch (e) {
// swallow UI errors
console.warn('Branding apply failed', e);
}
}
playerManager.setMessageInterceptor(cast.framework.messages.MessageType.LOAD, (request) => {
const media = request.media || {};
const customData = media.customData || {};
applyBranding(customData, media.metadata || {});
return request;
});
playerManager.addEventListener(cast.framework.events.EventType.MEDIA_STATUS, () => {
const media = playerManager.getMediaInformation();
if (media) applyBranding(media.customData || {}, media.metadata || {});
});
context.start();

11
cast-receiver/styles.css Normal file
View File

@@ -0,0 +1,11 @@
:root{--primary:#6a0dad;--accent:#b36cf3}
html,body{height:100%;margin:0;font-family:Inter,system-ui,Arial,Helvetica,sans-serif}
body{background:linear-gradient(135deg,var(--primary),var(--accent));color:#fff}
.bg{position:fixed;inset:0;background-size:cover;background-position:center;filter:blur(10px) saturate(120%);opacity:0.9}
.app{position:relative;z-index:2;display:flex;align-items:center;gap:24px;padding:48px}
.artwork{width:320px;height:320px;flex:0 0 320px;background:rgba(255,255,255,0.06);display:flex;align-items:center;justify-content:center;border-radius:8px;overflow:hidden}
.artwork img{width:100%;height:100%;object-fit:cover}
.meta{display:flex;flex-direction:column}
.app-name{font-weight:600;opacity:0.9}
h1{margin:6px 0 0 0;font-size:28px}
h2{margin:6px 0 0 0;font-size:18px;opacity:0.9}