frontend: add svelte frontend

This commit is contained in:
2025-07-20 23:51:54 -06:00
parent a01ec82833
commit 7a2b0fd4d6
40 changed files with 17358 additions and 349 deletions
@@ -1,125 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Game Streaming App</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 20px;
background-color: #1a1a1a;
color: white;
}
.apps-container {
display: flex;
flex-wrap: wrap;
gap: 20px;
padding: 20px 0;
}
.app-box {
position: relative;
width: 200px;
height: 280px;
cursor: pointer;
transition: all 0.3s ease;
}
.app-box:hover {
transform: translateY(-5px);
box-shadow: 0 10px 20px rgba(255, 255, 255, 0.1);
}
.app-artwork {
width: 200px;
height: 200px;
background-color: #333;
border: 2px solid #555;
border-radius: 8px;
position: relative;
overflow: hidden;
transition: border-color 0.3s ease;
}
.app-box:hover .app-artwork {
border-color: #00aaff;
}
.app-title {
text-align: center;
margin: 10px 0 5px 0;
font-size: 16px;
font-weight: bold;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.app-server {
text-align: center;
font-size: 12px;
color: #aaa;
margin: 0;
}
.play-button {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 60px;
height: 60px;
background-color: rgba(0, 170, 255, 0.9);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
transition: opacity 0.3s ease;
pointer-events: none;
}
.play-button::after {
content: '';
width: 0;
height: 0;
border-left: 20px solid white;
border-top: 12px solid transparent;
border-bottom: 12px solid transparent;
margin-left: 4px;
}
.app-box:hover .play-button {
opacity: 1;
}
.app-box.clicked {
transform: scale(0.95);
}
.loading {
text-align: center;
font-size: 18px;
margin-top: 50px;
}
.error {
color: #ff4444;
text-align: center;
font-size: 18px;
margin-top: 50px;
}
</style>
</head>
<body>
<h1>Game Streaming Platform</h1>
<div id="content">
<div class="loading">Loading applications...</div>
</div>
<script src="index.js"></script>
</body>
</html>
@@ -1,224 +0,0 @@
class GameStreamingApp {
constructor() {
this.apps = [];
this.init();
}
async init() {
try {
await this.loadApps();
this.renderApps();
} catch (error) {
this.showError('Failed to load applications: ' + error.message);
}
}
async loadApps() {
try {
const response = await fetch('/api/apps');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
this.apps = this.processAppsData(data);
} catch (error) {
console.error('Error loading apps:', error);
throw error;
}
}
processAppsData(data) {
const apps = [];
if (data.apps) {
Object.keys(data.apps).forEach(serverName => {
data.apps[serverName].forEach(app => {
apps.push({
...app,
server: serverName
});
});
});
}
return apps;
}
renderApps() {
const contentDiv = document.getElementById('content');
if (this.apps.length === 0) {
contentDiv.innerHTML = '<div class="error">No applications found.</div>';
return;
}
const appsContainer = document.createElement('div');
appsContainer.className = 'apps-container';
this.apps.forEach(app => {
const appBox = this.createAppBox(app);
appsContainer.appendChild(appBox);
});
contentDiv.innerHTML = '';
contentDiv.appendChild(appsContainer);
}
createAppBox(app) {
const appBox = document.createElement('div');
appBox.className = 'app-box';
appBox.dataset.id = app.id;
appBox.dataset.server = app.server;
appBox.innerHTML = `
<div class="app-artwork">
<div class="play-button"></div>
</div>
<div class="app-title">${app.title}</div>
<div class="app-server">${app.server}</div>
`;
// Add click event listener
appBox.addEventListener('click', (e) => this.handleAppClick(e, app));
// Add click animation
appBox.addEventListener('mousedown', () => {
appBox.classList.add('clicked');
});
appBox.addEventListener('mouseup', () => {
setTimeout(() => {
appBox.classList.remove('clicked');
}, 150);
});
appBox.addEventListener('mouseleave', () => {
appBox.classList.remove('clicked');
});
return appBox;
}
async handleAppClick(event, app) {
event.preventDefault();
try {
console.log(`Starting stream for ${app.title} on ${app.server}`);
// Create the POST request payload
const payload = {
id: app.id,
server: app.server,
server_mode: {
fps: 60,
height: 1280,
width: 720
},
stream_config: {
bitrate_kbps: 5120,
mode: {
fps: 60,
height: 1280,
width: 720
}
}
};
// Make POST request to start stream
const response = await fetch('/api/stream/start', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(payload)
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const streamData = await response.json();
console.log('Stream started:', streamData);
if (streamData.url && streamData.cert_hash) {
await this.connectToStream(streamData.url, streamData.cert_hash);
} else {
throw new Error('Response was missing required parameters');
}
} catch (error) {
console.error('Error starting stream:', error);
alert('Failed to start stream: ' + error.message);
}
}
async connectToStream(url, cert_hash) {
const buffer = new Uint8Array(cert_hash);
console.log('Hash: ', buffer);
try {
console.log(`Connecting to stream`);
// Check if WebTransport is supported
if (!window.WebTransport) {
throw new Error('WebTransport is not supported in this browser');
}
//const url = new URL();
const transport = new WebTransport(url, {
serverCertificateHashes: [
{
algorithm: "sha-256",
value: buffer,
}
]
});
console.log('Connecting to WebTransport at ', url);
// Wait for the connection to be ready
await transport.ready;
console.log('WebTransport connection established');
console.log('Creating WebTransport bidirectional stream');
const stream = await transport.createBidirectionalStream();
console.log('Bidirectional stream created');
const reader = stream.readable.getReader();
// Handle incoming data
async function readData() {
while (true) {
const { value, done } = await reader.read();
if (done) break;
console.log(value)
//console.log(`Received: ${new TextDecoder().decode(value)}`);
}
}
readData();
// Handle connection close
transport.closed.then(() => {
console.log('WebTransport connection closed');
}).catch((error) => {
console.error('WebTransport connection closed with error:', error);
});
// You can add more WebTransport handling logic here
// For example, handling incoming streams, sending data, etc.
} catch (error) {
console.error('Error connecting to stream:', error);
alert('Failed to connect to stream: ' + error.message);
}
}
showError(message) {
const contentDiv = document.getElementById('content');
contentDiv.innerHTML = `<div class="error">${message}</div>`;
}
}
// Initialize the app when the page loads
document.addEventListener('DOMContentLoaded', () => {
new GameStreamingApp();
});