86 lines
4.1 KiB
HTML
86 lines
4.1 KiB
HTML
|
|
{% extends 'base.html' %}
|
|
{% block title %}Streamer Panel{% endblock %}
|
|
{% block content %}
|
|
<div class="page-header">
|
|
<div>
|
|
<h1>Streamer Panel</h1>
|
|
<p>{{ stream.name }} · /{{ stream.slug }}</p>
|
|
</div>
|
|
<div id="liveBadge" class="badge {{ 'live' if stream.is_live else 'offline' }}">{{ 'LIVE' if stream.is_live else 'OFFLINE' }}</div>
|
|
</div>
|
|
<div class="streamer-layout-fixed">
|
|
<section class="card preview-column">
|
|
<div class="panel-head"><h2>Preview</h2><div class="muted">{{ public_hls }}</div></div>
|
|
<div class="video-frame large"><video id="streamerPreview" controls playsinline muted poster="{{ url_for('static', filename='videooffline.png') }}"></video></div>
|
|
<div class="stats-grid compact-top">
|
|
<div class="mini-stat"><span>Bitrate</span><strong id="spBitrate">{{ stats.get('bitrate', '-') }}</strong></div>
|
|
<div class="mini-stat"><span>FPS</span><strong id="spFps">{{ stats.get('fps', '-') }}</strong></div>
|
|
<div class="mini-stat"><span>Video</span><strong id="spVideo">{{ stats.get('video_codec', '-') }}</strong></div>
|
|
<div class="mini-stat"><span>Lyd</span><strong id="spAudio">{{ stats.get('audio_codec', '-') }}</strong></div>
|
|
<div class="mini-stat"><span>Kanaler</span><strong id="spChannels">{{ stats.get('audio_channels', '-') }}</strong></div>
|
|
<div class="mini-stat"><span>Sample rate</span><strong id="spSample">{{ stats.get('audio_sample_rate', '-') }}</strong></div>
|
|
<div class="mini-stat"><span>Ready</span><strong id="spReady">{{ 'true' if path_info and path_info.ready else 'false' }}</strong></div>
|
|
<div class="mini-stat"><span>Online</span><strong id="spOnline">{{ 'true' if path_info and path_info.online else 'false' }}</strong></div>
|
|
</div>
|
|
<div class="ingest-box">
|
|
<h2>Dine forbindelser</h2>
|
|
<table class="stream-table">
|
|
<thead><tr><th>Start</th><th>Status</th><th>IP</th></tr></thead>
|
|
<tbody>
|
|
{% for row in sessions %}
|
|
<tr><td>{{ row.started_at }}</td><td>{{ row.status }}</td><td>{{ row.publisher_ip or '-' }}</td></tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</section>
|
|
<aside class="card chat-column">
|
|
<h2>Chat</h2>
|
|
{% if stream.chat_url %}
|
|
<iframe src="{{ stream.chat_url }}" class="chat-frame"></iframe>
|
|
{% else %}
|
|
<div class="muted">Ingen chat er sat op endnu for denne kanal.</div>
|
|
{% endif %}
|
|
</aside>
|
|
</div>
|
|
<script>
|
|
const previewUrl = {{ public_hls|tojson }};
|
|
const statusUrl = {{ url_for('main.stream_status', stream_id=stream.id)|tojson }};
|
|
const player = document.getElementById('streamerPreview');
|
|
let hlsInstance = null;
|
|
function attachPlayer(url) {
|
|
if (hlsInstance) { hlsInstance.destroy(); hlsInstance = null; }
|
|
if (window.Hls && Hls.isSupported()) {
|
|
hlsInstance = new Hls({lowLatencyMode: true});
|
|
hlsInstance.loadSource(url);
|
|
hlsInstance.attachMedia(player);
|
|
} else { player.src = url; }
|
|
}
|
|
function fillStats(stats) {
|
|
document.getElementById('spBitrate').textContent = stats.bitrate || '-';
|
|
document.getElementById('spFps').textContent = stats.fps || '-';
|
|
document.getElementById('spVideo').textContent = stats.video_codec || '-';
|
|
document.getElementById('spAudio').textContent = stats.audio_codec || '-';
|
|
document.getElementById('spChannels').textContent = stats.audio_channels || '-';
|
|
document.getElementById('spSample').textContent = stats.audio_sample_rate || '-';
|
|
}
|
|
attachPlayer(previewUrl);
|
|
fillStats({{ stats|tojson }});
|
|
async function refreshStatus() {
|
|
const res = await fetch(statusUrl);
|
|
const data = await res.json();
|
|
const live = !!data.is_live;
|
|
const badge = document.getElementById('liveBadge');
|
|
badge.textContent = live ? 'LIVE' : 'OFFLINE';
|
|
badge.classList.toggle('live', live);
|
|
badge.classList.toggle('offline', !live);
|
|
document.getElementById('spReady').textContent = data.path_info?.ready ? 'true' : 'false';
|
|
document.getElementById('spOnline').textContent = data.path_info?.online ? 'true' : 'false';
|
|
fillStats(data.stats || {});
|
|
if (!live) { player.poster = "{{ url_for('static', filename='videooffline.png') }}"; }
|
|
}
|
|
setInterval(refreshStatus, 3000);
|
|
</script>
|
|
{% endblock %}
|