frontend: add svelte frontend
This commit is contained in:
@@ -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();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user