Skip to content

Commit 76ff111

Browse files
authored
[plugin.video.orange.fr] v2.3.5 (#4656)
1 parent 8ed9bfd commit 76ff111

6 files changed

Lines changed: 43 additions & 26 deletions

File tree

plugin.video.orange.fr/CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
# 2.x
22

3+
## [2.3.5](https://github.com/f-lawe/plugin.video.orange.fr/releases/tag/v2.3.5) - 2025-04-18
4+
5+
### Fixed
6+
- Auth and live stream update following new Orange TV website changes
7+
8+
## [2.3.4](https://github.com/f-lawe/plugin.video.orange.fr/releases/tag/v2.3.4) - 2025-04-08
9+
10+
### Changed
11+
- New logo in order to avoid confusion with the new Orange Radio
12+
313
## [2.3.3](https://github.com/f-lawe/plugin.video.orange.fr/releases/tag/v2.3.3) - 2025-01-15
414

515
### Changed

plugin.video.orange.fr/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ _Cet addon n'est pas officiellement supporté par Orange. Tous les produits, log
1010
## Description
1111
Cet addon ajoute la TV d'Orange à Kodi. Toutes les chaînes inclues dans votre abonnement sont maintenant directement accessibles depuis Kodi !
1212

13-
Cet addon inclue :
13+
Cet addon inclut :
1414
- la télé en direct dans Kodi TV (en utilisant [IPTV Simple PVR](https://github.com/kodi-pvr/pvr.iptvsimple))
1515
- les programmes en replay
1616
- l'accès aux chaînes payantes qui font partie de votre souscription (pour le direct et le replay)

plugin.video.orange.fr/addon.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2-
<addon id="plugin.video.orange.fr" name="Orange TV France" version="2.3.3" provider-name="Flawe">
2+
<addon id="plugin.video.orange.fr" name="Orange TV France" version="2.3.5" provider-name="Flawe">
33
<requires>
44
<import addon="xbmc.python" version="3.0.1"/>
55
<import addon="script.module.requests" version="2.31.0"/>
@@ -22,7 +22,7 @@
2222
<source>https://github.com/f-lawe/plugin.video.orange.fr</source>
2323
<email>francois@lavaud.family</email>
2424
<assets>
25-
<icon>resources/media/icon.png</icon>
25+
<icon>resources/media/orange_tv.png</icon>
2626
<fanart>resources/media/fanart.jpg</fanart>
2727
<screenshot>resources/media/screenshot_1.jpg</screenshot>
2828
<screenshot>resources/media/screenshot_2.jpg</screenshot>

plugin.video.orange.fr/resources/lib/providers/abstract_orange_provider.py

Lines changed: 27 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,15 @@
2424
_CATCHUP_VIDEOS_ENDPOINT = "https://rp-ott-mediation-tv.woopic.com/api-gw/catchup/v4/applications/PC/groups/{group_id}"
2525
_CHANNELS_ENDPOINT = "https://rp-ott-mediation-tv.woopic.com/api-gw/pds/v1/live/ew?everywherePopulation=OTT_Metro"
2626

27-
_LIVE_STREAM_ENDPOINT = "https://mediation-tv.orange.fr/all/api-gw/live/v3/auth/accountToken/applications/PC/channels/{stream_id}/stream?terminalModel=WEB_PC&terminalId="
27+
_LIVE_STREAM_ENDPOINT = "https://mediation-tv.orange.fr/all/api-gw/stream/v2/auth/accountToken/live/{stream_id}?deviceModel=WEB_PC&customerOrangePopulation=OTT_Metro"
2828
_CATCHUP_STREAM_ENDPOINT = "https://mediation-tv.orange.fr/all/api-gw/catchup/v4/auth/accountToken/applications/PC/videos/{stream_id}/stream?terminalModel=WEB_PC&terminalId="
2929

3030
_STREAM_LOGO_URL = "https://proxymedia.woopic.com/api/v1/images/2090{path}"
31-
_LIVE_HOMEPAGE_URL = "https://chaines-tv.orange.fr/"
32-
_CATCHUP_VIDEO_URL = "https://replay.orange.fr/videos/{stream_id}"
31+
_LIVE_HOMEPAGE_URL = "https://tv.orange.fr/"
3332
_LOGIN_URL = "https://login.orange.fr"
3433

34+
_LICENSE_ENDPOINT = "https://mediation-tv.orange.fr/all/api-gw/license/v1/auth/accountToken"
35+
3536

3637
class AbstractOrangeProvider(AbstractProvider, ABC):
3738
"""Abstract Orange Provider."""
@@ -42,16 +43,15 @@ class AbstractOrangeProvider(AbstractProvider, ABC):
4243

4344
def get_live_stream_info(self, stream_id: str) -> dict:
4445
"""Get live stream info."""
45-
auth_url = _LIVE_HOMEPAGE_URL
46-
return self._get_stream_info(auth_url, _LIVE_STREAM_ENDPOINT, stream_id)
46+
return self._get_stream_info(_LIVE_STREAM_ENDPOINT, stream_id)
4747

4848
def get_catchup_stream_info(self, stream_id: str) -> dict:
4949
"""Get catchup stream info."""
50-
auth_url = _CATCHUP_VIDEO_URL.format(stream_id=stream_id)
51-
return self._get_stream_info(auth_url, _CATCHUP_STREAM_ENDPOINT, stream_id)
50+
return self._get_stream_info(_CATCHUP_STREAM_ENDPOINT, stream_id)
5251

5352
def get_streams(self) -> list:
5453
"""Load stream data from Orange and convert it to JSON-STREAMS format."""
54+
# @todo: use new API to check if channel is part of subscription
5555
channels = request_json(_CHANNELS_ENDPOINT, default={"channels": {}})["channels"]
5656
channels.sort(key=lambda channel: channel["displayOrder"])
5757

@@ -63,12 +63,16 @@ def get_streams(self) -> list:
6363
"name": channel["name"],
6464
"preset": str(channel["displayOrder"]),
6565
"logo": self._extract_logo(channel["logos"]),
66-
"stream": build_addon_url(f"/stream/live/{channel['idEPG']}"),
66+
"stream": build_addon_url(f"/stream/live/{self._get_channel_live_id(channel)}"),
6767
"group": [group_name for group_name in self.groups if int(channel["idEPG"]) in self.groups[group_name]],
6868
}
6969
for channel in channels
7070
]
7171

72+
def _get_channel_live_id(self, channel: dict) -> str:
73+
"""Get live id for given channel."""
74+
return channel["technicalChannels"]["live"][0]["liveTargetURLRelativePath"]
75+
7276
def get_epg(self) -> dict:
7377
"""Load EPG data from Orange and convert it to JSON-EPG format."""
7478
past_days_to_display = get_global_setting("epg.pastdaystodisplay", int)
@@ -198,9 +202,9 @@ def _get_catchup_videos(self, channel_id: str, category_id: str, article_id: str
198202
for video in videos
199203
]
200204

201-
def _get_stream_info(self, auth_url: str, stream_endpoint: str, stream_id: str) -> dict:
205+
def _get_stream_info(self, stream_endpoint: str, stream_id: str) -> dict:
202206
"""Load stream info from Orange."""
203-
tv_token, tv_token_expires, wassup = self._retrieve_auth_data(auth_url)
207+
tv_token, tv_token_expires, wassup = self._retrieve_auth_data()
204208

205209
try:
206210
stream_endpoint_url = stream_endpoint.format(stream_id=stream_id)
@@ -221,14 +225,16 @@ def _get_stream_info(self, auth_url: str, stream_endpoint: str, stream_id: str)
221225
def _compute_stream_info(self, stream: dict, tv_token: str, wassup: str) -> dict:
222226
"""Compute stream info."""
223227
protectionData = stream.get("protectionData") or stream.get("protectionDatas")
224-
license_server_url = None
228+
path = stream.get("streamURL") or stream.get("url")
229+
230+
license_server_url = _LICENSE_ENDPOINT if stream.get("url") is None else ""
225231

226232
for system in protectionData:
227233
if system.get("keySystem") == get_drm():
228-
license_server_url = system.get("laUrl")
234+
license_server_url += system.get("laUrl")
229235

230236
stream_info = {
231-
"path": stream.get("url"),
237+
"path": path,
232238
"protocol": "mpd",
233239
"mime_type": "application/xml+dash",
234240
"drm_config": {
@@ -253,7 +259,7 @@ def _compute_stream_info(self, stream: dict, tv_token: str, wassup: str) -> dict
253259
log(stream_info, xbmc.LOGDEBUG)
254260
return stream_info
255261

256-
def _retrieve_auth_data(self, auth_url: str, login: str = None, password: str = None) -> (str, str, str):
262+
def _retrieve_auth_data(self, login: str = None, password: str = None) -> (str, str, str):
257263
"""Retreive auth data from Orange (tv token and wassup cookie)."""
258264
provider_session_data = get_addon_setting("provider.session_data", dict)
259265
tv_token, tv_token_expires, wassup = (
@@ -268,13 +274,14 @@ def _retrieve_auth_data(self, auth_url: str, login: str = None, password: str =
268274
session.headers["Cookie"] = f"wassup={wassup}"
269275

270276
try:
271-
response = request("GET", f"{_LIVE_HOMEPAGE_URL}token", s=session)
272-
except RequestException:
277+
response = request("GET", _LIVE_HOMEPAGE_URL, session=session)
278+
tv_token = json.loads(re.search('"token":(".*?")', response.text).group(1))
279+
except AttributeError:
273280
log("Login required", xbmc.LOGINFO)
274281
self._login(session)
275-
response = request("GET", f"{_LIVE_HOMEPAGE_URL}token", s=session)
282+
response = request("GET", _LIVE_HOMEPAGE_URL, session=session)
283+
tv_token = json.loads(re.search('"token":(".*?")', response.text).group(1))
276284

277-
tv_token = response.json()
278285
tv_token_expires = datetime.utcnow().timestamp() + 30 * 60
279286

280287
if "wassup" in session.cookies:
@@ -309,7 +316,7 @@ def _login(self, session):
309316
}
310317

311318
try:
312-
request("GET", _LOGIN_URL, headers=session.headers, s=session)
319+
request("GET", _LOGIN_URL, headers=session.headers, session=session)
313320
except RequestException:
314321
log("Error while authenticating (init)", xbmc.LOGWARNING)
315322
return
@@ -319,7 +326,7 @@ def _login(self, session):
319326
"POST",
320327
f"{_LOGIN_URL}/api/login",
321328
data=json.dumps({"login": login, "params": {}}),
322-
s=session,
329+
session=session,
323330
)
324331
except RequestException:
325332
log("Error while authenticating (login)", xbmc.LOGWARNING)

plugin.video.orange.fr/resources/lib/utils/request.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ def get_random_ua() -> str:
3636
return _USER_AGENTS[randint(0, len(_USER_AGENTS) - 1)]
3737

3838

39-
def request(method: str, url: str, headers: Mapping[str, str] = None, data=None, s: Session = None) -> Response:
39+
def request(method: str, url: str, headers: Mapping[str, str] = None, data=None, session: Session = None) -> Response:
4040
"""Send HTTP request using requests."""
4141
if headers is None:
4242
headers = {}
@@ -50,10 +50,10 @@ def request(method: str, url: str, headers: Mapping[str, str] = None, data=None,
5050
**headers,
5151
}
5252

53-
s = s if s is not None else Session()
53+
session = session if session is not None else Session()
5454

5555
log(f"Fetching {url}", xbmc.LOGDEBUG)
56-
res = s.request(method, url, headers=headers, data=data)
56+
res = session.request(method, url, headers=headers, data=data)
5757
res.raise_for_status()
5858
log(f" -> {res.status_code}", xbmc.LOGDEBUG)
5959
return res
3.05 KB
Loading

0 commit comments

Comments
 (0)