aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCody Robibero <cody@robibe.ro>2026-06-27 09:52:51 -0400
committerGitHub <noreply@github.com>2026-06-27 09:52:51 -0400
commitcbef19c313ba0f05f13e176ced6349122c4a7d88 (patch)
tree6ebcae8bcd6ae765b9f6095ca20c5763c08f7541
parentfc13a7ca7d4fbd2753f10e201e42bd1eb1dabf66 (diff)
parent1ea525a4083dbdc929605eb0eb5c6add93bc8392 (diff)
Merge pull request #16914 from danieltutuianu/fix/livetv-channel-icon-refresh
Live TV: re-fetch channel icons on guide refresh
-rw-r--r--src/Jellyfin.LiveTv/Channels/ChannelManager.cs4
-rw-r--r--src/Jellyfin.LiveTv/Guide/GuideManager.cs19
-rw-r--r--src/Jellyfin.LiveTv/LiveTvChannelImageHelper.cs33
-rw-r--r--tests/Jellyfin.LiveTv.Tests/LiveTvChannelImageHelperTests.cs51
4 files changed, 89 insertions, 18 deletions
diff --git a/src/Jellyfin.LiveTv/Channels/ChannelManager.cs b/src/Jellyfin.LiveTv/Channels/ChannelManager.cs
index ed02fe6a1d..e421601092 100644
--- a/src/Jellyfin.LiveTv/Channels/ChannelManager.cs
+++ b/src/Jellyfin.LiveTv/Channels/ChannelManager.cs
@@ -14,6 +14,7 @@ using Jellyfin.Database.Implementations.Entities;
using Jellyfin.Database.Implementations.Enums;
using Jellyfin.Extensions;
using Jellyfin.Extensions.Json;
+using Jellyfin.LiveTv;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration;
@@ -1109,9 +1110,8 @@ namespace Jellyfin.LiveTv.Channels
item.Path = mediaSource?.Path;
}
- if (!string.IsNullOrEmpty(info.ImageUrl) && !item.HasImage(ImageType.Primary))
+ if (LiveTvChannelImageHelper.UpdateChannelImageIfNeeded(item, null, info.ImageUrl))
{
- item.SetImagePath(ImageType.Primary, info.ImageUrl);
_logger.LogDebug("Forcing update due to ImageUrl {0}", item.Name);
forceUpdate = true;
}
diff --git a/src/Jellyfin.LiveTv/Guide/GuideManager.cs b/src/Jellyfin.LiveTv/Guide/GuideManager.cs
index c3cc70381e..b8545cbb64 100644
--- a/src/Jellyfin.LiveTv/Guide/GuideManager.cs
+++ b/src/Jellyfin.LiveTv/Guide/GuideManager.cs
@@ -5,6 +5,7 @@ using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Data.Enums;
using Jellyfin.Extensions;
+using Jellyfin.LiveTv;
using Jellyfin.LiveTv.Configuration;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Dto;
@@ -448,23 +449,9 @@ public class GuideManager : IGuideManager
item.Name = channelInfo.Name;
- var currentPrimary = item.GetImageInfo(ImageType.Primary, 0);
- var imageUrlIsNull = string.IsNullOrWhiteSpace(channelInfo.ImageUrl);
-
- // Update channel image if image URL has changed
- if (currentPrimary is null
- || (!imageUrlIsNull && !string.Equals(currentPrimary.Path, channelInfo.ImageUrl, StringComparison.Ordinal)))
+ if (LiveTvChannelImageHelper.UpdateChannelImageIfNeeded(item, channelInfo.ImagePath, channelInfo.ImageUrl))
{
- if (!string.IsNullOrWhiteSpace(channelInfo.ImagePath))
- {
- item.SetImagePath(ImageType.Primary, channelInfo.ImagePath);
- forceUpdate = true;
- }
- else if (!imageUrlIsNull)
- {
- item.SetImagePath(ImageType.Primary, channelInfo.ImageUrl);
- forceUpdate = true;
- }
+ forceUpdate = true;
}
if (isNew)
diff --git a/src/Jellyfin.LiveTv/LiveTvChannelImageHelper.cs b/src/Jellyfin.LiveTv/LiveTvChannelImageHelper.cs
new file mode 100644
index 0000000000..a590193b5f
--- /dev/null
+++ b/src/Jellyfin.LiveTv/LiveTvChannelImageHelper.cs
@@ -0,0 +1,33 @@
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Model.Entities;
+
+namespace Jellyfin.LiveTv;
+
+/// <summary>
+/// Helpers for keeping Live TV channel icons in sync with guide data.
+/// </summary>
+internal static class LiveTvChannelImageHelper
+{
+ /// <summary>
+ /// Applies the channel icon from guide or tuner metadata.
+ /// Called on each guide refresh so remote icons are re-downloaded even when the URL is unchanged.
+ /// </summary>
+ /// <param name="item">The channel item.</param>
+ /// <param name="imagePath">The local image path from the tuner, if any.</param>
+ /// <param name="imageUrl">The remote image URL from the guide provider, if any.</param>
+ /// <returns><c>true</c> when the item image metadata was updated.</returns>
+ internal static bool UpdateChannelImageIfNeeded(BaseItem item, string? imagePath, string? imageUrl)
+ {
+ var newImageSource = !string.IsNullOrWhiteSpace(imagePath)
+ ? imagePath
+ : imageUrl;
+
+ if (string.IsNullOrWhiteSpace(newImageSource))
+ {
+ return false;
+ }
+
+ item.SetImagePath(ImageType.Primary, newImageSource);
+ return true;
+ }
+}
diff --git a/tests/Jellyfin.LiveTv.Tests/LiveTvChannelImageHelperTests.cs b/tests/Jellyfin.LiveTv.Tests/LiveTvChannelImageHelperTests.cs
new file mode 100644
index 0000000000..f44cb88834
--- /dev/null
+++ b/tests/Jellyfin.LiveTv.Tests/LiveTvChannelImageHelperTests.cs
@@ -0,0 +1,51 @@
+using Jellyfin.LiveTv;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.LiveTv;
+using MediaBrowser.Model.Entities;
+using Xunit;
+
+namespace Jellyfin.LiveTv.Tests;
+
+public class LiveTvChannelImageHelperTests
+{
+ [Fact]
+ public void UpdateChannelImageIfNeeded_NoSource_DoesNotUpdate()
+ {
+ var channel = new LiveTvChannel { Name = "Test Channel" };
+
+ var updated = LiveTvChannelImageHelper.UpdateChannelImageIfNeeded(channel, null, null);
+
+ Assert.False(updated);
+ Assert.False(channel.HasImage(ImageType.Primary));
+ }
+
+ [Fact]
+ public void UpdateChannelImageIfNeeded_WithUrl_AppliesUrl()
+ {
+ var channel = new LiveTvChannel { Name = "Test Channel" };
+
+ var updated = LiveTvChannelImageHelper.UpdateChannelImageIfNeeded(
+ channel,
+ null,
+ "https://example.com/icon.png");
+
+ Assert.True(updated);
+ Assert.True(channel.HasImage(ImageType.Primary));
+ Assert.Equal("https://example.com/icon.png", channel.GetImagePath(ImageType.Primary));
+ }
+
+ [Fact]
+ public void UpdateChannelImageIfNeeded_SameUrl_StillUpdates()
+ {
+ var channel = new LiveTvChannel { Name = "Test Channel" };
+ LiveTvChannelImageHelper.UpdateChannelImageIfNeeded(channel, null, "https://example.com/icon.png");
+
+ var updated = LiveTvChannelImageHelper.UpdateChannelImageIfNeeded(
+ channel,
+ null,
+ "https://example.com/icon.png");
+
+ Assert.True(updated);
+ Assert.Equal("https://example.com/icon.png", channel.GetImagePath(ImageType.Primary));
+ }
+}