- C# 79%
- HTML 17.9%
- Python 3.1%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
- "Channel number offset", 0 by default: Jellyfin's guide sorts every tuner's channels by number together, so each plugin can keep a block of its own (DIGI TV starts at 5001). - "Guide time zone": Orange's guide gives exact times (TF1's JT 20h at 19:00 UTC in November), so the default corrects nothing. A zone reads the times as clock times there and places them in Paris, or La Reunion for its guide, should Orange ever change. Applied while writing, so the cached guide is never corrected twice. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
| .forgejo/workflows | ||
| assets | ||
| src/Jellyfin.Plugin.OrangeFrance | ||
| tests/Jellyfin.Plugin.OrangeFrance.Tests | ||
| tools | ||
| .gitignore | ||
| build.yaml | ||
| image.png | ||
| Jellyfin.Plugin.OrangeFrance.sln | ||
| README.md | ||
jellyfin-plugin-orange-france
Live TV from an Orange France subscription, inside Jellyfin.
Orange delivers linear TV to the Livebox as RTP multicast on the local network. This plugin finds those streams, and turns them into an M3U playlist and an XMLTV guide for Jellyfin's own M3U tuner and XMLTV provider, which it sets up itself. The channels appear in the guide alongside everything else, and Jellyfin's DVR can record them.
Two limits decide what actually plays:
- Scrambling. Most channels are scrambled under Orange's conditional
access, which only the Orange decoder can descramble. The plugin still lists
them, with their guide, marked unavailable (
GULLI ❌); they do not play. Arte is the clear channel seen so far. - Eight channels at once. The Livebox serves at most eight multicast channels at a time to the whole network. The plugin limits Jellyfin to six by default and keeps its own scans within the rest. See How the Livebox serves channels.
Orange's over-the-top streams are not used for playback. They are Widevine protected, and Jellyfin has no CDM on the server or in any client, so they are not decodable. See Design for what that rules in and out.
Status
Early. What works today:
- Multicast discovery and channel identification, inside Jellyfin
- M3U playlist and XMLTV guide, added to Live TV automatically
- Recording through Jellyfin's own DVR
- Orange metadata: lineup, logos for dark or light themes, preset numbers
- EPG from the Orange guide API, with season and episode numbers
- Scrambled channels listed as unavailable
- La Réunion feeds, with La Réunion's own guide and presets
- Status page: last scan and update, every known channel, Live TV setup
- Catchup / start-over (OTT only, so blocked on DRM)
Requirements
- Jellyfin 12.0 or later
- An Orange France subscription with the TV option, and a Livebox
- The Jellyfin host wired to the Livebox — multicast does not survive WiFi
- Docker: host networking. Bridge networking drops multicast entirely.
Install
Add the plugin repository in Dashboard → Plugins → Repositories:
https://source.tiagoagueda.com/tiagoagueda/jellyfin-orangetv/raw/branch/manifest/manifest.json
then install Orange France from the catalogue and restart Jellyfin. That is the whole setup. At startup the plugin:
- scans the Orange multicast range for the channels the Livebox serves (about 40 minutes, first run only),
- matches them to Orange's lineup and fetches the guide,
- writes the playlist and guide under
<data>/orange-france/, e.g./config/data/data/orange-france/in the linuxserver image, - adds to Dashboard → Live TV an M3U Tuner named "Orange France", limited to six streams, and an XMLTV guide enabled for that tuner only.
Dashboard → Plugins → Orange France then shows what was found.
Settings and status
The settings page follows each viewer's interface language, English or French.
Status shows the last scan and update, whether Live TV is set up, and every known channel: its preset, whether it is clear or scrambled, when it last answered, and whether it has a guide. Scan now sweeps the range again.
Two scheduled tasks, under Dashboard → Scheduled Tasks → Live TV, keep things current:
| Task | When | What |
|---|---|---|
| Orange France: update channels and guide | startup, every 12 h, on saving the settings | checks each known channel (answers? scrambled?), rewrites the playlist and guide, refreshes the Live TV guide |
| Orange France: scan multicast channels | Mondays 04:00, and Scan now | tries all 3,327 addresses from 232.0.1.1 to 232.0.13.255 for new channels, then runs the update |
A channel that has not answered for 30 days is dropped at the next scan.
Settings:
- Region: metropolitan France (default), La Réunion, or both. The Livebox serves La Réunion feeds too. They take their guide and presets from La Réunion's guide; listed next to metropolitan channels they are named "… Réunion" and numbered from 2000.
- Picture quality: "HD only" (default) lists each channel once, in its best version: UHD, then HD, then SD for a channel that has no HD. "SD and HD" lists every version Orange sends.
- List scrambled channels: on by default; off leaves them out. With "HD only", a channel whose HD version is scrambled but SD version clear still shows, in SD.
- Channels at once: how many Orange channels Jellyfin may play or record together, six by default. Applied to the Orange France tuner. Keep it below eight: the Livebox's limit covers the whole network.
- Unavailable mark: a red cross after the name (the default), or the text "(unavailable)" for clients whose font has no emoji
- Channel logos: for dark themes (default) or light ones. Orange draws some logos twice, with white or black lettering.
- Channel number offset: added to every channel number, 0 by default. Jellyfin's guide sorts every tuner's channels by number together, so a different offset per plugin keeps each in a block (DIGI TV starts at 5001).
- Guide time zone: Orange's guide gives exact times, so leave it on Automatic. A zone reads the times as clock times there, should Orange ever change that.
- Interface IP: only needed on a multi-homed host
- Language: English or French for the "(unavailable)" text. Automatic follows the server's display language. It is one setting for the whole server because channel names are shared by every user.
- Set up Live TV automatically: on by default, see below
Choosing channels
Jellyfin puts every tuner's channels in one list, and its guide filters only by programme type and favourites, so the plugin offers two ways to narrow down its own channels:
-
For everyone: untick a channel in the status table. It leaves the playlist and guide at the next update, which saving starts.
-
Per user: after each guide refresh the plugin tags its channels, for Dashboard → Users → (user) → Parental control → allowed or blocked tags:
Tag On Orange Franceevery channel of the plugin Orange France: clear/Orange France: scrambledchannels that play / do not Orange France: metropolitan/Orange France: La Reunioneach region's feeds For example, blocking
Orange France: scrambledshows a user only the channels that play. The tags are the same in every language, so these rules survive a change of the language setting. Tags added to a channel by hand are kept.
The playlist cannot carry tags itself: Jellyfin's M3U tuner reads
group-title but does nothing with it.
Channel streams
The settings page's Channel streams tab gives a channel a stream of your
own, played instead of Orange's multicast feed: for example the broadcaster's
free stream for a channel Orange scrambles. Such a channel loses its ❌, is
tagged Orange France: clear, and keeps Orange's guide; with "HD only", the
version with a stream wins over a scrambled one.
The URL must be absolute, with a scheme Jellyfin's tuner opens (http,
https, udp, rtp, rtsp, rtmp, rtmps or srt), a host and no
spaces. Whether the stream actually plays is not checked. An invalid URL is
kept, so it shows again for fixing, but left out of the playlist.
Live TV setup
An M3U tuner or XMLTV guide that already reads the plugin's file is reused, as is one reading a file of the same name elsewhere (it is repointed): a setup made by hand for an earlier version carries over. The guide is kept limited to the plugin's tuner, so Orange programmes never map onto another tuner's channels. If you delete either entry it stays deleted; untick and tick Set up Live TV automatically to have it added again.
With automatic setup off, add the two entries yourself; the settings page shows the file paths.
Upgrading from 0.2 or earlier
Earlier versions read a multicast_map.json written by a Python scanner. On
the first update the plugin imports that map if it has none of its own, and
repoints a tuner and guide added by hand to its new files. The old files can
then be deleted.
Releasing
Push a v* tag (git tag v0.3.0 && git push origin v0.3.0). CI builds the
package with the tag as its version, attaches it to a Forgejo release and adds
it to manifest.json on the manifest branch. Update changelog in
build.yaml first: it becomes the release notes Jellyfin shows.
Building by hand
dotnet build -c Release src/Jellyfin.Plugin.OrangeFrance
Copy Jellyfin.Plugin.OrangeFrance.dll into
<jellyfin-config>/plugins/Orange France/ and restart the server.
For a proper package, use JPRM:
jprm plugin build .
Design
Two delivery paths exist for Orange France. This plugin deliberately uses the first:
Multicast (used). RTP-encapsulated MPEG-TS on the LAN, linear channels only, on 232.0.x.x port 8200. ffmpeg opens it directly, but only the clear channels decode: most are scrambled under Orange's conditional access, with only their SI tables left in the clear (about 97% of packets carry scrambling bits). The scanner records which is which, and the plugin lists scrambled channels as unavailable rather than descrambling them, which would be circumvention.
The SDT on the live feed carries a Globecast slug rather than a display name,
e.g. arte-hdplus-t401-dlv-01. ChannelNameMatcher.CleanServiceName keeps
the part before the quality tag (arte) for matching against the lineup, and
a short alias table covers slugs that differ from the lineup name
(equidia_live is "EQUIDIA"). France 3's regional feeds are f3_<region>
for "FRANCE 3 "; three of them spell the region differently
(f3_champagne, f3_nord_pdc, f3_midi_pyrennees). The distribution code tells the regions apart:
dlv-01 is metropolitan, dlv-02 La Réunion.
How the Livebox serves channels
Measured on one line, with no Orange decoder connected:
- A plain IGMPv2 join from the host starts a channel at once, within 0.15 s, and leaving stops it within seconds.
- The Livebox serves at most eight groups at once, to the whole network. The ninth join is silently ignored, which looks just like an address with nothing behind it.
- A join to an empty address takes a slot too, and a slot can take several seconds to come back after a leave. If a leave goes unanswered, the membership only expires after about four minutes.
An earlier version misread this: a scan that probed eight addresses at once filled the slots, later joins failed, and the groups still holding slots looked like a set the Livebox chose by itself.
So the scanner holds one address at a time and, after every 20, rejoins a channel known to answer. If that stays silent, the slots are full, from the scan or from someone watching, and the batch is redone once they free up. Jellyfin's tuner is limited to six streams so that viewing and recording leave room for that.
OTT (not used)
mediation-tv.orange.fr serves DASH behind Widevine. Kodi's addon plays it
because InputStream Adaptive runs a CDM on the client, which is where a CDM
belongs. Jellyfin's server-side ffmpeg pipeline has no CDM and no client
implements EME, so these streams cannot be decoded. A proxy does not change
this: relaying encrypted bytes leaves them encrypted, and decrypting them
means stripping DRM, which this project does not do.
The legitimate long-term path would pass the MPD and license URL through to a client that holds a real CDM, the browser's via EME. That needs client-side work that does not exist yet.
Why a tuner, not a Live TV service
The plugin first implemented Jellyfin's ILiveTvService. Jellyfin sends
recordings for a service's channels to that service, so recording would have
meant reimplementing the DVR: schedules, series rules, padding, conflicts.
Feeding the built-in M3U tuner and XMLTV provider instead gets all of it, plus
Jellyfin's own stream probing.
A tuner of the plugin's own (ITunerHost) would avoid the files, but the code
Jellyfin's tuners share for opening and buffering streams lives in
Jellyfin.LiveTv, which plugins cannot reference, so the DVR's stream
handling would have to be rewritten. Adding the built-in tuner through
ITunerHostManager and IListingsManager keeps to public API.
Metadata
The OTT API is still used for metadata, which needs no authentication:
| Data | Endpoint | Key fields |
|---|---|---|
| Lineup | pds/v1/live/ew |
lcn, idEPG, name, logos, technicalChannels |
| Guide | live/v3/applications/STB4PC/programs |
channelId, diffusionDate, duration, season, episodeNumber |
The guide joins to the lineup on program.channelId == channel.idEPG, and the
lineup joins to multicast on the folded channel name — see
ChannelNameMatcher. Across the 433 published channels the fold is unique for
432; the lone clash is a UHD simulcast, resolved by preferring the lower lcn.
Logos come in four variants. mobileAppliDark (183×183, white lettering) is
used for dark themes and mobileAppli for light ones; their paths are relative
to https://proxymedia.woopic.com/api/v1/images/2090.
Quirks established from live responses rather than documentation:
programType only ever takes EPISODE or OTHER, so films are recognised by
genre == "Film"; csa is a category from 1 to 5 that maps to the French
ratings (2 → -10, 5 → -18), not a number of years; and the programme
id changes with every broadcast, so episodes are told apart by season and
episode number instead.
tools/probe_orange_api.py dumps the lineup and a slice of the guide to
data/ with a schema summary, for when Orange changes a field.
Layout
build.yaml JPRM manifest
image.png plugin catalogue image (16:9, from assets/)
assets/orange-logo.svg source logo
src/Jellyfin.Plugin.OrangeFrance/
Plugin.cs plugin entry point
PluginServiceRegistrator.cs DI registration
Configuration/ settings + dashboard page (en/fr)
Export/ M3U + XMLTV writers, update task, Live TV setup
Lineup/ joins channels to the Orange lineup, region filter
Localization/ server-side strings (en/fr)
Multicast/ MPEG-TS parsing, scanner, scan task, channel store
Orange/ metadata API client, models, name matcher
Status/ status model and the settings page's API
tests/
Jellyfin.Plugin.OrangeFrance.Tests/
tools/
probe_orange_api.py metadata API schema probe
dotnet test tests/Jellyfin.Plugin.OrangeFrance.Tests