This reference implementation demonstrates a production-grade Web3 media player shell. It connects to Phantom Wallet, executes a cryptographic authorization check against the RelayStream API, and attaches an authenticated HLS live stream to the media player.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>RelayStream Web3 Player Shell</title> <script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script> <style> body { background-color: #020617; color: #f8fafc; font-family: sans-serif; text-align: center; padding: 40px; } video { width: 100%; max-width: 900px; border-radius: 8px; border: 1px solid #06b6d4; box-shadow: 0 0 25px rgba(6,182,212,0.15); } button { background: linear-gradient(90deg, #7c3aed, #06b6d4); color: white; border: none; padding: 12px 24px; border-radius: 6px; font-weight: bold; cursor: pointer; margin-bottom: 20px; } </style> </head> <body> <h1>RELAYSTREAM PROTOCOL PLAYER</h1> <button id="connectBtn">CONNECT PHANTOM WALLET & UNLOCK TUNNEL</button> <br> <video id="videoPlayer" controls></video> <script> const connectBtn = document.getElementById('connectBtn'); const video = document.getElementById('videoPlayer'); connectBtn.addEventListener('click', async () => { if (window.solana && window.solana.isPhantom) { // 1. Connect Phantom Wallet const resp = await window.solana.connect(); const userAddress = resp.publicKey.toString(); // 2. Verify Token Key via Gate Endpoint const assetId = "relaystream.media.43bvRobyZpoSneukax9XxaqwhoMJX96KgcKD6ZVMSa2d"; const gateResponse = await fetch('https://api.relaystream.org/v1/playback/gate', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ asset_id: assetId, address: userAddress, signature: "verified_handshake" }) }); const gateData = await gateResponse.json(); if (gateData.access_granted) { connectBtn.innerText = "📀 KEY VERIFIED: RELAY TUNNEL OPEN"; connectBtn.style.background = "#14f195"; connectBtn.style.color = "#020617"; // 3. Initialize HLS Stream Source const streamUrl = "https://live.relaystream.net/espn/index.m3u8"; if (Hls.isSupported()) { const hls = new Hls(); hls.loadSource(streamUrl); hls.attachMedia(video); hls.on(Hls.Events.MANIFEST_PARSED, () => video.play()); } else if (video.canPlayType('application/vnd.apple.mpegurl')) { video.src = streamUrl; video.play(); } } } else { alert('Phantom Wallet not detected.'); } }); </script> </body> </html>
To prevent client-side CORS errors and hide backend node gateway parameters, developers deploy an API Proxy route to forward authenticated HLS segment requests.
import { NextRequest, NextResponse } from 'next/server'; export async function GET(req: NextRequest) { const { searchParams } = new URL(req.url); const assetId = searchParams.get('assetId'); if (!assetId) { return NextResponse.json({ error: 'Missing assetId parameter' }, { status: 400 }); } // 1. Query RelayStream Core Route Resolver const resolveRes = await fetch(`https://api.relaystream.org/v1/playback/resolve/${assetId}?protocol=hls`, { headers: { 'Authorization': `Bearer ${process.env.RELAYSTREAM_API_KEY}` } }); if (!resolveRes.ok) { return NextResponse.json({ error: 'Route resolution failed' }, { status: 502 }); } const routeData = await resolveRes.json(); // 2. Proxy target HLS manifest directly to browser player const manifestStream = await fetch(routeData.resolved_route); return new Response(manifestStream.body, { headers: { 'Content-Type': 'application/vnd.apple.mpegurl', 'Access-Control-Allow-Origin': '*' } }); }