# Jellyfin SRF Play Plugin A Jellyfin plugin for accessing SRF Play (Swiss Radio and Television) video-on-demand content and live sports streaming. ## Status **Beta/Alpha** - This plugin has been tested on two Jellyfin instances and is working. Some clients may experience issues with hardware decoding, which appears to be client-specific behavior. ## Features - Access to SRF Play VOD content (video-on-demand, no DRM-protected content) - **Live Sports Streaming** - Watch scheduled sports events (skiing, Formula 1, football, tennis, etc.) - Support for all Swiss broadcasting units (SRF, RTS, RSI, RTR, SWI) - Automatic content expiration handling - Latest and trending content discovery - Quality selection (Auto, SD, HD) - HLS streaming support with Akamai token authentication - Proxy support for routing traffic through alternate gateways - Smart caching with reduced TTL for upcoming livestreams ## Screenshots ### Channel Menu ![SRF Play Menu](res/menu.png) The main channel interface showing the content folders. ### Playback ![SRF Play Playback](res/playback.png) Video playback with HLS streaming support and quality selection. ## Testing The plugin includes comprehensive testing: - **Unit tests** (xUnit framework) for core services - **API spec validation tests** for all business units (SRF, RTS, RSI, RTR, SWI) - **Integration testing** - Tested on multiple Jellyfin instances with VOD and live sports streaming **Run tests:** ```bash # All tests dotnet test # Unit tests only dotnet test --filter "Category!=Integration&Category!=APISpec" # API spec tests only dotnet test --filter "Category=APISpec" # With coverage dotnet test --collect:"XPlat Code Coverage" ``` See [Test Documentation](Jellyfin.Plugin.SRFPlay.Tests/README.md) for more details. ## API Information **Base URL:** `https://il.srgssr.ch/integrationlayer/2.0/` ### Key Endpoints **Integration Layer API v2.0:** - `GET /mediaComposition/byUrn/{urn}` - Get video metadata and playable URLs - `GET /video/{businessUnit}/latest` - Get latest videos - `GET /video/{businessUnit}/trending` - Get trending videos **Play v3 API:** - `GET /play/v3/api/{bu}/production/livestreams?eventType=SPORT` - Get scheduled sports livestreams - `GET /play/v3/api/{bu}/production/shows/{id}` - Get show details - `GET /play/v3/api/{bu}/production/videos-by-show-id?showId={id}` - Get episodes for a show **Akamai Token Service:** - `GET /akahd/token?acl=/{path}/*` - Authenticate stream URLs for playback ### URN Format - `urn:{bu}:video:{id}` - Video URN (VOD content) - `urn:{bu}:scheduled_livestream:video:{id}` - Scheduled sports livestream URN (supported) - `urn:{bu}:video:livestream_{channel}` - Live TV channel URN (not supported due to Widevine DRM) Examples: - `urn:srf:video:c4927fcf-4ab4-4bcf-be4e-00e4ee9e6d6b` (VOD) - `urn:srf:scheduled_livestream:video:7b31c9a6-a96e-4c0e-bfc2-f0d6237f2233` (Sports event) - `urn:srf:video:c4927fcf-e1a0-0001-7edd-1ef01d441651` (SRF 1 Live - DRM protected) ## Building the Plugin ### Prerequisites - .NET 8.0 SDK - Jellyfin 10.9.11 or later ### Build Steps ```bash cd Jellyfin.Plugin.SRFPlay dotnet build ``` ### Output The compiled plugin will be in `bin/Debug/net8.0/` ## 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 3. Restart Jellyfin 4. Configure the plugin in Jellyfin Dashboard → Plugins → SRF Play 5. The SRF Play channel will appear in Jellyfin with three main folders: - **Latest Videos** - Recently published content - **Trending Videos** - Popular content - **Live Sports & Events** - Scheduled sports livestreams (skiing, F1, football, etc.) ## Configuration - **Business Unit**: Select the Swiss broadcasting unit (default: SRF) - **Quality Preference**: Choose video quality (Auto/SD/HD) - **Content Refresh Interval**: How often to check for new content (1-168 hours) - **Expiration Check Interval**: How often to check for expired content (1-168 hours) - **Cache Duration**: How long to cache metadata (5-1440 minutes) - **Enable Latest Content**: Automatically discover latest videos - **Enable Trending Content**: Automatically discover trending videos - **Proxy Settings**: Configure proxy server for routing SRF API traffic (optional) - **Use Proxy**: Enable/disable proxy usage - **Proxy Address**: Proxy server URL (e.g., http://proxy.example.com:8080) - **Proxy Username**: Optional authentication username - **Proxy Password**: Optional authentication password For detailed proxy setup instructions, see [PROXY_SETUP_GUIDE.md](PROXY_SETUP_GUIDE.md). ## Troubleshooting If you encounter issues with the plugin: - Check [DEBUG_GUIDE.md](DEBUG_GUIDE.md) for detailed logging information - Enable debug logging in Jellyfin to see detailed request/response information - Common issues include DRM-protected content and geo-restrictions ## Technical Architecture ### Directory Structure ``` Jellyfin.Plugin.SRFPlay/ ├── Api/ │ ├── Models/ # API response models │ │ ├── MediaComposition.cs │ │ ├── Chapter.cs │ │ ├── Resource.cs │ │ ├── Show.cs │ │ ├── Episode.cs │ │ └── PlayV3/ # Play v3 API models │ │ ├── PlayV3TvProgram.cs │ │ └── PlayV3TvProgramGuideResponse.cs │ └── SRFApiClient.cs # HTTP client for SRF API ├── Channels/ │ └── SRFPlayChannel.cs # Channel implementation ├── Configuration/ │ ├── PluginConfiguration.cs │ └── configPage.html ├── Services/ │ ├── StreamUrlResolver.cs # HLS stream resolution & authentication │ ├── MetadataCache.cs # Caching layer │ ├── ContentExpirationService.cs # Expiration management │ ├── ContentRefreshService.cs # Content discovery │ └── CategoryService.cs # Topic/category management ├── Providers/ │ ├── SRFSeriesProvider.cs # Series metadata │ ├── SRFEpisodeProvider.cs # Episode metadata │ ├── SRFImageProvider.cs # Image fetching │ └── SRFMediaProvider.cs # Playback URLs ├── ScheduledTasks/ │ ├── ContentRefreshTask.cs # Periodic content refresh │ └── ExpirationCheckTask.cs # Periodic expiration check ├── ServiceRegistrator.cs # DI registration └── Plugin.cs # Main plugin entry point ``` ### Key Components 1. **API Client**: Handles all HTTP requests to SRF Integration Layer and Play v3 API 2. **Channel**: SRF Play channel with Latest, Trending, and Live Sports folders 3. **Stream Resolver**: Extracts and selects optimal HLS streams with Akamai authentication 4. **Configuration**: User-configurable settings via Jellyfin dashboard 5. **Metadata Cache**: Thread-safe caching with dynamic TTL for livestreams 6. **Content Providers**: Jellyfin integration for series, episodes, images, and media sources 7. **Scheduled Tasks**: Automatic content refresh and expiration management 8. **Service Layer**: Content discovery, expiration handling, stream resolution, and category management ## Important Notes ### Content Limitations - **No DRM content**: Only non-DRM protected content is accessible (no Widevine/FairPlay) - **No live TV channels**: Main channels (SRF 1, SRF zwei, SRF info) use DRM and are not supported - **Sports livestreams supported**: Scheduled sports events (skiing, F1, football, etc.) work without DRM - **Content expiration**: SRF content has validity periods, plugin tracks and removes expired content - **Upcoming events**: Sports events appear before they start but require channel refresh to play once live - **No subtitles**: Subtitle support not currently implemented ### Extensibility The plugin is designed to support all Swiss broadcasting units: - **SRF** (Schweizer Radio und Fernsehen - German) - **RTS** (Radio Télévision Suisse - French) - **RSI** (Radiotelevisione svizzera - Italian) - **RTR** (Radiotelevisiun Svizra Rumantscha - Romansh) - **SWI** (Swiss World International) Currently focused on SRF but easily extensible. ## Development The plugin includes: - Complete API integration with SRF Play (Integration Layer v2.0 and Play v3) - **Live sports streaming** with scheduled event detection - Channel with Latest, Trending, and Live Sports folders - Metadata providers for series and episodes - Image fetching and caching - HLS stream playback with Akamai token authentication - Automatic content expiration handling - Scheduled tasks for content refresh - Smart caching with dynamic TTL for upcoming livestreams ### Known Issues - Some clients may experience issues with hardware decoding (appears to be client-specific) - Some edge cases may need additional handling - Performance optimization may be needed for very large content catalogs ### Contributing Contributions welcome! ## License See LICENSE file for details. ## Acknowledgments This plugin was developed with inspiration from the excellent [Kodi SRG SSR addon](https://github.com/goggle/script.module.srgssr) by [@goggle](https://github.com/goggle). The Kodi addon served as a fantastic reference for understanding the SRG SSR API structure, authentication mechanisms, and handling of scheduled livestreams. ## References - [SRF Play](https://www.srf.ch/play) - [Jellyfin Plugin Documentation](https://jellyfin.org/docs/general/server/plugins/) - [SRG SSR Integration Layer API](https://il.srgssr.ch/) - [Kodi SRG SSR Addon](https://github.com/goggle/script.module.srgssr) - Reference implementation