Compare commits

..
Author SHA1 Message Date
Gitea Actions affc87bd38 Nightly: 1.0.20260627.185 (1101385)
🚀 Release Plugin / build-and-release (push) Successful in 31s
2026-06-27 09:30:57 +00:00
dtourolle 1101385107 Really fix CI
🏗️ Build Plugin / build (push) Successful in 36s
Nightly Build / nightly-build (push) Successful in 43s
🧪 Test Plugin / test (push) Successful in 28s
2026-06-27 11:29:32 +02:00
dtourolle 4fc79f39f7 Fix CI
🏗️ Build Plugin / build (push) Successful in 41s
Nightly Build / nightly-build (push) Failing after 35s
🧪 Test Plugin / test (push) Successful in 30s
2026-06-27 11:23:53 +02:00
dtourolle 5875f81b9b fix correct guid
🏗️ Build Plugin / build (push) Successful in 42s
Nightly Build / nightly-build (push) Failing after 35s
🧪 Test Plugin / test (push) Successful in 28s
2026-06-27 11:18:48 +02:00
dtourolle cedef6d6aa Add nightly job
🏗️ Build Plugin / build (push) Successful in 29s
Nightly Build / nightly-build (push) Has been cancelled
🧪 Test Plugin / test (push) Has been cancelled
2026-06-27 11:17:57 +02:00
dtourolle 4b5d7e2a7f info add github cross-login info 2026-06-27 11:17:57 +02:00
Gitea Actions 8281960f0b Update manifest.json for latest build (7a719ee) 2026-06-27 09:01:57 +00:00
13 changed files with 143 additions and 143 deletions
+24 -1
View File
@@ -36,6 +36,29 @@ jobs:
working-directory: build-${{ github.run_id }} working-directory: build-${{ github.run_id }}
run: dotnet restore Jellyfin.Plugin.SRFPlay.sln run: dotnet restore Jellyfin.Plugin.SRFPlay.sln
- name: Compute build version
id: version
run: |
# For PRs, stamp a distinct version so a side-loaded build is
# identifiable in Jellyfin: 1.0.<YYYYMMDD>-pr<N>.<run_number>.
# For plain master pushes, keep a date-based dev version.
DATE=$(date -u +"%Y%m%d")
if [ -n "${{ github.event.pull_request.number }}" ]; then
VERSION="1.0.${DATE}.${{ github.run_number }}"
LABEL="pr${{ github.event.pull_request.number }}"
else
VERSION="1.0.${DATE}.${{ github.run_number }}"
LABEL="master"
fi
echo "version=${VERSION}" >> $GITHUB_OUTPUT
echo "label=${LABEL}" >> $GITHUB_OUTPUT
echo "Build version: ${VERSION} (${LABEL})"
- name: Set build version
working-directory: build-${{ github.run_id }}
run: |
sed -i "s/^version:.*/version: \"${{ steps.version.outputs.version }}\"/" build.yaml
- name: Build solution - name: Build solution
working-directory: build-${{ github.run_id }} working-directory: build-${{ github.run_id }}
run: dotnet build Jellyfin.Plugin.SRFPlay.sln --configuration Release --no-restore --no-self-contained /m:1 run: dotnet build Jellyfin.Plugin.SRFPlay.sln --configuration Release --no-restore --no-self-contained /m:1
@@ -59,7 +82,7 @@ jobs:
- name: Upload build artifact - name: Upload build artifact
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: jellyfin-srfplay-plugin name: srfplay-${{ steps.version.outputs.label }}-${{ steps.version.outputs.version }}
path: build-${{ github.run_id }}/${{ steps.jprm.outputs.artifact }} path: build-${{ github.run_id }}/${{ steps.jprm.outputs.artifact }}
retention-days: 30 retention-days: 30
if-no-files-found: error if-no-files-found: error
+39 -16
View File
@@ -1,4 +1,4 @@
name: 'Latest Release' name: 'Nightly Build'
on: on:
push: push:
@@ -7,9 +7,10 @@ on:
paths-ignore: paths-ignore:
- '**/*.md' - '**/*.md'
- 'manifest.json' - 'manifest.json'
- 'manifest-nightly.json'
jobs: jobs:
latest-release: nightly-build:
runs-on: linux/amd64 runs-on: linux/amd64
container: container:
image: gitea.tourolle.paris/dtourolle/srfplay-builder:latest image: gitea.tourolle.paris/dtourolle/srfplay-builder:latest
@@ -20,6 +21,24 @@ jobs:
with: with:
path: build-${{ github.run_id }} path: build-${{ github.run_id }}
- name: Compute nightly version
id: version
run: |
# Date-based nightly version: 1.0.<YYYYMMDD>.<run_number>
# The run_number suffix keeps multiple builds on the same day
# monotonically increasing so Jellyfin always offers the newest.
DATE=$(date -u +"%Y%m%d")
VERSION="1.0.${DATE}.${{ github.run_number }}"
echo "version=${VERSION}" >> $GITHUB_OUTPUT
echo "Nightly version: ${VERSION}"
- name: Set build version
working-directory: build-${{ github.run_id }}
run: |
VERSION="${{ steps.version.outputs.version }}"
sed -i "s/^version:.*/version: \"${VERSION}\"/" build.yaml
cat build.yaml
- name: Restore dependencies - name: Restore dependencies
working-directory: build-${{ github.run_id }} working-directory: build-${{ github.run_id }}
run: dotnet restore Jellyfin.Plugin.SRFPlay.sln run: dotnet restore Jellyfin.Plugin.SRFPlay.sln
@@ -52,7 +71,7 @@ jobs:
echo "checksum=${CHECKSUM}" >> $GITHUB_OUTPUT echo "checksum=${CHECKSUM}" >> $GITHUB_OUTPUT
echo "Checksum: ${CHECKSUM}" echo "Checksum: ${CHECKSUM}"
- name: Delete existing latest release - name: Delete existing nightly release
working-directory: build-${{ github.run_id }} working-directory: build-${{ github.run_id }}
env: env:
GITEA_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITEA_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -71,7 +90,7 @@ jobs:
-H "Authorization: token ${GITEA_TOKEN}" \ -H "Authorization: token ${GITEA_TOKEN}" \
"${GITEA_URL}/api/v1/repos/${REPO_OWNER}/${REPO_NAME}/releases/tags/${TAG}" | jq -r '.id') "${GITEA_URL}/api/v1/repos/${REPO_OWNER}/${REPO_NAME}/releases/tags/${TAG}" | jq -r '.id')
echo "Deleting existing latest release (ID: ${RELEASE_ID})..." echo "Deleting existing nightly release (ID: ${RELEASE_ID})..."
curl -s -X DELETE \ curl -s -X DELETE \
-H "Authorization: token ${GITEA_TOKEN}" \ -H "Authorization: token ${GITEA_TOKEN}" \
"${GITEA_URL}/api/v1/repos/${REPO_OWNER}/${REPO_NAME}/releases/${RELEASE_ID}" "${GITEA_URL}/api/v1/repos/${REPO_OWNER}/${REPO_NAME}/releases/${RELEASE_ID}"
@@ -81,7 +100,7 @@ jobs:
-H "Authorization: token ${GITEA_TOKEN}" \ -H "Authorization: token ${GITEA_TOKEN}" \
"${GITEA_URL}/api/v1/repos/${REPO_OWNER}/${REPO_NAME}/tags/${TAG}" || true "${GITEA_URL}/api/v1/repos/${REPO_OWNER}/${REPO_NAME}/tags/${TAG}" || true
- name: Create latest release - name: Create nightly release
working-directory: build-${{ github.run_id }} working-directory: build-${{ github.run_id }}
env: env:
GITEA_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITEA_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -89,12 +108,14 @@ jobs:
REPO_OWNER="${{ github.repository_owner }}" REPO_OWNER="${{ github.repository_owner }}"
REPO_NAME="${{ github.event.repository.name }}" REPO_NAME="${{ github.event.repository.name }}"
GITEA_URL="${{ github.server_url }}" GITEA_URL="${{ github.server_url }}"
VERSION="${{ steps.version.outputs.version }}"
SHORT_SHA=$(echo "${{ github.sha }}" | cut -c1-7)
RESPONSE=$(curl -s -w "\n%{http_code}" -X POST \ RESPONSE=$(curl -s -w "\n%{http_code}" -X POST \
-H "Authorization: token ${GITEA_TOKEN}" \ -H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
"${GITEA_URL}/api/v1/repos/${REPO_OWNER}/${REPO_NAME}/releases" \ "${GITEA_URL}/api/v1/repos/${REPO_OWNER}/${REPO_NAME}/releases" \
-d "$(jq -n --arg tag "latest" --arg name "Latest Build" --arg body "SRFPlay Jellyfin Plugin latest build from master." '{tag_name: $tag, name: $name, body: $body, target_commitish: "master", draft: false, prerelease: true}')") -d "$(jq -n --arg tag "latest" --arg name "Nightly Build ${VERSION}" --arg body "SRFPlay Jellyfin Plugin nightly build ${VERSION} from master (${SHORT_SHA})." '{tag_name: $tag, name: $name, body: $body, target_commitish: "master", draft: false, prerelease: true}')")
HTTP_CODE=$(echo "$RESPONSE" | tail -n1) HTTP_CODE=$(echo "$RESPONSE" | tail -n1)
BODY=$(echo "$RESPONSE" | sed '$d') BODY=$(echo "$RESPONSE" | sed '$d')
@@ -124,9 +145,9 @@ jobs:
--data-binary "@build.yaml" \ --data-binary "@build.yaml" \
"${GITEA_URL}/api/v1/repos/${REPO_OWNER}/${REPO_NAME}/releases/${RELEASE_ID}/assets?name=build.yaml" "${GITEA_URL}/api/v1/repos/${REPO_OWNER}/${REPO_NAME}/releases/${RELEASE_ID}/assets?name=build.yaml"
echo "Latest release updated successfully!" echo "Nightly release updated successfully!"
- name: Update manifest.json - name: Update manifest-nightly.json
working-directory: build-${{ github.run_id }} working-directory: build-${{ github.run_id }}
env: env:
GITEA_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITEA_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -134,6 +155,7 @@ jobs:
REPO_OWNER="${{ github.repository_owner }}" REPO_OWNER="${{ github.repository_owner }}"
REPO_NAME="${{ github.event.repository.name }}" REPO_NAME="${{ github.event.repository.name }}"
GITEA_URL="${{ github.server_url }}" GITEA_URL="${{ github.server_url }}"
VERSION="${{ steps.version.outputs.version }}"
CHECKSUM="${{ steps.checksum.outputs.checksum }}" CHECKSUM="${{ steps.checksum.outputs.checksum }}"
ARTIFACT_NAME="${{ steps.jprm.outputs.artifact_name }}" ARTIFACT_NAME="${{ steps.jprm.outputs.artifact_name }}"
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ") TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
@@ -145,13 +167,10 @@ jobs:
git fetch origin master git fetch origin master
git checkout master git checkout master
# Remove existing "latest" entry if present, then prepend new one
jq --arg url "$DOWNLOAD_URL" 'if .[0].versions[0].changelog == "Latest Build" then .[0].versions = .[0].versions[1:] else . end' manifest.json > manifest.tmp && mv manifest.tmp manifest.json
NEW_VERSION=$(cat <<EOF NEW_VERSION=$(cat <<EOF
{ {
"version": "0.0.0.0", "version": "${VERSION}",
"changelog": "Latest Build", "changelog": "Nightly build ${VERSION} (${SHORT_SHA})",
"targetAbi": "10.9.0.0", "targetAbi": "10.9.0.0",
"sourceUrl": "${DOWNLOAD_URL}", "sourceUrl": "${DOWNLOAD_URL}",
"checksum": "${CHECKSUM}", "checksum": "${CHECKSUM}",
@@ -160,9 +179,13 @@ jobs:
EOF EOF
) )
jq --argjson newver "${NEW_VERSION}" '.[0].versions = [$newver] + .[0].versions' manifest.json > manifest.tmp && mv manifest.tmp manifest.json # Prepend the new build and keep only the most recent 5 nightlies.
git add manifest.json jq --argjson newver "${NEW_VERSION}" \
git commit -m "Update manifest.json for latest build (${SHORT_SHA})" || echo "No changes to commit" '.[0].versions = ([$newver] + .[0].versions)[0:5]' \
manifest-nightly.json > manifest.tmp && mv manifest.tmp manifest-nightly.json
git add manifest-nightly.json
git commit -m "Nightly: ${VERSION} (${SHORT_SHA})" || echo "No changes to commit"
git push origin master git push origin master
- name: Cleanup - name: Cleanup
+17
View File
@@ -0,0 +1,17 @@
<Project>
<!--
The Jellyfin meta build workflow rewrites Directory.Build.props, stamping Version,
AssemblyVersion and FileVersion all to a date-based value (e.g. 1.0.20260627.182).
The build segment (20260627) exceeds the 16-bit limit (0-65535) that AssemblyVersion
and FileVersion require, which fails the compile (CS7034/CS7035).
Directory.Build.targets is imported AFTER the project (and after Directory.Build.props),
and the workflow does not touch it, so we force valid assembly/file versions here. The
package/plugin manifest Version stays as injected; only the .NET assembly identity is
normalised, which is fine because Jellyfin identifies plugins by GUID + manifest version.
-->
<PropertyGroup>
<AssemblyVersion>1.0.0.0</AssemblyVersion>
<FileVersion>1.0.0.0</FileVersion>
</PropertyGroup>
</Project>
+18 -4
View File
@@ -20,7 +20,6 @@ namespace Jellyfin.Plugin.SRFPlay.Api;
/// </summary> /// </summary>
public class SRFApiClient : IDisposable public class SRFApiClient : IDisposable
{ {
private static readonly System.Text.CompositeFormat PlayV3UrlFormat = System.Text.CompositeFormat.Parse(ApiEndpoints.PlayV3BaseUrlTemplate);
private readonly HttpClient _httpClient; private readonly HttpClient _httpClient;
private readonly HttpClient _playV3HttpClient; private readonly HttpClient _playV3HttpClient;
private readonly ILogger _logger; private readonly ILogger _logger;
@@ -58,6 +57,21 @@ public class SRFApiClient : IDisposable
}; };
} }
/// <summary>
/// Builds the Play v3 API base URL for a business unit. The host is normally
/// www.&lt;unit&gt;.ch, but SWI's site lives at www.swissinfo.ch (www.swi.ch is an
/// unrelated welding institute), while the API path segment stays the unit code.
/// </summary>
/// <param name="businessUnit">The lowercase business unit (e.g. "srf", "swi").</param>
/// <returns>The Play v3 production base URL ending in a slash.</returns>
private static string BuildPlayV3BaseUrl(string businessUnit)
{
var host = string.Equals(businessUnit, "swi", StringComparison.OrdinalIgnoreCase)
? "www.swissinfo.ch"
: $"www.{businessUnit}.ch";
return $"https://{host}/play/v3/api/{businessUnit}/production/";
}
/// <summary> /// <summary>
/// Reads HTTP response content as UTF-8 string. /// Reads HTTP response content as UTF-8 string.
/// </summary> /// </summary>
@@ -315,7 +329,7 @@ public class SRFApiClient : IDisposable
{ {
try try
{ {
var baseUrl = string.Format(CultureInfo.InvariantCulture, PlayV3UrlFormat, businessUnit); var baseUrl = BuildPlayV3BaseUrl(businessUnit);
var url = $"{baseUrl}{endpoint}"; var url = $"{baseUrl}{endpoint}";
_logger.LogInformation("Fetching all {Endpoint} for business unit: {BusinessUnit} from URL: {Url}", endpoint, businessUnit, url); _logger.LogInformation("Fetching all {Endpoint} for business unit: {BusinessUnit} from URL: {Url}", endpoint, businessUnit, url);
@@ -352,7 +366,7 @@ public class SRFApiClient : IDisposable
{ {
try try
{ {
var baseUrl = string.Format(CultureInfo.InvariantCulture, PlayV3UrlFormat, businessUnit); var baseUrl = BuildPlayV3BaseUrl(businessUnit);
var url = $"{baseUrl}videos-by-show-id?showId={showId}"; var url = $"{baseUrl}videos-by-show-id?showId={showId}";
_logger.LogDebug("Fetching videos for show {ShowId} from business unit: {BusinessUnit}", showId, businessUnit); _logger.LogDebug("Fetching videos for show {ShowId} from business unit: {BusinessUnit}", showId, businessUnit);
@@ -392,7 +406,7 @@ public class SRFApiClient : IDisposable
{ {
try try
{ {
var baseUrl = string.Format(CultureInfo.InvariantCulture, PlayV3UrlFormat, businessUnit); var baseUrl = BuildPlayV3BaseUrl(businessUnit);
var url = $"{baseUrl}livestreams?eventType={eventType.ToUpperInvariant()}"; var url = $"{baseUrl}livestreams?eventType={eventType.ToUpperInvariant()}";
_logger.LogInformation("Fetching scheduled livestreams for eventType={EventType} from business unit: {BusinessUnit}", eventType, businessUnit); _logger.LogInformation("Fetching scheduled livestreams for eventType={EventType} from business unit: {BusinessUnit}", eventType, businessUnit);
@@ -175,21 +175,8 @@ public abstract class SrgChannelBase : IChannel, IHasCacheKey
} }
} }
private bool IsUnitEnabled()
{
var config = Plugin.Instance?.Configuration;
return config == null || config.ResolveEnabledUnits().Contains(Unit);
}
private async Task<List<ChannelItemInfo>> GetFolderItemsAsync(string? folderId, CancellationToken cancellationToken) private async Task<List<ChannelItemInfo>> GetFolderItemsAsync(string? folderId, CancellationToken cancellationToken)
{ {
// If this unit is not enabled in configuration, show nothing (empty tile).
if (!IsUnitEnabled())
{
_logger.LogDebug("Business unit {Unit} is not enabled - returning no items", Unit);
return new List<ChannelItemInfo>();
}
// Root level - show folder list // Root level - show folder list
if (string.IsNullOrEmpty(folderId)) if (string.IsNullOrEmpty(folderId))
{ {
@@ -66,7 +66,6 @@ public class PluginConfiguration : BasePluginConfiguration
{ {
// Set default options // Set default options
BusinessUnit = BusinessUnit.SRF; BusinessUnit = BusinessUnit.SRF;
EnabledBusinessUnits = new System.Collections.Generic.List<BusinessUnit> { BusinessUnit.SRF };
QualityPreference = QualityPreference.Auto; QualityPreference = QualityPreference.Auto;
ContentRefreshIntervalHours = 6; ContentRefreshIntervalHours = 6;
ExpirationCheckIntervalHours = 24; ExpirationCheckIntervalHours = 24;
@@ -79,20 +78,11 @@ public class PluginConfiguration : BasePluginConfiguration
} }
/// <summary> /// <summary>
/// Gets or sets the legacy single business unit. Retained for backwards compatibility and /// Gets or sets the legacy single business unit. Retained only for backwards compatibility
/// migration into <see cref="EnabledBusinessUnits"/>; new code should use the list instead. /// with older configs; every unit now has its own always-on channel.
/// </summary> /// </summary>
public BusinessUnit BusinessUnit { get; set; } public BusinessUnit BusinessUnit { get; set; }
/// <summary>
/// Gets or sets the list of business units to expose as channels. One channel tile is shown
/// per enabled unit, so polylingual households can browse e.g. SRF (German) and RTS (French)
/// at the same time.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2227:Collection properties should be read only", Justification = "Required for configuration serialization")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1002:Do not expose generic lists", Justification = "Configuration DTO")]
public System.Collections.Generic.List<BusinessUnit> EnabledBusinessUnits { get; set; }
/// <summary> /// <summary>
/// Gets or sets the preferred video quality. /// Gets or sets the preferred video quality.
/// </summary> /// </summary>
@@ -172,20 +162,4 @@ public class PluginConfiguration : BasePluginConfiguration
/// Gets or sets the output directory for sport livestream recordings. /// Gets or sets the output directory for sport livestream recordings.
/// </summary> /// </summary>
public string RecordingOutputPath { get; set; } = string.Empty; public string RecordingOutputPath { get; set; } = string.Empty;
/// <summary>
/// Resolves the effective set of enabled business units, migrating from the legacy single
/// <see cref="BusinessUnit"/> value when the list has not been populated yet.
/// </summary>
/// <returns>The business units that should have channels.</returns>
public System.Collections.Generic.IReadOnlyList<BusinessUnit> ResolveEnabledUnits()
{
if (EnabledBusinessUnits != null && EnabledBusinessUnits.Count > 0)
{
return EnabledBusinessUnits;
}
// Legacy installs only had a single BusinessUnit; honour it so they keep working.
return new System.Collections.Generic.List<BusinessUnit> { BusinessUnit };
}
} }
@@ -3,28 +3,8 @@
<div class="content-primary"> <div class="content-primary">
<form id="SRFPlayConfigForm"> <form id="SRFPlayConfigForm">
<div class="checkboxContainer checkboxContainer-withDescription"> <div class="checkboxContainer checkboxContainer-withDescription">
<h3>Channels (Business Units)</h3> <h3>Channels</h3>
<div class="fieldDescription" style="margin-bottom: 0.5em;">Enable a channel tile for each Swiss broadcaster you want. A separate tile appears per enabled unit, so polylingual households can browse e.g. SRF (German) and RTS (French) at the same time.</div> <div class="fieldDescription">Every Swiss broadcaster has its own channel tile: SRF (German), RTS (French), RSI (Italian), RTR (Romansh) and SWI (swissinfo.ch). They are always available — pin the ones you use from the Jellyfin home screen.</div>
<label class="emby-checkbox-label">
<input id="buSRF" type="checkbox" is="emby-checkbox" data-bu="SRF" />
<span>SRF — Schweizer Radio und Fernsehen (German)</span>
</label>
<label class="emby-checkbox-label">
<input id="buRTS" type="checkbox" is="emby-checkbox" data-bu="RTS" />
<span>RTS — Radio Télévision Suisse (French)</span>
</label>
<label class="emby-checkbox-label">
<input id="buRSI" type="checkbox" is="emby-checkbox" data-bu="RSI" />
<span>RSI — Radiotelevisione svizzera (Italian)</span>
</label>
<label class="emby-checkbox-label">
<input id="buRTR" type="checkbox" is="emby-checkbox" data-bu="RTR" />
<span>RTR — Radiotelevisiun Svizra Rumantscha (Romansh)</span>
</label>
<label class="emby-checkbox-label">
<input id="buSWI" type="checkbox" is="emby-checkbox" data-bu="SWI" />
<span>SWI — swissinfo.ch (International)</span>
</label>
</div> </div>
<div class="selectContainer"> <div class="selectContainer">
<label class="selectLabel" for="QualityPreference">Quality Preference</label> <label class="selectLabel" for="QualityPreference">Quality Preference</label>
@@ -155,12 +135,6 @@
.addEventListener('pageshow', function() { .addEventListener('pageshow', function() {
Dashboard.showLoadingMsg(); Dashboard.showLoadingMsg();
ApiClient.getPluginConfiguration(SRFPlayConfig.pluginUniqueId).then(function (config) { ApiClient.getPluginConfiguration(SRFPlayConfig.pluginUniqueId).then(function (config) {
var enabled = (config.EnabledBusinessUnits && config.EnabledBusinessUnits.length)
? config.EnabledBusinessUnits
: [config.BusinessUnit]; // migrate legacy single value
['SRF','RTS','RSI','RTR','SWI'].forEach(function(bu) {
document.querySelector('#bu' + bu).checked = enabled.indexOf(bu) !== -1;
});
document.querySelector('#QualityPreference').value = config.QualityPreference; document.querySelector('#QualityPreference').value = config.QualityPreference;
document.querySelector('#ContentRefreshIntervalHours').value = config.ContentRefreshIntervalHours; document.querySelector('#ContentRefreshIntervalHours').value = config.ContentRefreshIntervalHours;
document.querySelector('#ExpirationCheckIntervalHours').value = config.ExpirationCheckIntervalHours; document.querySelector('#ExpirationCheckIntervalHours').value = config.ExpirationCheckIntervalHours;
@@ -186,11 +160,6 @@
.addEventListener('submit', function(e) { .addEventListener('submit', function(e) {
Dashboard.showLoadingMsg(); Dashboard.showLoadingMsg();
ApiClient.getPluginConfiguration(SRFPlayConfig.pluginUniqueId).then(function (config) { ApiClient.getPluginConfiguration(SRFPlayConfig.pluginUniqueId).then(function (config) {
config.EnabledBusinessUnits = ['SRF','RTS','RSI','RTR','SWI'].filter(function(bu) {
return document.querySelector('#bu' + bu).checked;
});
// Keep legacy field in sync with the first enabled unit for backwards compat.
config.BusinessUnit = config.EnabledBusinessUnits[0] || 'SRF';
config.QualityPreference = document.querySelector('#QualityPreference').value; config.QualityPreference = document.querySelector('#QualityPreference').value;
config.ContentRefreshIntervalHours = parseInt(document.querySelector('#ContentRefreshIntervalHours').value); config.ContentRefreshIntervalHours = parseInt(document.querySelector('#ContentRefreshIntervalHours').value);
config.ExpirationCheckIntervalHours = parseInt(document.querySelector('#ExpirationCheckIntervalHours').value); config.ExpirationCheckIntervalHours = parseInt(document.querySelector('#ExpirationCheckIntervalHours').value);
@@ -64,8 +64,8 @@ public class ContentRefreshTask : IScheduledTask
return; return;
} }
// Refresh content for every enabled business unit (one channel per unit). // Refresh content for every business unit (one channel per unit).
var units = config.ResolveEnabledUnits(); var units = System.Enum.GetValues<Configuration.BusinessUnit>();
foreach (var unit in units) foreach (var unit in units)
{ {
var businessUnit = unit.ToLowerString(); var businessUnit = unit.ToLowerString();
@@ -147,13 +147,12 @@ public class RecordingService : IRecordingService, IDisposable
/// <inheritdoc /> /// <inheritdoc />
public async Task<IReadOnlyList<PlayV3TvProgram>> GetUpcomingScheduleAsync(CancellationToken cancellationToken) public async Task<IReadOnlyList<PlayV3TvProgram>> GetUpcomingScheduleAsync(CancellationToken cancellationToken)
{ {
var config = Plugin.Instance?.Configuration; var units = System.Enum.GetValues<Configuration.BusinessUnit>();
var units = config?.ResolveEnabledUnits() ?? new[] { Configuration.BusinessUnit.SRF };
using var apiClient = _apiClientFactory.CreateClient(); using var apiClient = _apiClientFactory.CreateClient();
// Aggregate sport livestreams across every enabled business unit so the recordings // Aggregate sport livestreams across every business unit so the recordings
// page shows events from all enabled languages. // page shows events from all languages.
var all = new List<PlayV3TvProgram>(); var all = new List<PlayV3TvProgram>();
foreach (var unit in units) foreach (var unit in units)
{ {
+6 -1
View File
@@ -270,7 +270,12 @@ The plugin includes:
### Contributing ### Contributing
Contributions welcome! Contributions welcome! This project is hosted on a self-hosted [Gitea](https://gitea.tourolle.paris/dtourolle/jellyfin-srfPlay) instance.
**You don't need a separate account** — you can sign in with your existing GitHub account. On the [sign-in page](https://gitea.tourolle.paris/user/login), choose **"Sign in with GitHub"** to register and log in via GitHub OAuth. Once signed in, you can:
- **Raise issues** — report bugs or request features on the [issue tracker](https://gitea.tourolle.paris/dtourolle/jellyfin-srfPlay/issues)
- **Contribute code** — fork the repository, push a branch, and open a pull request
## License ## License
+1 -1
View File
@@ -1,6 +1,6 @@
--- ---
name: "SRFPlay" name: "SRFPlay"
guid: "eb5d7894-8eef-4b36-aa6f-5d124e828ce1" guid: "a4b12f86-8c3d-4e9a-b7f2-1d5e6c8a9b4f"
version: "1.0.0.0" version: "1.0.0.0"
targetAbi: "10.9.0.0" targetAbi: "10.9.0.0"
framework: "net8.0" framework: "net8.0"
+21
View File
@@ -0,0 +1,21 @@
[
{
"guid": "a4b12f86-8c3d-4e9a-b7f2-1d5e6c8a9b4f",
"name": "SRF Play (Nightly)",
"description": "NIGHTLY/UNSTABLE builds of the SRF Play plugin for Jellyfin. Built automatically from the master branch on every push. Expect bugs. For stable releases use the regular SRF Play repository instead.",
"overview": "Nightly builds of SRF Play for Jellyfin",
"owner": "dtourolle",
"category": "Live TV",
"imageUrl": "https://gitea.tourolle.paris/dtourolle/jellyfin-srfPlay/raw/branch/master/assests/main%20logo.png",
"versions": [
{
"version": "1.0.20260627.185",
"changelog": "Nightly build 1.0.20260627.185 (1101385)",
"targetAbi": "10.9.0.0",
"sourceUrl": "https://gitea.tourolle.paris/dtourolle/jellyfin-srfPlay/releases/download/latest/srfplay_1.0.20260627.185.zip",
"checksum": "577af725ca202bec555bf51905b75913",
"timestamp": "2026-06-27T09:30:57Z"
}
]
}
]
+8 -40
View File
@@ -8,6 +8,14 @@
"category": "Live TV", "category": "Live TV",
"imageUrl": "https://gitea.tourolle.paris/dtourolle/jellyfin-srfPlay/raw/branch/master/assests/main%20logo.png", "imageUrl": "https://gitea.tourolle.paris/dtourolle/jellyfin-srfPlay/raw/branch/master/assests/main%20logo.png",
"versions": [ "versions": [
{
"version": "0.0.0.0",
"changelog": "Latest Build",
"targetAbi": "10.9.0.0",
"sourceUrl": "https://gitea.tourolle.paris/dtourolle/jellyfin-srfPlay/releases/download/latest/srfplay_1.0.0.0.zip",
"checksum": "2ed3281990b56fbfb5f75a1ee82a2f55",
"timestamp": "2026-06-27T09:01:57Z"
},
{ {
"version": "1.0.32", "version": "1.0.32",
"changelog": "Release 1.0.32", "changelog": "Release 1.0.32",
@@ -16,14 +24,6 @@
"checksum": "82f53848f2e2b15a35ea731ee69ee402", "checksum": "82f53848f2e2b15a35ea731ee69ee402",
"timestamp": "2026-06-27T07:16:38Z" "timestamp": "2026-06-27T07:16:38Z"
}, },
{
"version": "0.0.0.0",
"changelog": "Latest Build",
"targetAbi": "10.9.0.0",
"sourceUrl": "https://gitea.tourolle.paris/dtourolle/jellyfin-srfPlay/releases/download/latest/srfplay_1.0.0.0.zip",
"checksum": "a3848128ff37d68f97493c33538a4d25",
"timestamp": "2026-06-27T07:15:26Z"
},
{ {
"version": "1.0.31", "version": "1.0.31",
"changelog": "Release 1.0.31", "changelog": "Release 1.0.31",
@@ -32,14 +32,6 @@
"checksum": "4e499de15687e6e328ddca41c6192485", "checksum": "4e499de15687e6e328ddca41c6192485",
"timestamp": "2026-06-27T06:54:01Z" "timestamp": "2026-06-27T06:54:01Z"
}, },
{
"version": "0.0.0.0",
"changelog": "Latest Build",
"targetAbi": "10.9.0.0",
"sourceUrl": "https://gitea.tourolle.paris/dtourolle/jellyfin-srfPlay/releases/download/latest/srfplay_1.0.0.0.zip",
"checksum": "1a8b8b018ff6bcdb8da4783b74253fc4",
"timestamp": "2026-06-27T06:52:52Z"
},
{ {
"version": "1.0.30", "version": "1.0.30",
"changelog": "Release 1.0.30", "changelog": "Release 1.0.30",
@@ -48,14 +40,6 @@
"checksum": "06731df9ba3d2dab53885c9c8ac95fa6", "checksum": "06731df9ba3d2dab53885c9c8ac95fa6",
"timestamp": "2026-05-03T16:44:57Z" "timestamp": "2026-05-03T16:44:57Z"
}, },
{
"version": "0.0.0.0",
"changelog": "Latest Build",
"targetAbi": "10.9.0.0",
"sourceUrl": "https://gitea.tourolle.paris/dtourolle/jellyfin-srfPlay/releases/download/latest/srfplay_1.0.0.0.zip",
"checksum": "834e9f57206eee47ec9607ef6f65d17b",
"timestamp": "2026-05-03T16:39:53Z"
},
{ {
"version": "1.0.29", "version": "1.0.29",
"changelog": "Release 1.0.29", "changelog": "Release 1.0.29",
@@ -64,14 +48,6 @@
"checksum": "fb745388e64299497262d9ad370d8823", "checksum": "fb745388e64299497262d9ad370d8823",
"timestamp": "2026-05-03T16:07:24Z" "timestamp": "2026-05-03T16:07:24Z"
}, },
{
"version": "0.0.0.0",
"changelog": "Latest Build",
"targetAbi": "10.9.0.0",
"sourceUrl": "https://gitea.tourolle.paris/dtourolle/jellyfin-srfPlay/releases/download/latest/srfplay_1.0.0.0.zip",
"checksum": "7471a63c69deb5b9a31343bb6c49075f",
"timestamp": "2026-05-03T16:05:37Z"
},
{ {
"version": "1.0.27", "version": "1.0.27",
"changelog": "Release 1.0.27", "changelog": "Release 1.0.27",
@@ -80,14 +56,6 @@
"checksum": "1e15e35452f7b82bf74d8c3560c15949", "checksum": "1e15e35452f7b82bf74d8c3560c15949",
"timestamp": "2026-03-07T16:40:17Z" "timestamp": "2026-03-07T16:40:17Z"
}, },
{
"version": "0.0.0.0",
"changelog": "Latest Build",
"targetAbi": "10.9.0.0",
"sourceUrl": "https://gitea.tourolle.paris/dtourolle/jellyfin-srfPlay/releases/download/latest/srfplay_1.0.0.0.zip",
"checksum": "c7e868d23293adcc21d72e735094d9d6",
"timestamp": "2026-03-07T16:28:38Z"
},
{ {
"version": "1.0.25", "version": "1.0.25",
"changelog": "Release 1.0.25", "changelog": "Release 1.0.25",