| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 56bd900 commit 5f7e07a
3 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -64,30 +64,6 @@ header { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin- | |||
| 64 | 64 | minmax(0,1fr) και όχι 1fr: το αυτόματο κάτω φράγμα του 1fr είναι το min-content | |
| 65 | 65 | του panel, δηλαδή και οι τέσσερις στήλες μαζί — έσπρωχνε το preview στα 276px. */ | |
| 66 | 66 | .body { display: grid; grid-template-columns: minmax(0, 480px) minmax(0, 1fr); } | |
| 67 | - .preview { position: relative; background: #000; aspect-ratio: 16 / 9; } | ||
| 68 | - .preview video { width: 100%; height: 100%; display: block; object-fit: contain; } | ||
| 69 | - .play { | ||
| 70 | - position: absolute; inset: 0; width: 100%; padding: 0; border: 0; | ||
| 71 | - background: transparent; color: #fff; font-size: 34px; opacity: .7; cursor: pointer; | ||
| 72 | - } | ||
| 73 | - .play:hover { opacity: 1; background: rgba(255, 255, 255, .07); } | ||
| 74 | - /* 2x2 δίπλα σε άλλη κάρτα, 4 σε σειρά όταν είναι μόνη της — και στις δύο | ||
| 75 | - περιπτώσεις κεντραρισμένα στον χώρο δεξιά από το preview. | ||
| 76 | - Ρητό πλήθος στηλών με φράγμα πλάτους, όχι auto-fit με 1fr: τα 1fr απλώνουν τα | ||
| 77 | - νούμερα σε όλο το πλάτος της κάρτας, ενώ definite max μέσα σε auto-fit αλλάζει | ||
| 78 | - και το πλήθος των tracks. Το κεντράρισμα θέλει justify-content — margin auto ή | ||
| 79 | - justify-self κάνουν το κουτί shrink-to-fit και ξεχειλίζει στις στενές κάρτες. */ | ||
| 80 | - .stat { | ||
| 81 | - margin: 0; padding: 16px; align-content: center; justify-content: center; | ||
| 82 | - display: grid; grid-template-columns: repeat(2, minmax(0, 120px)); gap: 14px; | ||
| 83 | - } | ||
| 84 | - /* Container και όχι media query: το πλάτος της κάρτας το ορίζει το πλήθος των | ||
| 85 | - streams, όχι μόνο το viewport. Το κατώφλι είναι εκεί που χωράνε 4 στήλες. */ | ||
| 86 | - @container (min-width: 1120px) { | ||
| 87 | - .stat { grid-template-columns: repeat(4, minmax(0, 140px)); } | ||
| 88 | - } | ||
| 89 | - .stat dt { font-size: 12px; color: var(--muted); white-space: nowrap; } | ||
| 90 | - .stat dd { margin: 0; font-size: 19px; font-weight: 600; line-height: 1.25; font-variant-numeric: tabular-nums; } | ||
| 91 | 67 | .quiet { color: var(--muted); text-align: center; padding: 30px; } | |
| 92 | 68 | /* Container και όχι media query: με δύο κάρτες δίπλα-δίπλα η στήλη δεξιά από το | |
| 93 | 69 | preview είναι στενή ακόμα και σε μεγάλη οθόνη — εκεί τα πεδία του OBS έβγαζαν | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -58,10 +58,15 @@ const tiles = computed(() => [ | |||
| 58 | 58 | { label: `Μνήμη ${selected.value}`, value: current.value ? String(current.value.snapshot.server.rss_mb) : '—', unit: 'MB' }, | |
| 59 | 59 | ]) | |
| 60 | 60 | ||
| 61 | - // Ο stream server σερβίρει το HLS από το ίδιο hostname με το οποίο δηλώνεται στο | ||
| 62 | - // panel (config.panel.host == το domain του Caddy του) — δες apps/stream/install. | ||
| 63 | - const hlsUrl = (host: string, stream: string) => | ||
| 64 | - `https://${host}/${stream.replace(/^\//, '')}/index.m3u8` | ||
| 61 | + // Ο player ανοίγει σε popup και όχι μέσα στη σελίδα: με 2-3 ζωντανές εκπομπές τα | ||
| 62 | + // preview έπιαναν όλο το ύψος και το dashboard έπαυε να δείχνει νούμερα. Ένα | ||
| 63 | + // παράθυρο ανά stream (το όνομα είναι host+path): δεύτερο κλικ φέρνει μπροστά το | ||
| 64 | + // ίδιο, αντί για δεύτερο player πάνω στο ίδιο stream — κάθε player μετράει ως | ||
| 65 | + // κανονικός θεατής. | ||
| 66 | + function openPlayer(s: { host: string, stream: string }) { | ||
| 67 | + const url = `/admin/streams/${encodeURIComponent(s.stream)}?host=${s.host}` | ||
| 68 | + window.open(url, `player:${s.host}${s.stream}`, 'popup=yes,width=960,height=640')?.focus() | ||
| 69 | + } | ||
| 65 | 70 | ||
| 66 | 71 | async function loadLive() { | |
| 67 | 72 | try { | |
@@ -216,51 +221,53 @@ watch([selected, range], () => { | |||
| 216 | 221 | ||
| 217 | 222 | <UAlert v-if="error" color="error" variant="subtle" icon="i-lucide-triangle-alert" :description="error" /> | |
| 218 | 223 | ||
| 219 | - <div class="hero"> | ||
| 220 | - <UCard | ||
| 221 | - v-for="s in streams" :key="s.host + s.stream" | ||
| 222 | - :ui="{ body: 'p-0 sm:p-0', header: 'px-4 py-3 sm:px-4', footer: 'px-4 py-2 sm:px-4' }" | ||
| 223 | - > | ||
| 224 | - <template #header> | ||
| 225 | - <div class="head"> | ||
| 226 | - <UBadge v-if="s.online" color="error" variant="solid" class="tracking-wider">ON AIR</UBadge> | ||
| 227 | - <UBadge v-else color="neutral" variant="subtle" title="ο server δεν έχει κάνει sync εδώ και >30s"> | ||
| 228 | - ΕΚΤΟΣ | ||
| 229 | - </UBadge> | ||
| 230 | - <NuxtLink :to="`/admin/streams/${encodeURIComponent(s.stream)}?host=${s.host}`"> | ||
| 231 | - {{ s.stream }} | ||
| 232 | - </NuxtLink> | ||
| 233 | - <span class="host">{{ s.host }}</span> | ||
| 234 | - <span class="spacer" /> | ||
| 235 | - <span class="since">{{ dur((Date.now() - s.since) / 1000) }}</span> | ||
| 236 | - </div> | ||
| 237 | - </template> | ||
| 238 | - | ||
| 239 | - <div class="body"> | ||
| 240 | - <Preview :src="hlsUrl(s.host, s.stream)" /> | ||
| 241 | - <dl class="stat"> | ||
| 242 | - <div><dt>Θεατές</dt><dd>{{ s.viewers }}</dd></div> | ||
| 243 | - <!-- Με R2 ενεργό το out_bps είναι εκτίμηση (bytes segment × θεατές) — τα .ts | ||
| 244 | - σερβίρονται από το CDN και δεν αγγίζουν ποτέ αυτόν τον server. Δείχνε το, | ||
| 245 | - με αστερίσκο και όχι με λεζάντα: το «(εκτ.)» δεν χωράει στη στήλη. --> | ||
| 246 | - <div> | ||
| 247 | - <dt :title="s.r2Estimate ? 'Εκτίμηση: τα .ts segments σερβίρονται από το R2, όχι μέτρηση πραγματικής κίνησης' : ''"> | ||
| 248 | - {{ s.r2Estimate ? 'Έξοδος *' : 'Έξοδος' }} | ||
| 249 | - </dt> | ||
| 250 | - <dd>{{ bps(s.out_bps) }}</dd> | ||
| 251 | - </div> | ||
| 252 | - <div><dt>Είσοδος</dt><dd>{{ bps(s.in_bps) }}</dd></div> | ||
| 253 | - <div><dt>Ανάλυση</dt><dd>{{ s.resolution }}</dd></div> | ||
| 254 | - </dl> | ||
| 255 | - </div> | ||
| 256 | - | ||
| 257 | - <template #footer> | ||
| 258 | - <span class="host">{{ s.video }} · {{ s.audio }} · {{ s.protocol }} από {{ s.ip }}</span> | ||
| 259 | - </template> | ||
| 260 | - </UCard> | ||
| 261 | - </div> | ||
| 262 | - <UCard v-if="!streams.length"> | ||
| 263 | - <div class="quiet">Καμία ενεργή εκπομπή</div> | ||
| 224 | + <!-- Μία γραμμή ανά εκπομπή και όχι κάρτα με preview: τα βίντεο έπιαναν όλη | ||
| 225 | + τη σελίδα με δύο-τρία streams, ενώ αυτό που κοιτάει ο διαχειριστής όλη | ||
| 226 | + την ώρα είναι τα νούμερα. Ο player είναι ένα κλικ μακριά, σε popup. --> | ||
| 227 | + <UCard> | ||
| 228 | + <h2>Ενεργές εκπομπές</h2> | ||
| 229 | + <div class="scroll"> | ||
| 230 | + <table v-if="streams.length"> | ||
| 231 | + <thead> | ||
| 232 | + <tr> | ||
| 233 | + <th /> | ||
| 234 | + <th>Stream</th><th>Server</th><th class="num">Διάρκεια</th><th class="num">Θεατές</th> | ||
| 235 | + <th class="num">Είσοδος</th><th class="num">Έξοδος</th> | ||
| 236 | + <th>Ανάλυση</th><th>Κωδικοποίηση</th><th>IP</th><th /> | ||
| 237 | + </tr> | ||
| 238 | + </thead> | ||
| 239 | + <tbody> | ||
| 240 | + <tr v-for="s in streams" :key="s.host + s.stream"> | ||
| 241 | + <td> | ||
| 242 | + <UBadge v-if="s.online" color="error" variant="solid" class="tracking-wider">ON AIR</UBadge> | ||
| 243 | + <UBadge v-else color="neutral" variant="subtle" title="ο server δεν έχει κάνει sync εδώ και >30s"> | ||
| 244 | + ΕΚΤΟΣ | ||
| 245 | + </UBadge> | ||
| 246 | + </td> | ||
| 247 | + <td>{{ s.stream }}</td> | ||
| 248 | + <td>{{ s.host }}</td> | ||
| 249 | + <td class="num">{{ dur((Date.now() - s.since) / 1000) }}</td> | ||
| 250 | + <td class="num">{{ s.viewers }}</td> | ||
| 251 | + <td class="num">{{ bps(s.in_bps) }}</td> | ||
| 252 | + <!-- Με R2 ενεργό η έξοδος είναι εκτίμηση (bytes segment × θεατές) — τα .ts | ||
| 253 | + σερβίρονται από το CDN και δεν αγγίζουν ποτέ αυτόν τον server. --> | ||
| 254 | + <td | ||
| 255 | + class="num" | ||
| 256 | + :title="s.r2Estimate ? 'Εκτίμηση: τα .ts segments σερβίρονται από το R2, όχι μέτρηση πραγματικής κίνησης' : ''" | ||
| 257 | + >{{ bps(s.out_bps) }}{{ s.r2Estimate ? ' *' : '' }}</td> | ||
| 258 | + <td>{{ s.resolution }}</td> | ||
| 259 | + <td>{{ s.video }} · {{ s.audio }}</td> | ||
| 260 | + <td>{{ s.protocol }} από {{ s.ip }}</td> | ||
| 261 | + <td> | ||
| 262 | + <UButton size="xs" color="neutral" variant="subtle" icon="i-lucide-play" @click="openPlayer(s)"> | ||
| 263 | + player | ||
| 264 | + </UButton> | ||
| 265 | + </td> | ||
| 266 | + </tr> | ||
| 267 | + </tbody> | ||
| 268 | + </table> | ||
| 269 | + <div v-else class="empty">Καμία ενεργή εκπομπή</div> | ||
| 270 | + </div> | ||
| 264 | 271 | </UCard> | |
| 265 | 272 | ||
| 266 | 273 | <!-- Μόνο αθροίσματα εδώ — τα νούμερα ανά stream τα δείχνει η κάρτα. Το uptime | |
| Back | FazBrowse Home | New Git URL |
0 commit comments