aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MediaBrowser.Controller/LiveTv/ISchedulesDirectService.cs7
-rw-r--r--src/Jellyfin.LiveTv/Guide/GuideManager.cs8
-rw-r--r--src/Jellyfin.LiveTv/Listings/SchedulesDirect.cs26
3 files changed, 36 insertions, 5 deletions
diff --git a/MediaBrowser.Controller/LiveTv/ISchedulesDirectService.cs b/MediaBrowser.Controller/LiveTv/ISchedulesDirectService.cs
index a33b4422b2..6953650952 100644
--- a/MediaBrowser.Controller/LiveTv/ISchedulesDirectService.cs
+++ b/MediaBrowser.Controller/LiveTv/ISchedulesDirectService.cs
@@ -21,4 +21,11 @@ public interface ISchedulesDirectService
/// </summary>
/// <returns><c>true</c> if the image limit has been hit and has not yet reset; otherwise <c>false</c>.</returns>
bool IsImageDailyLimitActive();
+
+ /// <summary>
+ /// Gets a value indicating whether the Schedules Direct service is available.
+ /// Returns <c>false</c> if a permanent account error has occurred or a transient backoff is active.
+ /// </summary>
+ /// <returns><c>true</c> if the service can accept requests; otherwise <c>false</c>.</returns>
+ bool IsServiceAvailable();
}
diff --git a/src/Jellyfin.LiveTv/Guide/GuideManager.cs b/src/Jellyfin.LiveTv/Guide/GuideManager.cs
index a659cc020b..556516674b 100644
--- a/src/Jellyfin.LiveTv/Guide/GuideManager.cs
+++ b/src/Jellyfin.LiveTv/Guide/GuideManager.cs
@@ -738,6 +738,14 @@ public class GuideManager : IGuideManager
_cacheParallelOptions,
async (program, cancellationToken) =>
{
+ // Re-check: limit may have been set by a parallel task since the LINQ filter ran.
+ if (_schedulesDirectService.IsImageDailyLimitActive()
+ && program.ImageInfos.All(
+ img => img.IsLocalFile || img.Path.Contains("schedulesdirect", StringComparison.OrdinalIgnoreCase)))
+ {
+ return;
+ }
+
for (var i = 0; i < program.ImageInfos.Length; i++)
{
if (cancellationToken.IsCancellationRequested)
diff --git a/src/Jellyfin.LiveTv/Listings/SchedulesDirect.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirect.cs
index 7b97dcc8db..3aa0f0408b 100644
--- a/src/Jellyfin.LiveTv/Listings/SchedulesDirect.cs
+++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirect.cs
@@ -45,8 +45,8 @@ namespace Jellyfin.LiveTv.Listings
private readonly ConcurrentDictionary<string, NameValuePair> _tokens = new();
private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options;
- private DateTime _lastErrorResponse;
- private bool _accountError;
+ private long _lastErrorResponseTicks;
+ private volatile bool _accountError;
private bool _disposed = false;
private byte[] _countriesCache;
@@ -594,7 +594,7 @@ namespace Jellyfin.LiveTv.Listings
}
// Avoid hammering SD after transient login failures (e.g. max attempts / temporary lockout)
- if ((DateTime.UtcNow - _lastErrorResponse).TotalMinutes < 30)
+ if ((DateTime.UtcNow - new DateTime(Interlocked.Read(ref _lastErrorResponseTicks), DateTimeKind.Utc)).TotalMinutes < 30)
{
return null;
}
@@ -635,7 +635,7 @@ namespace Jellyfin.LiveTv.Listings
&& (int)ex.StatusCode.Value < 500)
{
_tokens.Clear();
- _lastErrorResponse = DateTime.UtcNow;
+ Interlocked.Exchange(ref _lastErrorResponseTicks, DateTime.UtcNow.Ticks);
}
throw;
@@ -695,7 +695,7 @@ namespace Jellyfin.LiveTv.Listings
{
// Transient login errors — back off for 30 minutes, then allow retry.
_tokens.Clear();
- _lastErrorResponse = DateTime.UtcNow;
+ Interlocked.Exchange(ref _lastErrorResponseTicks, DateTime.UtcNow.Ticks);
}
else if (sdCode is SdErrorCode.MaxImageDownloads)
{
@@ -877,6 +877,22 @@ namespace Jellyfin.LiveTv.Listings
}
/// <inheritdoc />
+ public bool IsServiceAvailable()
+ {
+ if (_accountError)
+ {
+ return false;
+ }
+
+ if ((DateTime.UtcNow - new DateTime(Interlocked.Read(ref _lastErrorResponseTicks), DateTimeKind.Utc)).TotalMinutes < 30)
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ /// <inheritdoc />
public bool IsImageDailyLimitActive()
{
if (!_imageLimitHitDate.HasValue)