lghorizon 0.7.5b1__tar.gz → 0.8.0b1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {lghorizon-0.7.5b1 → lghorizon-0.8.0b1}/PKG-INFO +2 -2
- {lghorizon-0.7.5b1 → lghorizon-0.8.0b1}/lghorizon/helpers.py +3 -2
- {lghorizon-0.7.5b1 → lghorizon-0.8.0b1}/lghorizon/lghorizon_api.py +112 -103
- {lghorizon-0.7.5b1 → lghorizon-0.8.0b1}/lghorizon/models.py +185 -136
- {lghorizon-0.7.5b1 → lghorizon-0.8.0b1}/lghorizon.egg-info/PKG-INFO +2 -2
- {lghorizon-0.7.5b1 → lghorizon-0.8.0b1}/lghorizon.egg-info/requires.txt +1 -1
- {lghorizon-0.7.5b1 → lghorizon-0.8.0b1}/setup.py +1 -1
- {lghorizon-0.7.5b1 → lghorizon-0.8.0b1}/test.py +14 -9
- {lghorizon-0.7.5b1 → lghorizon-0.8.0b1}/.coverage +0 -0
- {lghorizon-0.7.5b1 → lghorizon-0.8.0b1}/.flake8 +0 -0
- {lghorizon-0.7.5b1 → lghorizon-0.8.0b1}/.github/workflows/build-on-pr.yml +0 -0
- {lghorizon-0.7.5b1 → lghorizon-0.8.0b1}/.github/workflows/publish-to-pypi.yml +0 -0
- {lghorizon-0.7.5b1 → lghorizon-0.8.0b1}/.gitignore +0 -0
- {lghorizon-0.7.5b1 → lghorizon-0.8.0b1}/LICENSE +0 -0
- {lghorizon-0.7.5b1 → lghorizon-0.8.0b1}/README.md +0 -0
- {lghorizon-0.7.5b1 → lghorizon-0.8.0b1}/instructions.txt +0 -0
- {lghorizon-0.7.5b1 → lghorizon-0.8.0b1}/lghorizon/__init__.py +0 -0
- {lghorizon-0.7.5b1 → lghorizon-0.8.0b1}/lghorizon/const.py +0 -0
- {lghorizon-0.7.5b1 → lghorizon-0.8.0b1}/lghorizon/exceptions.py +0 -0
- {lghorizon-0.7.5b1 → lghorizon-0.8.0b1}/lghorizon/py.typed +0 -0
- {lghorizon-0.7.5b1 → lghorizon-0.8.0b1}/lghorizon.egg-info/SOURCES.txt +0 -0
- {lghorizon-0.7.5b1 → lghorizon-0.8.0b1}/lghorizon.egg-info/dependency_links.txt +0 -0
- {lghorizon-0.7.5b1 → lghorizon-0.8.0b1}/lghorizon.egg-info/not-zip-safe +0 -0
- {lghorizon-0.7.5b1 → lghorizon-0.8.0b1}/lghorizon.egg-info/top_level.txt +0 -0
- {lghorizon-0.7.5b1 → lghorizon-0.8.0b1}/lib64 +0 -0
- {lghorizon-0.7.5b1 → lghorizon-0.8.0b1}/pyvenv.cfg +0 -0
- {lghorizon-0.7.5b1 → lghorizon-0.8.0b1}/renovate.json +0 -0
- {lghorizon-0.7.5b1 → lghorizon-0.8.0b1}/secrets_stub.json +0 -0
- {lghorizon-0.7.5b1 → lghorizon-0.8.0b1}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: lghorizon
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.0b1
|
|
4
4
|
Summary: Python client for Liberty Global Horizon settop boxes
|
|
5
5
|
Home-page: https://github.com/sholofly/LGHorizon-python
|
|
6
6
|
Author: Rudolf Offereins
|
|
@@ -20,7 +20,7 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
|
20
20
|
Requires-Python: >=3.9
|
|
21
21
|
Description-Content-Type: text/markdown
|
|
22
22
|
License-File: LICENSE
|
|
23
|
-
Requires-Dist: paho-mqtt
|
|
23
|
+
Requires-Dist: paho-mqtt>=2.0.0
|
|
24
24
|
Requires-Dist: requests>=2.22.0
|
|
25
25
|
Requires-Dist: backoff>=1.9.0
|
|
26
26
|
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
"""Helper functions."""
|
|
2
|
+
|
|
2
3
|
import random
|
|
3
4
|
|
|
4
5
|
|
|
5
|
-
def make_id(
|
|
6
|
+
def make_id(string_length=10):
|
|
6
7
|
"""Create an id with given length."""
|
|
7
8
|
letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
|
|
8
|
-
return "".join(random.choice(letters) for i in range(
|
|
9
|
+
return "".join(random.choice(letters) for i in range(string_length))
|
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
"""Python client for LGHorizon."""
|
|
2
|
+
# pylint: disable=broad-exception-caught
|
|
3
|
+
# pylint: disable=line-too-long
|
|
2
4
|
|
|
3
5
|
import logging
|
|
4
6
|
import json
|
|
5
|
-
import
|
|
7
|
+
import re
|
|
8
|
+
|
|
9
|
+
from typing import Any, Callable, Dict, List
|
|
10
|
+
import backoff
|
|
11
|
+
|
|
12
|
+
from requests import Session, exceptions as request_exceptions
|
|
13
|
+
|
|
6
14
|
from .exceptions import (
|
|
7
15
|
LGHorizonApiUnauthorizedError,
|
|
8
16
|
LGHorizonApiConnectionError,
|
|
9
17
|
LGHorizonApiLockedError,
|
|
10
18
|
)
|
|
11
|
-
|
|
12
|
-
from requests import Session, exceptions as request_exceptions
|
|
13
|
-
from paho.mqtt.client import WebsocketConnectionError
|
|
14
|
-
import re
|
|
19
|
+
|
|
15
20
|
from .models import (
|
|
16
21
|
LGHorizonAuth,
|
|
17
22
|
LGHorizonBox,
|
|
@@ -39,7 +44,7 @@ from .const import (
|
|
|
39
44
|
RECORDING_TYPE_SEASON,
|
|
40
45
|
RECORDING_TYPE_SHOW,
|
|
41
46
|
)
|
|
42
|
-
|
|
47
|
+
|
|
43
48
|
|
|
44
49
|
_logger = logging.getLogger(__name__)
|
|
45
50
|
_supported_platforms = ["EOS", "EOS2", "HORIZON", "APOLLO"]
|
|
@@ -52,7 +57,7 @@ class LGHorizonApi:
|
|
|
52
57
|
_session: Session = None
|
|
53
58
|
settop_boxes: Dict[str, LGHorizonBox] = None
|
|
54
59
|
customer: LGHorizonCustomer = None
|
|
55
|
-
|
|
60
|
+
_mqtt_client: LGHorizonMqttClient = None
|
|
56
61
|
_channels: Dict[str, LGHorizonChannel] = None
|
|
57
62
|
_country_settings = None
|
|
58
63
|
_country_code: str = None
|
|
@@ -89,12 +94,10 @@ class LGHorizonApi:
|
|
|
89
94
|
def _authorize(self) -> None:
|
|
90
95
|
ctry_code = self._country_code[0:2]
|
|
91
96
|
if ctry_code == "be":
|
|
92
|
-
self.
|
|
97
|
+
self._authorize_telenet()
|
|
93
98
|
elif ctry_code in ("gb", "ch"):
|
|
94
|
-
self.
|
|
95
|
-
|
|
96
|
-
# self.authorize_sunrise()
|
|
97
|
-
# else:
|
|
99
|
+
self._authorize_with_refresh_token()
|
|
100
|
+
else:
|
|
98
101
|
self._authorize_default()
|
|
99
102
|
|
|
100
103
|
def _authorize_default(self) -> None:
|
|
@@ -124,7 +127,8 @@ class LGHorizonApi:
|
|
|
124
127
|
self._auth.fill(auth_response.json())
|
|
125
128
|
_logger.debug("Authorization succeeded")
|
|
126
129
|
|
|
127
|
-
def
|
|
130
|
+
def _authorize_with_refresh_token(self) -> None:
|
|
131
|
+
"""Handle authorizzationg using request token."""
|
|
128
132
|
_logger.debug("Authorizing via refresh")
|
|
129
133
|
refresh_url = (
|
|
130
134
|
f"{self._country_settings['api_url']}/auth-service/v1/authorization/refresh"
|
|
@@ -153,8 +157,8 @@ class LGHorizonApi:
|
|
|
153
157
|
raise LGHorizonApiConnectionError("Unknown connection error")
|
|
154
158
|
|
|
155
159
|
self._auth.fill(auth_response.json())
|
|
156
|
-
self.refresh_token = self._auth.
|
|
157
|
-
self._session.cookies["ACCESSTOKEN"] = self._auth.
|
|
160
|
+
self.refresh_token = self._auth.refresh_token
|
|
161
|
+
self._session.cookies["ACCESSTOKEN"] = self._auth.access_token
|
|
158
162
|
|
|
159
163
|
if self._refresh_callback:
|
|
160
164
|
self._refresh_callback()
|
|
@@ -162,9 +166,11 @@ class LGHorizonApi:
|
|
|
162
166
|
_logger.debug("Authorization succeeded")
|
|
163
167
|
|
|
164
168
|
def set_callback(self, refresh_callback: Callable) -> None:
|
|
169
|
+
"""Set the refresh callback."""
|
|
165
170
|
self._refresh_callback = refresh_callback
|
|
166
171
|
|
|
167
|
-
def
|
|
172
|
+
def _authorize_telenet(self):
|
|
173
|
+
"""Authorize telenet users."""
|
|
168
174
|
try:
|
|
169
175
|
login_session = Session()
|
|
170
176
|
# Step 1 - Get Authorization data
|
|
@@ -176,13 +182,13 @@ class LGHorizonApi:
|
|
|
176
182
|
if not auth_response.ok:
|
|
177
183
|
raise LGHorizonApiConnectionError("Can't connect to authorization URL")
|
|
178
184
|
auth_response_json = auth_response.json()
|
|
179
|
-
|
|
180
|
-
|
|
185
|
+
authorization_uri = auth_response_json["authorizationUri"]
|
|
186
|
+
authorization_validity_token = auth_response_json["validityToken"]
|
|
181
187
|
|
|
182
188
|
# Step 2 - Get Authorization cookie
|
|
183
189
|
_logger.debug("Step 2 - Get Authorization cookie")
|
|
184
190
|
|
|
185
|
-
auth_cookie_response = login_session.get(
|
|
191
|
+
auth_cookie_response = login_session.get(authorization_uri)
|
|
186
192
|
if not auth_cookie_response.ok:
|
|
187
193
|
raise LGHorizonApiConnectionError("Can't connect to authorization URL")
|
|
188
194
|
|
|
@@ -206,20 +212,20 @@ class LGHorizonApi:
|
|
|
206
212
|
self._country_settings["oauth_redirect_header"]
|
|
207
213
|
]
|
|
208
214
|
|
|
209
|
-
if
|
|
215
|
+
if self._identifier is not None:
|
|
210
216
|
redirect_url += f"&dtv_identifier={self._identifier}"
|
|
211
217
|
redirect_response = login_session.get(redirect_url, allow_redirects=False)
|
|
212
218
|
success_url = redirect_response.headers[
|
|
213
219
|
self._country_settings["oauth_redirect_header"]
|
|
214
220
|
]
|
|
215
|
-
|
|
221
|
+
code_matches = re.findall(r"code=(.*)&", success_url)
|
|
216
222
|
|
|
217
|
-
|
|
223
|
+
authorization_code = code_matches[0]
|
|
218
224
|
|
|
219
225
|
new_payload = {
|
|
220
226
|
"authorizationGrant": {
|
|
221
|
-
"authorizationCode":
|
|
222
|
-
"validityToken":
|
|
227
|
+
"authorizationCode": authorization_code,
|
|
228
|
+
"validityToken": authorization_validity_token,
|
|
223
229
|
}
|
|
224
230
|
}
|
|
225
231
|
headers = {
|
|
@@ -229,8 +235,8 @@ class LGHorizonApi:
|
|
|
229
235
|
auth_url, json.dumps(new_payload), headers=headers
|
|
230
236
|
)
|
|
231
237
|
self._auth.fill(post_result.json())
|
|
232
|
-
self._session.cookies["ACCESSTOKEN"] = self._auth.
|
|
233
|
-
except Exception
|
|
238
|
+
self._session.cookies["ACCESSTOKEN"] = self._auth.access_token
|
|
239
|
+
except Exception:
|
|
234
240
|
pass
|
|
235
241
|
|
|
236
242
|
def _obtain_mqtt_token(self):
|
|
@@ -238,7 +244,7 @@ class LGHorizonApi:
|
|
|
238
244
|
mqtt_auth_url = self._config["authorizationService"]["URL"]
|
|
239
245
|
mqtt_response = self._do_api_call(f"{mqtt_auth_url}/v1/mqtt/token")
|
|
240
246
|
self._auth.mqttToken = mqtt_response["token"]
|
|
241
|
-
_logger.debug(
|
|
247
|
+
_logger.debug("MQTT token: %s", self._auth.mqttToken)
|
|
242
248
|
|
|
243
249
|
@backoff.on_exception(
|
|
244
250
|
backoff.expo,
|
|
@@ -251,11 +257,12 @@ class LGHorizonApi:
|
|
|
251
257
|
),
|
|
252
258
|
)
|
|
253
259
|
def connect(self) -> None:
|
|
260
|
+
"""Start connection process."""
|
|
254
261
|
self._config = self._get_config(self._country_code)
|
|
255
262
|
_logger.debug("Connect to API")
|
|
256
263
|
self._authorize()
|
|
257
264
|
self._obtain_mqtt_token()
|
|
258
|
-
self.
|
|
265
|
+
self._mqtt_client = LGHorizonMqttClient(
|
|
259
266
|
self._auth,
|
|
260
267
|
self._config["mqttBroker"]["URL"],
|
|
261
268
|
self._on_mqtt_connected,
|
|
@@ -263,14 +270,14 @@ class LGHorizonApi:
|
|
|
263
270
|
)
|
|
264
271
|
|
|
265
272
|
self._register_customer_and_boxes()
|
|
266
|
-
self.
|
|
273
|
+
self._mqtt_client.connect()
|
|
267
274
|
|
|
268
275
|
def disconnect(self):
|
|
269
276
|
"""Disconnect."""
|
|
270
277
|
_logger.debug("Disconnect from API")
|
|
271
|
-
if not self.
|
|
278
|
+
if not self._mqtt_client or not self._mqtt_client.is_connected:
|
|
272
279
|
return
|
|
273
|
-
self.
|
|
280
|
+
self._mqtt_client.disconnect()
|
|
274
281
|
|
|
275
282
|
def _on_mqtt_connected(self) -> None:
|
|
276
283
|
_logger.debug("Connected to MQTT server. Registering all boxes...")
|
|
@@ -281,47 +288,46 @@ class LGHorizonApi:
|
|
|
281
288
|
def _on_mqtt_message(self, message: str, topic: str) -> None:
|
|
282
289
|
if "action" in message and message["action"] == "OPS.getProfilesUpdate":
|
|
283
290
|
self._update_customer()
|
|
284
|
-
box: LGHorizonBox
|
|
285
291
|
elif "source" in message:
|
|
286
|
-
|
|
287
|
-
if not isinstance(
|
|
292
|
+
device_id = message["source"]
|
|
293
|
+
if not isinstance(device_id, str):
|
|
288
294
|
_logger.debug("ignoring message - not a string")
|
|
289
295
|
return
|
|
290
|
-
if not
|
|
296
|
+
if device_id not in self.settop_boxes:
|
|
291
297
|
return
|
|
292
298
|
try:
|
|
293
299
|
if "deviceType" in message and message["deviceType"] == "STB":
|
|
294
|
-
self.settop_boxes[
|
|
300
|
+
self.settop_boxes[device_id].update_state(message)
|
|
295
301
|
if "status" in message:
|
|
296
|
-
self._handle_box_update(
|
|
302
|
+
self._handle_box_update(device_id, message)
|
|
297
303
|
|
|
298
304
|
except Exception:
|
|
299
305
|
_logger.exception("Could not handle status message")
|
|
300
|
-
_logger.warning(
|
|
301
|
-
self.settop_boxes[
|
|
302
|
-
self.settop_boxes[
|
|
306
|
+
_logger.warning("Full message: %s", str(message))
|
|
307
|
+
self.settop_boxes[device_id].playing_info.reset()
|
|
308
|
+
self.settop_boxes[device_id].playing_info.set_paused(False)
|
|
303
309
|
elif "CPE.capacity" in message:
|
|
304
310
|
splitted_topic = topic.split("/")
|
|
305
311
|
if len(splitted_topic) != 4:
|
|
306
312
|
return
|
|
307
|
-
|
|
308
|
-
if not
|
|
313
|
+
device_id = splitted_topic[1]
|
|
314
|
+
if device_id not in self.settop_boxes:
|
|
309
315
|
return
|
|
310
|
-
self.settop_boxes[
|
|
316
|
+
self.settop_boxes[device_id].update_recording_capacity(message)
|
|
311
317
|
|
|
312
|
-
def _handle_box_update(self,
|
|
313
|
-
|
|
314
|
-
if "uiStatus" not in
|
|
318
|
+
def _handle_box_update(self, device_id: str, raw_message: Any) -> None:
|
|
319
|
+
status_payload = raw_message["status"]
|
|
320
|
+
if "uiStatus" not in status_payload:
|
|
315
321
|
return
|
|
316
|
-
|
|
317
|
-
if
|
|
318
|
-
|
|
319
|
-
if "sourceType" not in
|
|
322
|
+
ui_status = status_payload["uiStatus"]
|
|
323
|
+
if ui_status == "mainUI":
|
|
324
|
+
player_state = status_payload["playerState"]
|
|
325
|
+
if "sourceType" not in player_state or "source" not in player_state:
|
|
320
326
|
return
|
|
321
|
-
source_type =
|
|
322
|
-
state_source =
|
|
323
|
-
self.settop_boxes[
|
|
324
|
-
|
|
327
|
+
source_type = player_state["sourceType"]
|
|
328
|
+
state_source = player_state["source"]
|
|
329
|
+
self.settop_boxes[device_id].playing_info.set_paused(
|
|
330
|
+
player_state["speed"] == 0
|
|
325
331
|
)
|
|
326
332
|
if (
|
|
327
333
|
source_type
|
|
@@ -332,27 +338,27 @@ class LGHorizonApi:
|
|
|
332
338
|
)
|
|
333
339
|
and "eventId" in state_source
|
|
334
340
|
):
|
|
335
|
-
|
|
341
|
+
event_id = state_source["eventId"]
|
|
336
342
|
raw_replay_event = self._do_api_call(
|
|
337
|
-
f"{self._config['linearService']['URL']}/v2/replayEvent/{
|
|
343
|
+
f"{self._config['linearService']['URL']}/v2/replayEvent/{event_id}?returnLinearContent=true&language={self._country_settings['language']}"
|
|
338
344
|
)
|
|
339
|
-
|
|
340
|
-
channel = self._channels[
|
|
341
|
-
self.settop_boxes[
|
|
342
|
-
source_type,
|
|
345
|
+
replay_event = LGHorizonReplayEvent(raw_replay_event)
|
|
346
|
+
channel = self._channels[replay_event.channel_id]
|
|
347
|
+
self.settop_boxes[device_id].update_with_replay_event(
|
|
348
|
+
source_type, replay_event, channel
|
|
343
349
|
)
|
|
344
350
|
elif source_type == BOX_PLAY_STATE_DVR:
|
|
345
|
-
|
|
351
|
+
recording_id = state_source["recordingId"]
|
|
346
352
|
session_start_time = state_source["sessionStartTime"]
|
|
347
353
|
session_end_time = state_source["sessionEndTime"]
|
|
348
|
-
last_speed_change_time =
|
|
349
|
-
relative_position =
|
|
354
|
+
last_speed_change_time = player_state["lastSpeedChangeTime"]
|
|
355
|
+
relative_position = player_state["relativePosition"]
|
|
350
356
|
raw_recording = self._do_api_call(
|
|
351
|
-
f"{self._config['recordingService']['URL']}/customers/{self._auth.
|
|
357
|
+
f"{self._config['recordingService']['URL']}/customers/{self._auth.household_id}/details/single/{recording_id}?profileId=4504e28d-c1cb-4284-810b-f5eaab06f034&language={self._country_settings['language']}"
|
|
352
358
|
)
|
|
353
359
|
recording = LGHorizonRecordingSingle(raw_recording)
|
|
354
|
-
channel = self._channels[recording.
|
|
355
|
-
self.settop_boxes[
|
|
360
|
+
channel = self._channels[recording.channel_id]
|
|
361
|
+
self.settop_boxes[device_id].update_with_recording(
|
|
356
362
|
source_type,
|
|
357
363
|
recording,
|
|
358
364
|
channel,
|
|
@@ -362,35 +368,35 @@ class LGHorizonApi:
|
|
|
362
368
|
relative_position,
|
|
363
369
|
)
|
|
364
370
|
elif source_type == BOX_PLAY_STATE_VOD:
|
|
365
|
-
|
|
366
|
-
last_speed_change_time =
|
|
367
|
-
relative_position =
|
|
371
|
+
title_id = state_source["titleId"]
|
|
372
|
+
last_speed_change_time = player_state["lastSpeedChangeTime"]
|
|
373
|
+
relative_position = player_state["relativePosition"]
|
|
368
374
|
raw_vod = self._do_api_call(
|
|
369
|
-
f"{self._config['vodService']['URL']}/v2/detailscreen/{
|
|
375
|
+
f"{self._config['vodService']['URL']}/v2/detailscreen/{title_id}?language={self._country_settings['language']}&profileId=4504e28d-c1cb-4284-810b-f5eaab06f034&cityId={self.customer.city_id}"
|
|
370
376
|
)
|
|
371
377
|
vod = LGHorizonVod(raw_vod)
|
|
372
|
-
self.settop_boxes[
|
|
378
|
+
self.settop_boxes[device_id].update_with_vod(
|
|
373
379
|
source_type, vod, last_speed_change_time, relative_position
|
|
374
380
|
)
|
|
375
|
-
elif
|
|
376
|
-
app = LGHorizonApp(
|
|
377
|
-
self.settop_boxes[
|
|
381
|
+
elif ui_status == "apps":
|
|
382
|
+
app = LGHorizonApp(status_payload["appsState"])
|
|
383
|
+
self.settop_boxes[device_id].update_with_app("app", app)
|
|
378
384
|
|
|
379
385
|
@backoff.on_exception(
|
|
380
386
|
backoff.expo, LGHorizonApiConnectionError, max_tries=3, logger=_logger
|
|
381
387
|
)
|
|
382
|
-
def _do_api_call(self, url: str
|
|
383
|
-
_logger.info(
|
|
388
|
+
def _do_api_call(self, url: str) -> str:
|
|
389
|
+
_logger.info("Executing API call to %s", url)
|
|
384
390
|
try:
|
|
385
391
|
api_response = self._session.get(url)
|
|
386
392
|
api_response.raise_for_status()
|
|
387
393
|
json_response = api_response.json()
|
|
388
|
-
except request_exceptions.HTTPError as
|
|
394
|
+
except request_exceptions.HTTPError as http_ex:
|
|
389
395
|
self._authorize()
|
|
390
396
|
raise LGHorizonApiConnectionError(
|
|
391
|
-
f"Unable to call {url}. Error:{str(
|
|
392
|
-
)
|
|
393
|
-
_logger.debug(
|
|
397
|
+
f"Unable to call {url}. Error:{str(http_ex)}"
|
|
398
|
+
) from http_ex
|
|
399
|
+
_logger.debug("Result API call: %s", json_response)
|
|
394
400
|
return json_response
|
|
395
401
|
|
|
396
402
|
def _register_customer_and_boxes(self):
|
|
@@ -412,15 +418,15 @@ class LGHorizonApi:
|
|
|
412
418
|
else:
|
|
413
419
|
platform_type = None
|
|
414
420
|
box = LGHorizonBox(
|
|
415
|
-
device, platform_type, self.
|
|
421
|
+
device, platform_type, self._mqtt_client, self._auth, self._channels
|
|
416
422
|
)
|
|
417
|
-
self.settop_boxes[box.
|
|
418
|
-
_logger.info("Box %s registered...", box.
|
|
423
|
+
self.settop_boxes[box.device_id] = box
|
|
424
|
+
_logger.info("Box %s registered...", box.device_id)
|
|
419
425
|
|
|
420
426
|
def _update_customer(self):
|
|
421
427
|
_logger.info("Get customer data")
|
|
422
428
|
personalisation_result = self._do_api_call(
|
|
423
|
-
f"{self._config['personalizationService']['URL']}/v1/customer/{self._auth.
|
|
429
|
+
f"{self._config['personalizationService']['URL']}/v1/customer/{self._auth.household_id}?with=profiles%2Cdevices"
|
|
424
430
|
)
|
|
425
431
|
_logger.debug("Personalisation result: %s ", personalisation_result)
|
|
426
432
|
self.customer = LGHorizonCustomer(personalisation_result)
|
|
@@ -429,7 +435,7 @@ class LGHorizonApi:
|
|
|
429
435
|
self._update_entitlements()
|
|
430
436
|
_logger.info("Retrieving channels...")
|
|
431
437
|
channels_result = self._do_api_call(
|
|
432
|
-
f"{self._config['linearService']['URL']}/v2/channels?cityId={self.customer.
|
|
438
|
+
f"{self._config['linearService']['URL']}/v2/channels?cityId={self.customer.city_id}&language={self._country_settings['language']}&productClass=Orion-DASH"
|
|
433
439
|
)
|
|
434
440
|
for channel in channels_result:
|
|
435
441
|
if "isRadio" in channel and channel["isRadio"]:
|
|
@@ -441,9 +447,10 @@ class LGHorizonApi:
|
|
|
441
447
|
continue
|
|
442
448
|
channel_id = channel["id"]
|
|
443
449
|
self._channels[channel_id] = LGHorizonChannel(channel)
|
|
444
|
-
_logger.info(
|
|
450
|
+
_logger.info("%s retrieved.", len(self._channels))
|
|
445
451
|
|
|
446
452
|
def get_display_channels(self):
|
|
453
|
+
"""Returns channels to display baed on profile."""
|
|
447
454
|
all_channels = self._channels.values()
|
|
448
455
|
if not self._profile_id or self._profile_id not in self.customer.profiles:
|
|
449
456
|
return all_channels
|
|
@@ -455,11 +462,11 @@ class LGHorizonApi:
|
|
|
455
462
|
channel for channel in all_channels if channel.id in profile_channel_ids
|
|
456
463
|
]
|
|
457
464
|
|
|
458
|
-
def _get_replay_event(self,
|
|
465
|
+
def _get_replay_event(self, listing_id) -> Any:
|
|
459
466
|
"""Get listing."""
|
|
460
467
|
_logger.info("Retrieving replay event details...")
|
|
461
468
|
response = self._do_api_call(
|
|
462
|
-
f"{self._config['linearService']['URL']}/v2/replayEvent/{
|
|
469
|
+
f"{self._config['linearService']['URL']}/v2/replayEvent/{listing_id}?returnLinearContent=true&language={self._country_settings['language']}"
|
|
463
470
|
)
|
|
464
471
|
_logger.info("Replay event details retrieved")
|
|
465
472
|
return response
|
|
@@ -473,38 +480,40 @@ class LGHorizonApi:
|
|
|
473
480
|
try:
|
|
474
481
|
_logger.info("Retrieving recordingcapacity...")
|
|
475
482
|
quota_content = self._do_api_call(
|
|
476
|
-
f"{self._config['recordingService']['URL']}/customers/{self._auth.
|
|
483
|
+
f"{self._config['recordingService']['URL']}/customers/{self._auth.household_id}/quota"
|
|
477
484
|
)
|
|
478
|
-
if
|
|
485
|
+
if "quota" not in quota_content and "occupied" not in quota_content:
|
|
479
486
|
_logger.error("Unable to fetch recording capacity...")
|
|
480
487
|
return None
|
|
481
488
|
capacity = (quota_content["occupied"] / quota_content["quota"]) * 100
|
|
482
489
|
self.recording_capacity = round(capacity)
|
|
483
|
-
_logger.debug(
|
|
490
|
+
_logger.debug("Remaining recordingcapacity %s %%", self.recording_capacity)
|
|
484
491
|
return self.recording_capacity
|
|
485
|
-
except:
|
|
492
|
+
except Exception:
|
|
486
493
|
_logger.error("Unable to fetch recording capacity...")
|
|
487
494
|
return None
|
|
488
495
|
|
|
489
496
|
def get_recordings(self) -> List[LGHorizonBaseRecording]:
|
|
497
|
+
"""Returns recordings."""
|
|
490
498
|
_logger.info("Retrieving recordings...")
|
|
491
499
|
recording_content = self._do_api_call(
|
|
492
|
-
f"{self._config['recordingService']['URL']}/customers/{self._auth.
|
|
500
|
+
f"{self._config['recordingService']['URL']}/customers/{self._auth.household_id}/recordings?sort=time&sortOrder=desc&language={self._country_settings['language']}"
|
|
493
501
|
)
|
|
494
502
|
recordings = []
|
|
495
503
|
for recording_data_item in recording_content["data"]:
|
|
496
|
-
|
|
497
|
-
if
|
|
504
|
+
recording_type = recording_data_item["type"]
|
|
505
|
+
if recording_type == RECORDING_TYPE_SINGLE:
|
|
498
506
|
recordings.append(LGHorizonRecordingSingle(recording_data_item))
|
|
499
|
-
elif
|
|
507
|
+
elif recording_type in (RECORDING_TYPE_SEASON, RECORDING_TYPE_SHOW):
|
|
500
508
|
recordings.append(LGHorizonRecordingListSeasonShow(recording_data_item))
|
|
501
|
-
_logger.info(
|
|
509
|
+
_logger.info("%s recordings retrieved...", len(recordings))
|
|
502
510
|
return recordings
|
|
503
511
|
|
|
504
|
-
def get_recording_show(self,
|
|
512
|
+
def get_recording_show(self, show_id: str) -> list[LGHorizonRecordingSingle]:
|
|
513
|
+
"""Returns show recording"""
|
|
505
514
|
_logger.info("Retrieving show recordings...")
|
|
506
515
|
show_recording_content = self._do_api_call(
|
|
507
|
-
f"{self._config['recordingService']['URL']}/customers/{self._auth.
|
|
516
|
+
f"{self._config['recordingService']['URL']}/customers/{self._auth.household_id}/episodes/shows/{show_id}?source=recording&language=nl&sort=time&sortOrder=asc"
|
|
508
517
|
)
|
|
509
518
|
recordings = []
|
|
510
519
|
for item in show_recording_content["data"]:
|
|
@@ -512,21 +521,21 @@ class LGHorizonApi:
|
|
|
512
521
|
recordings.append(LGHorizonRecordingShow(item))
|
|
513
522
|
else:
|
|
514
523
|
recordings.append(LGHorizonRecordingEpisode(item))
|
|
515
|
-
_logger.info(
|
|
524
|
+
_logger.info("%s showrecordings retrieved...", len(recordings))
|
|
516
525
|
return recordings
|
|
517
526
|
|
|
518
527
|
def _update_entitlements(self) -> None:
|
|
519
528
|
_logger.info("Retrieving entitlements...")
|
|
520
529
|
entitlements_json = self._do_api_call(
|
|
521
|
-
f"{self._config['purchaseService']['URL']}/v2/customers/{self._auth.
|
|
530
|
+
f"{self._config['purchaseService']['URL']}/v2/customers/{self._auth.household_id}/entitlements?enableDaypass=true"
|
|
522
531
|
)
|
|
523
532
|
self._entitlements.clear()
|
|
524
533
|
for entitlement in entitlements_json["entitlements"]:
|
|
525
534
|
self._entitlements.append(entitlement["id"])
|
|
526
535
|
|
|
527
536
|
def _get_config(self, country_code: str):
|
|
528
|
-
|
|
529
|
-
config_url = f"{self._country_settings['api_url']}/{
|
|
537
|
+
base_country_code = country_code[0:2]
|
|
538
|
+
config_url = f"{self._country_settings['api_url']}/{base_country_code}/en/config-service/conf/web/backoffice.json"
|
|
530
539
|
result = self._do_api_call(config_url)
|
|
531
540
|
_logger.debug(result)
|
|
532
541
|
return result
|