always same channels
Build and Push Docker Images / build-and-push (push) Successful in 22s

This commit is contained in:
Bram committed 2026-11-07 16:33:13 +01:00
1 parent d266a8258d
commit 774200fb39
4 files changed
+38 -15

No files matched your search

@@ -25,7 +25,8 @@ View BE| DAZN 03 [Standard de Liège - SK Beveren | Jupiler Pro League (nl) (202
vrijgekomen slot wordt gevuld met het oudste wachtende event (sortering op starttijd, vrijgekomen slot wordt gevuld met het oudste wachtende event (sortering op starttijd,
daarna volgorde van binnenkomst). Geen preemptie van een al toegewezen event. daarna volgorde van binnenkomst). Geen preemptie van een al toegewezen event.
- **Altijd 3 slots**: ook bij 0, 1 of 2 wedstrijden blijven `Jupiler 1` t/m `Jupiler 3` - **Altijd 3 slots**: ook bij 0, 1 of 2 wedstrijden blijven `Jupiler 1` t/m `Jupiler 3`
in de playlist staan. Een leeg slot heet `Geen wedstrijd` en `/stream/<slot>` geeft 503. in de playlist staan met volledig vaste id's, namen en `tvg-chno`. De actuele wedstrijd
staat in de EPG en `/status`, niet in de kanaalnaam. Een leeg slot geeft 503.
- **Live-bepaling**: starttijd uit de kanaalnaam + instelbare duur. Een event is eligibel - **Live-bepaling**: starttijd uit de kanaalnaam + instelbare duur. Een event is eligibel
vanaf `startAt - START_GRACE_MIN` tot `startAt + EVENT_DURATION_MIN`. vanaf `startAt - START_GRACE_MIN` tot `startAt + EVENT_DURATION_MIN`.
- **Einde event**: op basis van de berekende eindtijd, niet op basis van het verdwijnen - **Einde event**: op basis van de berekende eindtijd, niet op basis van het verdwijnen
@@ -159,9 +160,9 @@ blijven de overige slots leeg (naam `Geen wedstrijd`).
## Endpoints ## Endpoints
- `GET /` - eenvoudige HTML met playlist-link en kanaalstatus. - `GET /` - eenvoudige HTML met playlist-link en kanaalstatus.
- `GET /playlist.m3u` - altijd `MAX_CHANNELS` entries: - `GET /playlist.m3u` - altijd `MAX_CHANNELS` entries met vaste metadata:
`#EXTINF:-1 tvg-id="jupiler-1" tvg-name="Jupiler 1",Jupiler 1 | <titel> (<start>)` `#EXTINF:-1 tvg-id="jupiler-1" tvg-name="Jupiler 1" tvg-chno="1",Jupiler 1`
en daaronder `http://<host>/stream/1`. en daaronder `http://<host>/stream/1`. Wedstrijdinformatie staat in de EPG.
- `GET /epg.xml` of `/xmltv.xml` - XMLTV-gids voor de vaste kanalen. De gids toont de - `GET /epg.xml` of `/xmltv.xml` - XMLTV-gids voor de vaste kanalen. De gids toont de
actuele toewijzing plus een FIFO-projectie van toekomstige wedstrijden uit de laatste actuele toewijzing plus een FIFO-projectie van toekomstige wedstrijden uit de laatste
kanaalnamen-refresh. kanaalnamen-refresh.
+4 -3
View File
@@ -55,9 +55,10 @@ Voeg daarna in je IPTV-client toe:
| `GET /health` | Healthcheck voor Docker | | `GET /health` | Healthcheck voor Docker |
| `GET /` | Eenvoudige statuspagina | | `GET /` | Eenvoudige statuspagina |
De playlist bevat altijd `Jupiler 1` t/m `Jupiler 3`. Is een slot leeg, dan heet het De playlist bevat altijd exact dezelfde drie kanalen: `Jupiler 1`, `Jupiler 2` en
`Jupiler N | Geen wedstrijd` en geeft `/stream/N` een 503 tot er een wedstrijd op het slot `Jupiler 3`, met vaste `tvg-id`, `tvg-name` en `tvg-chno`. De kanaalnaam verandert nooit;
staat. de actuele wedstrijd staat in de EPG en op `/status`. Is een slot leeg, dan geeft
`/stream/N` een 503 tot er een wedstrijd op het slot staat.
## Configuratie ## Configuratie
+2 -5
View File
@@ -9,12 +9,9 @@ function buildPlaylist(req, scheduler) {
const count = scheduler.getSlotCount(); const count = scheduler.getSlotCount();
const lines = ['#EXTM3U']; const lines = ['#EXTM3U'];
for (let i = 0; i < count; i += 1) { for (let i = 0; i < count; i += 1) {
const event = scheduler.getSlotEvent(i); const name = `Jupiler ${i + 1}`;
const label = event
? `${event.title} (${formatLocalTime(event.startAt, settings.timezone)})`
: 'Geen wedstrijd';
lines.push( lines.push(
`#EXTINF:-1 tvg-id="jupiler-${i + 1}" tvg-name="Jupiler ${i + 1}",Jupiler ${i + 1} | ${label}` `#EXTINF:-1 tvg-id="jupiler-${i + 1}" tvg-name="${name}" tvg-chno="${i + 1}",${name}`
); );
lines.push(`${origin}/stream/${i + 1}`); lines.push(`${origin}/stream/${i + 1}`);
} }
@@ -26,10 +26,34 @@ test('playlist always contains three fixed channels', () => {
assert.equal(lines[0], '#EXTM3U'); assert.equal(lines[0], '#EXTM3U');
assert.equal( assert.equal(
lines[1], lines[1],
'#EXTINF:-1 tvg-id="jupiler-1" tvg-name="Jupiler 1",Jupiler 1 | Standard de Liège - SK Beveren (19:25)' '#EXTINF:-1 tvg-id="jupiler-1" tvg-name="Jupiler 1" tvg-chno="1",Jupiler 1'
); );
assert.equal(lines[2], 'http://example.test:8080/stream/1'); assert.equal(lines[2], 'http://example.test:8080/stream/1');
assert.ok(body.includes('Jupiler 2 | Geen wedstrijd')); assert.ok(body.includes('#EXTINF:-1 tvg-id="jupiler-2" tvg-name="Jupiler 2" tvg-chno="2",Jupiler 2'));
assert.ok(body.includes('Jupiler 3 | Geen wedstrijd')); assert.ok(body.includes('#EXTINF:-1 tvg-id="jupiler-3" tvg-name="Jupiler 3" tvg-chno="3",Jupiler 3'));
assert.ok(body.includes('http://example.test:8080/stream/3')); assert.ok(body.includes('http://example.test:8080/stream/3'));
}); });
test('channel metadata stays identical when the assigned event changes', () => {
const first = {
getSlotCount: () => 3,
getSlotEvent: (index) => (index === 0 ? event : null),
};
const second = {
getSlotCount: () => 3,
getSlotEvent: (index) =>
index === 0
? {
key: 'b',
title: 'Anderlecht - Genk',
startAt: new Date('2026-11-06T21:00:00Z'),
endAt: new Date('2026-11-06T23:30:00Z'),
}
: null,
};
const channelLines = (body) => body.split('\n').filter((line) => line.startsWith('#EXTINF'));
assert.deepEqual(
channelLines(buildPlaylist(fakeRequest(), first)),
channelLines(buildPlaylist(fakeRequest(), second))
);
});