Allow native guide import to complete without plugin-imposed short timeout

This commit is contained in:
2026-10-23 19:35:16 +02:00
parent 2e7662f018
commit eb95d10628
6 changed files with 14 additions and 7 deletions
+3 -1
View File
@@ -98,7 +98,9 @@ public sealed class ChannelSyncTask : IScheduledTask
} }
progress.Report(10); progress.Report(10);
var refreshStarted=DateTime.UtcNow; var refreshStarted=DateTime.UtcNow;
await tasks.Execute(worker,new TaskOptions{MaxRuntimeTicks=TimeSpan.FromMinutes(5).Ticks}).WaitAsync(token).ConfigureAwait(false); // A first seven-day import may take much longer than a channel check.
// Keep the native job's ordinary execution/cancellation policy.
await tasks.Execute(worker,new TaskOptions()).WaitAsync(token).ConfigureAwait(false);
token.ThrowIfCancellationRequested(); token.ThrowIfCancellationRequested();
if(worker.LastExecutionResult?.Status!=TaskCompletionStatus.Completed||worker.LastExecutionResult.StartTimeUtc<refreshStarted.AddSeconds(-1)) if(worker.LastExecutionResult?.Status!=TaskCompletionStatus.Completed||worker.LastExecutionResult.StartTimeUtc<refreshStarted.AddSeconds(-1))
throw new InvalidOperationException("Native guide refresh did not complete successfully."); throw new InvalidOperationException("Native guide refresh did not complete successfully.");
@@ -2,8 +2,8 @@
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net10.0</TargetFrameworks> <TargetFrameworks>net10.0</TargetFrameworks>
<AssemblyVersion>7.0.10.0</AssemblyVersion> <AssemblyVersion>7.0.11.0</AssemblyVersion>
<FileVersion>7.0.10.0</FileVersion> <FileVersion>7.0.11.0</FileVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net6.0|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net6.0|AnyCPU'">
+4
View File
@@ -84,3 +84,7 @@ Zadanie pobiera bukiety z limitem30s i wykrywa zmiany referencji, nazw, kolejno
Przed natywnym odświeżeniem zapisuje własny checkpoint w `data/decoder-integration/channel-sync.json`: stare identyfikatory/ref/nazwy oraz natywne ulubione. Po jednoznacznej migracji aktualizuje natywne ulubione oraz ulubione/ukryte/kolejność we własnych preferencjach użytkowników. Aktualizacje plików preferencji używają tych samych blokad co edytor, zachowują aktualne ustawienia i usuwają duplikaty. Błąd/anulowanie pozostawia checkpoint do ponowienia również wtedy, gdy natywne zadanie usunęło już stare kanały. Bezpośrednich zapisów do bazy Jellyfina nie ma. Stan ostatniej kontroli/synchronizacji jest dostępny w administracyjnym DecoderIntegration/Status. Przed natywnym odświeżeniem zapisuje własny checkpoint w `data/decoder-integration/channel-sync.json`: stare identyfikatory/ref/nazwy oraz natywne ulubione. Po jednoznacznej migracji aktualizuje natywne ulubione oraz ulubione/ukryte/kolejność we własnych preferencjach użytkowników. Aktualizacje plików preferencji używają tych samych blokad co edytor, zachowują aktualne ustawienia i usuwają duplikaty. Błąd/anulowanie pozostawia checkpoint do ponowienia również wtedy, gdy natywne zadanie usunęło już stare kanały. Bezpośrednich zapisów do bazy Jellyfina nie ma. Stan ostatniej kontroli/synchronizacji jest dostępny w administracyjnym DecoderIntegration/Status.
120 kontroli .NET10 przeszło: dokładnie6h/start, zmiany numerów i logo, migracja natywnych i własnych preferencji, rozdzielenie użytkowników, pusta lista, zajęty przewodnik, częściowa awaria i ponowienie, współbieżność, anulowanie oraz wyłączenie funkcji. Źródła ani nagrania produkcyjne nie były używane w tych testach. Stan przed wdrożeniem: produkcja7.0.9, kandydat7.0.10. 120 kontroli .NET10 przeszło: dokładnie6h/start, zmiany numerów i logo, migracja natywnych i własnych preferencji, rozdzielenie użytkowników, pusta lista, zajęty przewodnik, częściowa awaria i ponowienie, współbieżność, anulowanie oraz wyłączenie funkcji. Źródła ani nagrania produkcyjne nie były używane w tych testach. Stan przed wdrożeniem: produkcja7.0.9, kandydat7.0.10.
### 7.0.11 — czas pełnego importu EPG
7.0.10 wdrożono o17:27UTC. Zadanie startowe poprawnie uruchomiło natywny RefreshGuide i wprowadziło aktualne referencje Pulsu/Pulsu2/WP/TVN24. Pełny import7dni EPG przekroczył jednak narzucone5min i został anulowany o17:32:55UTC. Własna synchronizacja prawidłowo zgłosiła błąd i zachowała checkpoint216starych wpisów; nie oznaczono importu jako udanego. W7.0.11 usunięto własny limit czasu natywnego zadania — używane są zwykłe zasady jego wykonania i anulowania. Limit30s dotyczy tylko wstępnego pobrania bukietu. Dodano regresję na brak krótkiego MaxRuntimeTicks. Checkpoint i natywne preferencje pozostają zgodne pomiędzy7.0.10 i7.0.11, więc start nowej wersji ponawia migrację z oryginalnego stanu sprzed odświeżenia.
+3 -2
View File
@@ -34,6 +34,7 @@ public static class ChannelSyncTests
await c.Store.Save(c.SecondOwner.Id,new ChannelPreferences{HiddenIds=new[]{c.OldId.ToString()}},CancellationToken.None); await c.Store.Save(c.SecondOwner.Id,new ChannelPreferences{HiddenIds=new[]{c.OldId.ToString()}},CancellationToken.None);
await c.Run(); await c.Run();
check(c.Executions==1&&c.SourceCalls==1,"changed bouquet invokes native RefreshGuide once without opening a receiver stream"); check(c.Executions==1&&c.SourceCalls==1,"changed bouquet invokes native RefreshGuide once without opening a receiver stream");
check(c.NativeRuntimePolicy,"full guide import retains native runtime policy without an artificial short timeout");
var prefs=await c.Store.Load(c.Owner.Id,CancellationToken.None); var prefs=await c.Store.Load(c.Owner.Id,CancellationToken.None);
check(prefs.FavoriteIds.SequenceEqual(new[]{c.NewId.ToString("N")})&&prefs.HiddenIds.SequenceEqual(new[]{c.NewId.ToString("N")})&&prefs.OrderedIds.SequenceEqual(new[]{c.OtherId.ToString("N"),c.NewId.ToString("N")})&&prefs.DefaultView=="favorites","native channel migration remaps and deduplicates saved favorites, hidden IDs and ordering"); check(prefs.FavoriteIds.SequenceEqual(new[]{c.NewId.ToString("N")})&&prefs.HiddenIds.SequenceEqual(new[]{c.NewId.ToString("N")})&&prefs.OrderedIds.SequenceEqual(new[]{c.OtherId.ToString("N"),c.NewId.ToString("N")})&&prefs.DefaultView=="favorites","native channel migration remaps and deduplicates saved favorites, hidden IDs and ordering");
check(c.Data[(c.Owner.Id,c.NewId)].IsFavorite&&c.Data[(c.Owner.Id,c.NewId)].PlayCount==7,"native favorite migration preserves existing play history"); check(c.Data[(c.Owner.Id,c.NewId)].IsFavorite&&c.Data[(c.Owner.Id,c.NewId)].PlayCount==7,"native favorite migration preserves existing play history");
@@ -95,7 +96,7 @@ public static class ChannelSyncTests
public Guid OldId=Guid.NewGuid(),NewId=Guid.NewGuid(),OtherId=Guid.NewGuid(); public Guid OldId=Guid.NewGuid(),NewId=Guid.NewGuid(),OtherId=Guid.NewGuid();
public User Owner=new("sync-owner","test","test"),SecondOwner=new("sync-second","test","test"); public User Owner=new("sync-owner","test","test"),SecondOwner=new("sync-second","test","test");
public ChannelInfo[] Current={new(){Id=SavedChannelPlaybackTests.Current,Name="TV PULS HD",Number="1"}}; public ChannelInfo[] Current={new(){Id=SavedChannelPlaybackTests.Current,Name="TV PULS HD",Number="1"}};
public BaseItem[] Items;public int Executions,SourceCalls;public bool Fail; public BaseItem[] Items;public int Executions,SourceCalls;public bool Fail,NativeRuntimePolicy;
public Dictionary<(Guid,Guid),UserItemData> Data=new(); public Dictionary<(Guid,Guid),UserItemData> Data=new();
public PreferencesStore Store;public ChannelSyncTask Task;public Worker Worker=new(); public PreferencesStore Store;public ChannelSyncTask Task;public Worker Worker=new();
public TaskCompletionSource Block;public TaskCompletionSource Started=new(TaskCreationOptions.RunContinuationsAsynchronously); public TaskCompletionSource Block;public TaskCompletionSource Started=new(TaskCreationOptions.RunContinuationsAsynchronously);
@@ -135,7 +136,7 @@ public static class ChannelSyncTests
case "get_Name":return "Enigma2"; case "get_Name":return "Enigma2";
case "GetChannelsAsync":c.SourceCalls++;return System.Threading.Tasks.Task.FromResult<IEnumerable<ChannelInfo>>(c.Current); case "GetChannelsAsync":c.SourceCalls++;return System.Threading.Tasks.Task.FromResult<IEnumerable<ChannelInfo>>(c.Current);
case "get_ScheduledTasks":return new IScheduledTaskWorker[]{c.Worker}; case "get_ScheduledTasks":return new IScheduledTaskWorker[]{c.Worker};
case "Execute":if(!ReferenceEquals(args[0],c.Worker))throw new Exception("Wrong native task");return c.Execute(); case "Execute":if(!ReferenceEquals(args[0],c.Worker))throw new Exception("Wrong native task");c.NativeRuntimePolicy=((TaskOptions)args[1]).MaxRuntimeTicks==null;return c.Execute();
case "GetItemList":return c.Items; case "GetItemList":return c.Items;
case "GetUsers":return new[]{c.Owner,c.SecondOwner}; case "GetUsers":return new[]{c.Owner,c.SecondOwner};
case "GetUserDataBatch": case "GetUserDataBatch":
+1 -1
View File
@@ -8,7 +8,7 @@ guid: "193f29f9-ea6c-4595-a6f6-55e79d7c590a"
# The version of the plugin, starting at 1. Note that the AssemblyVersion # The version of the plugin, starting at 1. Note that the AssemblyVersion
# and FileVersion flags in the `.csproj` file must have two additional # and FileVersion flags in the `.csproj` file must have two additional
# fiels, e.g. 1.0.0 to be built correctly. # fiels, e.g. 1.0.0 to be built correctly.
version: "7.0.10.0" # Please increment with each pull request version: "7.0.11.0" # Please increment with each pull request
# The supported Jellyfin version, usually the earliest binary-compatible # The supported Jellyfin version, usually the earliest binary-compatible
# version. Based on the Jellyfin components from NuGet. # version. Based on the Jellyfin components from NuGet.
+1 -1
View File
@@ -13,7 +13,7 @@ import urllib.error
import urllib.request import urllib.request
import uuid import uuid
VERSION = '7.0.10.0' VERSION = '7.0.11.0'
ABI = '12.2.0.0' ABI = '12.2.0.0'
GUID = '193f29f9-ea6c-4595-a6f6-55e79d7c590a' GUID = '193f29f9-ea6c-4595-a6f6-55e79d7c590a'
DLL = 'Jellyfin.Plugin.Enigma2.dll' DLL = 'Jellyfin.Plugin.Enigma2.dll'