Bound bouquet lookup and preserve playback if OpenWebif is unavailable
This commit is contained in:
@@ -1145,9 +1145,25 @@ namespace Jellyfin.Plugin.Enigma2
|
|||||||
_logger.LogInformation("[Enigma2] Start GetChannelStream");
|
_logger.LogInformation("[Enigma2] Start GetChannelStream");
|
||||||
|
|
||||||
var requestedReference = channelOid;
|
var requestedReference = channelOid;
|
||||||
var currentChannels = await GetChannelsAsync(cancellationToken).ConfigureAwait(false);
|
IEnumerable<ChannelInfo> currentChannels = Array.Empty<ChannelInfo>();
|
||||||
|
using (var lookupDeadline = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken))
|
||||||
|
{
|
||||||
|
lookupDeadline.CancelAfter(TimeSpan.FromSeconds(5));
|
||||||
|
try
|
||||||
|
{
|
||||||
|
currentChannels = await GetChannelsAsync(lookupDeadline.Token).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch (Exception error) when (!cancellationToken.IsCancellationRequested
|
||||||
|
&& (error is OperationCanceledException || error is HttpRequestException
|
||||||
|
|| error is IOException || error is ApplicationException))
|
||||||
|
{
|
||||||
|
// A stale-reference repair must not prevent direct streaming
|
||||||
|
// when the receiver's web interface is temporarily unavailable.
|
||||||
|
_logger.LogWarning("[Enigma2] Current bouquet lookup unavailable ({FailureType}); retaining requested channel reference", error.GetType().Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
channelOid = ChannelReferenceResolver.Resolve(channelOid, currentChannels, () =>
|
channelOid = !currentChannels.Any() ? channelOid : ChannelReferenceResolver.Resolve(channelOid, currentChannels, () =>
|
||||||
{
|
{
|
||||||
// Native playback has already checked the user's channel access.
|
// Native playback has already checked the user's channel access.
|
||||||
// This internal lookup reads only Enigma2 channel metadata and
|
// This internal lookup reads only Enigma2 channel metadata and
|
||||||
|
|||||||
@@ -65,8 +65,8 @@ See [integration changes and deployment](README.integration.md). Internet EPG ar
|
|||||||
|
|
||||||
## Kandydat 7.0.9 — stare wpisy kanałów po migracji, 23.10.2026
|
## Kandydat 7.0.9 — stare wpisy kanałów po migracji, 23.10.2026
|
||||||
|
|
||||||
Odczyt metadanych LiveTvChannel potwierdził stare i nowe wpisy Pulsu/Pulsu 2 oraz stare wpisy WP i TVN24 po zmianie bukietów dekodera. GetChannelStream odświeża listę z dekodera i rozwiązuje zapamiętaną referencję przed zapem, rejestracją relay i wyznaczeniem SID do probe. Najpierw zachowuje dokładny aktualny wpis; potem dopuszcza jednoznaczną zmianę samego typu usługi DVB; migrację SID/TSID rozpoznaje tylko po unikalnej dokładnej nazwie odczytanej z historycznego kanału Enigma2. Brak dopasowania lub niejednoznaczność zachowuje poprzednie zachowanie. Nie zgadujemy HD/SD ani wariantów regionalnych. Natywny identyfikator kanału i preferencje użytkownika pozostają zachowane. Stary i nowy wpis wskazują ten sam adres relay, więc nie otwierają dwóch źródeł.
|
Odczyt metadanych LiveTvChannel potwierdził stare i nowe wpisy Pulsu/Pulsu 2 oraz stare wpisy WP i TVN24 po zmianie bukietów dekodera. GetChannelStream odświeża listę z dekodera i rozwiązuje zapamiętaną referencję przed zapem, rejestracją relay i wyznaczeniem SID do probe. Najpierw zachowuje dokładny aktualny wpis; potem dopuszcza jednoznaczną zmianę samego typu usługi DVB; migrację SID/TSID rozpoznaje tylko po unikalnej dokładnej nazwie odczytanej z historycznego kanału Enigma2. Brak dopasowania lub niejednoznaczność zachowuje poprzednie zachowanie. Nie zgadujemy HD/SD ani wariantów regionalnych. Natywny identyfikator kanału i preferencje użytkownika pozostają zachowane. Stary i nowy wpis wskazują ten sam adres relay, więc nie otwierają dwóch źródeł. Odczyt bukietu ma wspólny limit 5 s; niedostępny lub zawieszony OpenWebif zachowuje próbę odtwarzania oryginalnej referencji. Anulowanie przez klienta pozostaje obowiązujące.
|
||||||
|
|
||||||
100 kontroli .NET10 przeszło, w tym rzeczywiste GetChannelStream z atrapą OpenWebif i natywnej biblioteki: nowy SID 5105, wspólne metadane i jedno połączenie relay dla starego/nowego Pulsu, anulowanie bez dodatkowego połączenia. Build Release: 0 ostrzeżeń, 0 błędów. Restore korzystał z cache pakietów bez sieci; NuGetAudit wyłączono jedynie dla tego odtworzenia offline. Nie potwierdza to odbioru na zalogowanym urządzeniu.
|
102 kontrole .NET10 przeszły, w tym rzeczywiste GetChannelStream z atrapą OpenWebif i natywnej biblioteki: nowy SID 5105, wspólne metadane i jedno połączenie relay dla starego/nowego Pulsu, anulowanie bez dodatkowego połączenia. Build Release: 0 ostrzeżeń, 0 błędów. Restore korzystał z cache pakietów bez sieci; NuGetAudit wyłączono jedynie dla tego odtworzenia offline. Nie potwierdza to odbioru na zalogowanym urządzeniu.
|
||||||
|
|
||||||
Status na moment zapisu: **7.0.9 przygotowana, niewdrożona**. W produkcji pozostaje 7.0.8. Nowa sesja Wydarzeń 24 rozpoczęta o 16:21:45 UTC dała poprawne serwerowe HLS (248 klatek 1080p / 9,88 s oraz 10,027 s audio). Restart wymagany do podmiany DLL czeka na zgodę dotyczącą tej aktywnej sesji. Osobny brak odkodowanego wyjścia CANAL+4K wymaga diagnostyki źródła ECM i nie jest naprawiany przez ten resolver. Nie zmieniono ustawień Jellyfina, wrapperów, plików Web ani konfiguracji dekodera.
|
Status na moment zapisu: **7.0.9 przygotowana, niewdrożona**. W produkcji pozostaje 7.0.8. Nowa sesja Wydarzeń 24 rozpoczęta o 16:21:45 UTC dała poprawne serwerowe HLS (248 klatek 1080p / 9,88 s oraz 10,027 s audio). Restart wymagany do podmiany DLL czeka na zgodę dotyczącą tej aktywnej sesji. Osobny brak odkodowanego wyjścia CANAL+4K wymaga diagnostyki źródła ECM i nie jest naprawiany przez ten resolver. Nie zmieniono ustawień Jellyfina, wrapperów, plików Web ani konfiguracji dekodera.
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
@@ -54,6 +55,14 @@ public static class SavedChannelPlaybackTests
|
|||||||
using var cancelled=new CancellationTokenSource();cancelled.Cancel();
|
using var cancelled=new CancellationTokenSource();cancelled.Cancel();
|
||||||
try { await service.GetChannelStream(Old,null,cancelled.Token);throw new Exception("Expected cancellation"); }
|
try { await service.GetChannelStream(Old,null,cancelled.Token);throw new Exception("Expected cancellation"); }
|
||||||
catch(OperationCanceledException) { check(handler.Calls==1,"cancelled channel lookup opens no extra upstream"); }
|
catch(OperationCanceledException) { check(handler.Calls==1,"cancelled channel lookup opens no extra upstream"); }
|
||||||
|
|
||||||
|
var offline=new LiveTvService(NullLogger<LiveTvService>.Instance,new ReceiverFactory(new UnavailableBouquet(false)),null,probe,relay,library);
|
||||||
|
check((await offline.GetChannelStream(Current,null,CancellationToken.None)).Path==current.Path&&((SavedChannelLibrary)(object)library).Calls==1,
|
||||||
|
"unavailable web interface preserves direct playback without historical lookup");
|
||||||
|
var stalled=new LiveTvService(NullLogger<LiveTvService>.Instance,new ReceiverFactory(new UnavailableBouquet(true)),null,probe,relay,library);
|
||||||
|
var watch=Stopwatch.StartNew();
|
||||||
|
check((await stalled.GetChannelStream(Current,null,CancellationToken.None)).Path==current.Path&&watch.Elapsed<TimeSpan.FromSeconds(7),
|
||||||
|
"stalled bouquet lookup is bounded to five seconds and preserves requested playback");
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -89,7 +98,19 @@ public static class SavedChannelPlaybackTests
|
|||||||
}
|
}
|
||||||
private sealed class ReceiverFactory:IHttpClientFactory
|
private sealed class ReceiverFactory:IHttpClientFactory
|
||||||
{
|
{
|
||||||
public HttpClient CreateClient(string name)=>new(new BouquetHandler());
|
private readonly HttpMessageHandler handler;
|
||||||
|
public ReceiverFactory(HttpMessageHandler handler=null){this.handler=handler;}
|
||||||
|
public HttpClient CreateClient(string name)=>new(handler??new BouquetHandler());
|
||||||
|
}
|
||||||
|
private sealed class UnavailableBouquet:HttpMessageHandler
|
||||||
|
{
|
||||||
|
private readonly bool stall;
|
||||||
|
public UnavailableBouquet(bool stall){this.stall=stall;}
|
||||||
|
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,CancellationToken token)
|
||||||
|
{
|
||||||
|
if(stall)await Task.Delay(Timeout.InfiniteTimeSpan,token);
|
||||||
|
throw new HttpRequestException("Receiver web interface unavailable");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
private sealed class BouquetHandler:HttpMessageHandler
|
private sealed class BouquetHandler:HttpMessageHandler
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user