# Jellyfin SRF Play Plugin

A Jellyfin plugin for accessing SRF Play (Swiss Radio and Television) video-on-demand content.

## 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.

## Project Status

### ✅ Completed Components

#### Phase 1: Project Setup
- ✅ Renamed from Template to SRFPlay
- ✅ Updated all namespaces and identifiers
- ✅ Configured plugin metadata (ID, name)

#### Phase 2: Core API Infrastructure
- ✅ API Models (MediaComposition, Chapter, Resource, Show, Episode)
- ✅ SRF API Client with HTTP client wrapper
- ✅ JSON deserialization support
- ✅ Error handling and logging

#### Phase 3: Configuration
- ✅ Business unit selection (SRF/RTS/RSI/RTR/SWI)
- ✅ Quality preferences (Auto/SD/HD)
- ✅ Content refresh intervals
- ✅ Expiration check settings
- ✅ Cache duration configuration
- ✅ HTML configuration page

#### Phase 4: Services
- ✅ Stream URL Resolver
  - HLS stream selection
  - Quality-based filtering
  - DRM content filtering
  - Content expiration checking
  - Akamai token authentication
  - Upcoming livestream detection
- ✅ Metadata Cache Service
  - Efficient caching with configurable duration
  - Thread-safe with ReaderWriterLockSlim
  - IDisposable implementation
  - Dynamic TTL for scheduled livestreams
- ✅ Content Expiration Service
  - Automatic expiration checking
  - Library cleanup of expired content
  - Statistics and monitoring
- ✅ Content Refresh Service
  - Latest and trending content discovery
  - Scheduled sports livestreams
  - Automatic cache population
  - Recommendations system

#### Phase 5: Content Providers
- ✅ Series Provider (for show metadata)
- ✅ Episode Provider (for episode metadata)
- ✅ Image Provider (for thumbnails and artwork)
- ✅ Media Provider (for playback URLs and HLS streams)

#### Phase 6: Scheduled Tasks
- ✅ Content Refresh Task
  - Periodic discovery of new content
  - Configurable refresh intervals
- ✅ Expiration Check Task
  - Automatic cleanup of expired content
  - Configurable check intervals

#### Phase 7: Dependency Injection & Integration
- ✅ Service registration (ServiceRegistrator)
- ✅ Jellyfin provider interfaces implementation
- ✅ Plugin initialization and configuration

#### Phase 8: Live Sports Streaming
- ✅ Play v3 API integration for scheduled livestreams
- ✅ Sports events folder in channel
- ✅ Upcoming event detection and display
- ✅ Akamai token authentication for streams
- ✅ Dynamic cache refresh for live events
- ✅ Event scheduling with ValidFrom/ValidTo handling
- ✅ Automatic stream URL resolution when events go live

### ✅ Build Status
**Successfully compiling!** All code analysis warnings resolved.

### 🧪 Testing Status
- [x] Unit tests (xUnit framework)
  - StreamUrlResolver tests
  - MetadataCache tests
  - More to be added
- [x] API spec validation tests (nightly automated runs)
  - All business units (SRF, RTS, RSI, RTR, SWI)
  - Response schema validation
  - Performance monitoring
- [ ] Integration testing with Jellyfin instance
- [ ] End-to-end playback testing for VOD content
- [ ] Live sports streaming validation

**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.

### 📝 Next Steps
1. Test live sports streaming when events are scheduled
2. Verify Akamai token authentication
3. Test with different business units (RTS, RSI, RTR)
4. Add more unit tests for remaining services
5. Performance optimization if needed

## 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/`

## 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

### Current Status

All core functionality is implemented and compiling successfully! 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

### Testing

To test the plugin:

1. Build the plugin: `dotnet build`
2. Copy `bin/Debug/net8.0/Jellyfin.Plugin.SRFPlay.dll` to your Jellyfin plugins directory
3. Restart Jellyfin
4. Configure the plugin in Dashboard → Plugins → SRF Play
5. Add a library with the SRF Play content provider

### Known Limitations

- This is a first version that has not been tested with a live Jellyfin instance yet
- Some edge cases may need handling
- Performance optimization may be needed for large content catalogs

### Contributing

This plugin is in active development. 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
