From 7f71d3419c0652024784fc95334ae33d9fce3bb8 Mon Sep 17 00:00:00 2001 From: Duncan Tourolle Date: Sat, 20 Dec 2025 14:28:39 +0100 Subject: [PATCH] Add repo manifest Use title cards when non provided or is livestream --- .gitea/workflows/release.yaml | 42 ++++++++++ .../Channels/SRFPlayChannel.cs | 62 +++++++++++---- .../Configuration/PluginConfiguration.cs | 7 ++ .../Configuration/configPage.html | 9 +++ .../Controllers/StreamProxyController.cs | 34 ++++++++ .../Jellyfin.Plugin.SRFPlay.csproj | 6 ++ .../Utilities/PlaceholderImageGenerator.cs | 73 ++++++++++++++++++ README.md | 12 ++- assests/main logo.png | Bin 0 -> 20850 bytes manifest.json | 12 +++ 10 files changed, 243 insertions(+), 14 deletions(-) create mode 100644 Jellyfin.Plugin.SRFPlay/Utilities/PlaceholderImageGenerator.cs create mode 100644 assests/main logo.png create mode 100644 manifest.json diff --git a/.gitea/workflows/release.yaml b/.gitea/workflows/release.yaml index 6b14277..b14b9cb 100644 --- a/.gitea/workflows/release.yaml +++ b/.gitea/workflows/release.yaml @@ -70,6 +70,13 @@ jobs: echo "artifact_name=${ARTIFACT_NAME}" >> $GITHUB_OUTPUT echo "Found artifact: ${ARTIFACT}" + - name: Calculate checksum + id: checksum + run: | + CHECKSUM=$(md5sum "${{ steps.jprm.outputs.artifact }}" | awk '{print $1}') + echo "checksum=${CHECKSUM}" >> $GITHUB_OUTPUT + echo "Checksum: ${CHECKSUM}" + - name: Create Release env: GITEA_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -126,3 +133,38 @@ jobs: echo "✅ Release created successfully!" echo "View at: ${GITEA_URL}/${REPO_OWNER}/${REPO_NAME}/releases/tag/${{ steps.get_version.outputs.version }}" + + - name: Update manifest.json + env: + GITEA_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + REPO_OWNER="${{ github.repository_owner }}" + REPO_NAME="${{ github.event.repository.name }}" + GITEA_URL="${{ github.server_url }}" + VERSION="${{ steps.get_version.outputs.version_number }}" + CHECKSUM="${{ steps.checksum.outputs.checksum }}" + ARTIFACT_NAME="${{ steps.jprm.outputs.artifact_name }}" + TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ") + DOWNLOAD_URL="${GITEA_URL}/${REPO_OWNER}/${REPO_NAME}/releases/download/${{ steps.get_version.outputs.version }}/${ARTIFACT_NAME}" + + git config user.name "Gitea Actions" + git config user.email "actions@gitea.tourolle.paris" + git fetch origin master + git checkout master + + NEW_VERSION=$(cat < manifest.tmp && mv manifest.tmp manifest.json + git add manifest.json + git commit -m "Update manifest.json for version ${VERSION}" + git push origin master diff --git a/Jellyfin.Plugin.SRFPlay/Channels/SRFPlayChannel.cs b/Jellyfin.Plugin.SRFPlay/Channels/SRFPlayChannel.cs index 5879b95..b8625c8 100644 --- a/Jellyfin.Plugin.SRFPlay/Channels/SRFPlayChannel.cs +++ b/Jellyfin.Plugin.SRFPlay/Channels/SRFPlayChannel.cs @@ -107,10 +107,19 @@ public class SRFPlayChannel : IChannel, IHasCacheKey /// public Task GetChannelImage(ImageType type, CancellationToken cancellationToken) { - // Could provide a channel logo here + var assembly = GetType().Assembly; + var resourceStream = assembly.GetManifestResourceStream("Jellyfin.Plugin.SRFPlay.Images.logo.png"); + + if (resourceStream == null) + { + return Task.FromResult(new DynamicImageResponse { HasImage = false }); + } + return Task.FromResult(new DynamicImageResponse { - HasImage = false + HasImage = true, + Stream = resourceStream, + Format = MediaBrowser.Model.Drawing.ImageFormat.Png }); } @@ -491,21 +500,36 @@ public class SRFPlayChannel : IChannel, IHasCacheKey // Build overview var overview = chapter.Description ?? chapter.Lead; - // Get image URL - prefer chapter image, fall back to show image if available - var originalImageUrl = chapter.ImageUrl; - if (string.IsNullOrEmpty(originalImageUrl) && mediaComposition.Show != null) - { - originalImageUrl = mediaComposition.Show.ImageUrl; - _logger.LogDebug("URN {Urn}: Using show image as fallback", urn); - } + // Determine image URL based on configuration + string? imageUrl; + var generateTitleCards = config.GenerateTitleCards; - if (string.IsNullOrEmpty(originalImageUrl)) + if (generateTitleCards) { - _logger.LogWarning("URN {Urn}: No image URL available for '{Title}'", urn, chapter.Title); + // Generate title card with content name + _logger.LogDebug("URN {Urn}: Generating title card for '{Title}'", urn, chapter.Title); + imageUrl = CreatePlaceholderImageUrl(chapter.Title ?? "SRF Play", _mediaSourceFactory.GetServerBaseUrl()); } + else + { + // Use SRF-provided images - prefer chapter image, fall back to show image + var originalImageUrl = chapter.ImageUrl; + if (string.IsNullOrEmpty(originalImageUrl) && mediaComposition.Show != null) + { + originalImageUrl = mediaComposition.Show.ImageUrl; + _logger.LogDebug("URN {Urn}: Using show image as fallback", urn); + } - // Proxy image URL to fix Content-Type headers from SRF CDN - var imageUrl = CreateProxiedImageUrl(originalImageUrl, _mediaSourceFactory.GetServerBaseUrl()); + if (string.IsNullOrEmpty(originalImageUrl)) + { + _logger.LogDebug("URN {Urn}: No image URL available for '{Title}', using placeholder", urn, chapter.Title); + imageUrl = CreatePlaceholderImageUrl(chapter.Title ?? "SRF Play", _mediaSourceFactory.GetServerBaseUrl()); + } + else + { + imageUrl = CreateProxiedImageUrl(originalImageUrl, _mediaSourceFactory.GetServerBaseUrl()); + } + } // Use ValidFrom for premiere date if this is a scheduled livestream, otherwise use Date var premiereDate = chapter.Type == "SCHEDULED_LIVESTREAM" ? chapter.ValidFrom?.ToUniversalTime() : chapter.Date?.ToUniversalTime(); @@ -581,6 +605,18 @@ public class SRFPlayChannel : IChannel, IHasCacheKey return $"{serverUrl}/Plugins/SRFPlay/Proxy/Image?url={encodedUrl}"; } + /// + /// Creates a placeholder image URL with the given text. + /// + /// The text to display on the placeholder. + /// The server base URL. + /// The placeholder image URL. + private static string CreatePlaceholderImageUrl(string text, string serverUrl) + { + var encodedText = Convert.ToBase64String(Encoding.UTF8.GetBytes(text)); + return $"{serverUrl}/Plugins/SRFPlay/Proxy/Placeholder?text={encodedText}"; + } + /// public bool IsEnabledFor(string userId) { diff --git a/Jellyfin.Plugin.SRFPlay/Configuration/PluginConfiguration.cs b/Jellyfin.Plugin.SRFPlay/Configuration/PluginConfiguration.cs index 417ff0c..abd4c93 100644 --- a/Jellyfin.Plugin.SRFPlay/Configuration/PluginConfiguration.cs +++ b/Jellyfin.Plugin.SRFPlay/Configuration/PluginConfiguration.cs @@ -74,6 +74,7 @@ public class PluginConfiguration : BasePluginConfiguration EnableTrendingContent = true; EnableCategoryFolders = true; EnabledTopics = new System.Collections.Generic.List(); + GenerateTitleCards = true; } /// @@ -149,4 +150,10 @@ public class PluginConfiguration : BasePluginConfiguration /// This is important for Android and other remote clients to access streams. /// public string PublicServerUrl { get; set; } = string.Empty; + + /// + /// Gets or sets a value indicating whether to generate title card images with the content title. + /// When enabled, generates custom thumbnails instead of using SRF-provided images. + /// + public bool GenerateTitleCards { get; set; } } diff --git a/Jellyfin.Plugin.SRFPlay/Configuration/configPage.html b/Jellyfin.Plugin.SRFPlay/Configuration/configPage.html index 1d34e2d..31379ad 100644 --- a/Jellyfin.Plugin.SRFPlay/Configuration/configPage.html +++ b/Jellyfin.Plugin.SRFPlay/Configuration/configPage.html @@ -58,6 +58,13 @@
Automatically discover and add trending videos
+
+ +
Generate custom thumbnails with the content title instead of using SRF-provided images
+

Proxy Settings

@@ -117,6 +124,7 @@ document.querySelector('#CacheDurationMinutes').value = config.CacheDurationMinutes; document.querySelector('#EnableLatestContent').checked = config.EnableLatestContent; document.querySelector('#EnableTrendingContent').checked = config.EnableTrendingContent; + document.querySelector('#GenerateTitleCards').checked = config.GenerateTitleCards !== false; document.querySelector('#UseProxy').checked = config.UseProxy || false; document.querySelector('#ProxyAddress').value = config.ProxyAddress || ''; document.querySelector('#ProxyUsername').value = config.ProxyUsername || ''; @@ -137,6 +145,7 @@ config.CacheDurationMinutes = parseInt(document.querySelector('#CacheDurationMinutes').value); config.EnableLatestContent = document.querySelector('#EnableLatestContent').checked; config.EnableTrendingContent = document.querySelector('#EnableTrendingContent').checked; + config.GenerateTitleCards = document.querySelector('#GenerateTitleCards').checked; config.UseProxy = document.querySelector('#UseProxy').checked; config.ProxyAddress = document.querySelector('#ProxyAddress').value; config.ProxyUsername = document.querySelector('#ProxyUsername').value; diff --git a/Jellyfin.Plugin.SRFPlay/Controllers/StreamProxyController.cs b/Jellyfin.Plugin.SRFPlay/Controllers/StreamProxyController.cs index 88d8186..03e1aea 100644 --- a/Jellyfin.Plugin.SRFPlay/Controllers/StreamProxyController.cs +++ b/Jellyfin.Plugin.SRFPlay/Controllers/StreamProxyController.cs @@ -461,4 +461,38 @@ public class StreamProxyController : ControllerBase return StatusCode(StatusCodes.Status502BadGateway); } } + + /// + /// Generates a placeholder image with the given text centered. + /// + /// The text to display (base64 encoded). + /// A PNG image with the text centered. + [HttpGet("Placeholder")] + [AllowAnonymous] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + public IActionResult GetPlaceholder([FromQuery] string text) + { + if (string.IsNullOrEmpty(text)) + { + return BadRequest("Text parameter is required"); + } + + string decodedText; + try + { + var bytes = Convert.FromBase64String(text); + decodedText = System.Text.Encoding.UTF8.GetString(bytes); + } + catch (FormatException) + { + _logger.LogWarning("Invalid base64 text parameter: {Text}", text); + return BadRequest("Invalid text encoding"); + } + + _logger.LogDebug("Generating placeholder image for: {Text}", decodedText); + + var imageStream = PlaceholderImageGenerator.GeneratePlaceholder(decodedText); + return File(imageStream, "image/png"); + } } diff --git a/Jellyfin.Plugin.SRFPlay/Jellyfin.Plugin.SRFPlay.csproj b/Jellyfin.Plugin.SRFPlay/Jellyfin.Plugin.SRFPlay.csproj index 5251330..f9005cf 100644 --- a/Jellyfin.Plugin.SRFPlay/Jellyfin.Plugin.SRFPlay.csproj +++ b/Jellyfin.Plugin.SRFPlay/Jellyfin.Plugin.SRFPlay.csproj @@ -14,6 +14,7 @@ + @@ -27,4 +28,9 @@ + + + + + diff --git a/Jellyfin.Plugin.SRFPlay/Utilities/PlaceholderImageGenerator.cs b/Jellyfin.Plugin.SRFPlay/Utilities/PlaceholderImageGenerator.cs new file mode 100644 index 0000000..3d05157 --- /dev/null +++ b/Jellyfin.Plugin.SRFPlay/Utilities/PlaceholderImageGenerator.cs @@ -0,0 +1,73 @@ +using System.IO; +using SkiaSharp; + +namespace Jellyfin.Plugin.SRFPlay.Utilities; + +/// +/// Generates placeholder images for content without thumbnails. +/// +public static class PlaceholderImageGenerator +{ + private const int Width = 640; + private const int Height = 360; // 16:9 aspect ratio + + /// + /// Generates a placeholder image with the given text centered. + /// + /// The text to display (typically channel/show name). + /// A memory stream containing the PNG image. + public static MemoryStream GeneratePlaceholder(string text) + { + using var surface = SKSurface.Create(new SKImageInfo(Width, Height)); + var canvas = surface.Canvas; + + // Dark gradient background + using var backgroundPaint = new SKPaint(); + using var shader = SKShader.CreateLinearGradient( + new SKPoint(0, 0), + new SKPoint(Width, Height), + new[] { new SKColor(45, 45, 48), new SKColor(28, 28, 30) }, + null, + SKShaderTileMode.Clamp); + backgroundPaint.Shader = shader; + canvas.DrawRect(0, 0, Width, Height, backgroundPaint); + + // Text + using var textPaint = new SKPaint + { + Color = SKColors.White, + IsAntialias = true, + TextAlign = SKTextAlign.Center, + Typeface = SKTypeface.FromFamilyName("Arial", SKFontStyle.Bold) + }; + + // Calculate font size to fit text (max 48, min 24) + float fontSize = 48; + textPaint.TextSize = fontSize; + var textWidth = textPaint.MeasureText(text); + var maxWidth = Width * 0.85f; + + if (textWidth > maxWidth) + { + fontSize = fontSize * maxWidth / textWidth; + fontSize = fontSize < 24 ? 24 : fontSize; + textPaint.TextSize = fontSize; + } + + // Center text vertically + SKRect textBounds = default; + textPaint.MeasureText(text, ref textBounds); + var yPos = (Height / 2) - textBounds.MidY; + + canvas.DrawText(text, Width / 2, yPos, textPaint); + + // Encode to PNG + using var image = surface.Snapshot(); + using var data = image.Encode(SKEncodedImageFormat.Png, 100); + + var stream = new MemoryStream(); + data.SaveTo(stream); + stream.Position = 0; + return stream; + } +} diff --git a/README.md b/README.md index aab9f47..0474ee9 100644 --- a/README.md +++ b/README.md @@ -102,7 +102,17 @@ dotnet build The compiled plugin will be in `bin/Debug/net8.0/` -## Installation +## Quick Install + +Add this repository URL in Jellyfin (Dashboard → Plugins → Repositories): + +``` +https://gitea.tourolle.paris/dtourolle/jellyfin-srfPlay/raw/branch/master/manifest.json +``` + +Then install "SRF Play" from the plugin catalog. + +## Manual Installation 1. Build the plugin (see above) 2. Copy the compiled DLL to your Jellyfin plugins directory diff --git a/assests/main logo.png b/assests/main logo.png new file mode 100644 index 0000000000000000000000000000000000000000..06c4b2ccfde87c7e170934559a96d8f75308d1a7 GIT binary patch literal 20850 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