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
3637class 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 )
0 commit comments