lghorizon 0.9.0__tar.gz → 0.9.0.dev1__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.9.0 → lghorizon-0.9.0.dev1}/.github/workflows/build-on-pr.yml +1 -1
- {lghorizon-0.9.0 → lghorizon-0.9.0.dev1}/.github/workflows/publish-to-pypi.yml +1 -1
- lghorizon-0.9.0.dev1/PKG-INFO +41 -0
- lghorizon-0.9.0.dev1/README.md +3 -0
- lghorizon-0.9.0.dev1/lghorizon/__init__.py +36 -0
- lghorizon-0.9.0/lghorizon/lghorizon_device_state_processor.py → lghorizon-0.9.0.dev1/lghorizon/device_state_processor.py +34 -98
- lghorizon-0.9.0.dev1/lghorizon/legacy/lghorizon_api.py +469 -0
- lghorizon-0.9.0.dev1/lghorizon/legacy/models.py +768 -0
- lghorizon-0.9.0/lghorizon/lghorizon_api.py → lghorizon-0.9.0.dev1/lghorizon/lghorizonapi.py +27 -57
- lghorizon-0.9.0/lghorizon/lghorizon_message_factory.py → lghorizon-0.9.0.dev1/lghorizon/message_factory.py +2 -1
- lghorizon-0.9.0.dev1/lghorizon/models/__init__.py +12 -0
- {lghorizon-0.9.0/lghorizon → lghorizon-0.9.0.dev1/lghorizon/models}/exceptions.py +3 -3
- lghorizon-0.9.0.dev1/lghorizon/models/lghorizon_auth.py +227 -0
- lghorizon-0.9.0.dev1/lghorizon/models/lghorizon_channel.py +53 -0
- lghorizon-0.9.0.dev1/lghorizon/models/lghorizon_config.py +65 -0
- lghorizon-0.9.0.dev1/lghorizon/models/lghorizon_customer.py +55 -0
- {lghorizon-0.9.0/lghorizon → lghorizon-0.9.0.dev1/lghorizon/models}/lghorizon_device.py +58 -125
- lghorizon-0.9.0.dev1/lghorizon/models/lghorizon_device_state.py +180 -0
- lghorizon-0.9.0.dev1/lghorizon/models/lghorizon_entitlements.py +21 -0
- lghorizon-0.9.0.dev1/lghorizon/models/lghorizon_events.py +119 -0
- lghorizon-0.9.0.dev1/lghorizon/models/lghorizon_message.py +113 -0
- lghorizon-0.9.0.dev1/lghorizon/models/lghorizon_mqtt_client.py +126 -0
- lghorizon-0.9.0.dev1/lghorizon/models/lghorizon_profile.py +46 -0
- lghorizon-0.9.0.dev1/lghorizon/models/lghorizon_recordings.py +243 -0
- lghorizon-0.9.0.dev1/lghorizon/models/lghorizon_sources.py +127 -0
- lghorizon-0.9.0.dev1/lghorizon/models/lghorizon_ui_status.py +127 -0
- lghorizon-0.9.0/lghorizon/lghorizon_recording_factory.py → lghorizon-0.9.0.dev1/lghorizon/recording_factory.py +3 -17
- lghorizon-0.9.0.dev1/lghorizon.egg-info/PKG-INFO +41 -0
- lghorizon-0.9.0.dev1/lghorizon.egg-info/SOURCES.txt +47 -0
- {lghorizon-0.9.0 → lghorizon-0.9.0.dev1}/main.py +15 -6
- lghorizon-0.9.0.dev1/renovate.json +7 -0
- lghorizon-0.9.0/PKG-INFO +0 -191
- lghorizon-0.9.0/README.md +0 -153
- lghorizon-0.9.0/lghorizon/__init__.py +0 -71
- lghorizon-0.9.0/lghorizon/lghorizon_models.py +0 -1512
- lghorizon-0.9.0/lghorizon/lghorizon_mqtt_client.py +0 -335
- lghorizon-0.9.0/lghorizon.egg-info/PKG-INFO +0 -191
- lghorizon-0.9.0/lghorizon.egg-info/SOURCES.txt +0 -33
- lghorizon-0.9.0/renovate.json +0 -7
- {lghorizon-0.9.0 → lghorizon-0.9.0.dev1}/.coverage +0 -0
- {lghorizon-0.9.0 → lghorizon-0.9.0.dev1}/.flake8 +0 -0
- {lghorizon-0.9.0 → lghorizon-0.9.0.dev1}/.gitignore +0 -0
- {lghorizon-0.9.0 → lghorizon-0.9.0.dev1}/.vscode/launch.json +0 -0
- {lghorizon-0.9.0 → lghorizon-0.9.0.dev1}/LICENSE +0 -0
- {lghorizon-0.9.0 → lghorizon-0.9.0.dev1}/instructions.txt +0 -0
- {lghorizon-0.9.0 → lghorizon-0.9.0.dev1}/lghorizon/const.py +0 -0
- {lghorizon-0.9.0 → lghorizon-0.9.0.dev1}/lghorizon/helpers.py +0 -0
- {lghorizon-0.9.0 → lghorizon-0.9.0.dev1}/lghorizon/py.typed +0 -0
- {lghorizon-0.9.0 → lghorizon-0.9.0.dev1}/lghorizon.egg-info/dependency_links.txt +0 -0
- {lghorizon-0.9.0 → lghorizon-0.9.0.dev1}/lghorizon.egg-info/not-zip-safe +0 -0
- {lghorizon-0.9.0 → lghorizon-0.9.0.dev1}/lghorizon.egg-info/requires.txt +0 -0
- {lghorizon-0.9.0 → lghorizon-0.9.0.dev1}/lghorizon.egg-info/top_level.txt +0 -0
- {lghorizon-0.9.0 → lghorizon-0.9.0.dev1}/lib64 +0 -0
- {lghorizon-0.9.0 → lghorizon-0.9.0.dev1}/pyvenv.cfg +0 -0
- {lghorizon-0.9.0 → lghorizon-0.9.0.dev1}/secrets_stub.json +0 -0
- {lghorizon-0.9.0 → lghorizon-0.9.0.dev1}/setup.cfg +0 -0
- {lghorizon-0.9.0 → lghorizon-0.9.0.dev1}/setup.py +0 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: lghorizon
|
|
3
|
+
Version: 0.9.0.dev1
|
|
4
|
+
Summary: Python client for Liberty Global Horizon settop boxes
|
|
5
|
+
Home-page: https://github.com/sholofly/LGHorizon-python
|
|
6
|
+
Author: Rudolf Offereins
|
|
7
|
+
Author-email: r.offereins@gmail.com
|
|
8
|
+
License: MIT license
|
|
9
|
+
Keywords: LG,Horizon,API,Settop box
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Natural Language :: English
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.6
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
20
|
+
Requires-Python: >=3.9
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: paho-mqtt
|
|
24
|
+
Requires-Dist: requests>=2.22.0
|
|
25
|
+
Requires-Dist: backoff>=1.9.0
|
|
26
|
+
Dynamic: author
|
|
27
|
+
Dynamic: author-email
|
|
28
|
+
Dynamic: classifier
|
|
29
|
+
Dynamic: description
|
|
30
|
+
Dynamic: description-content-type
|
|
31
|
+
Dynamic: home-page
|
|
32
|
+
Dynamic: keywords
|
|
33
|
+
Dynamic: license
|
|
34
|
+
Dynamic: license-file
|
|
35
|
+
Dynamic: requires-dist
|
|
36
|
+
Dynamic: requires-python
|
|
37
|
+
Dynamic: summary
|
|
38
|
+
|
|
39
|
+
# LG Horizon Api
|
|
40
|
+
|
|
41
|
+
Python library to control multiple LG Horizon boxes
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""Python client for LG Horizon."""
|
|
2
|
+
|
|
3
|
+
from .lghorizonapi import LGHorizonApi
|
|
4
|
+
from .models.lghorizon_auth import (
|
|
5
|
+
LGHorizonAuth,
|
|
6
|
+
)
|
|
7
|
+
from .models.exceptions import (
|
|
8
|
+
LGHorizonApiUnauthorizedError,
|
|
9
|
+
LGHorizonApiConnectionError,
|
|
10
|
+
LGHorizonApiLockedError,
|
|
11
|
+
)
|
|
12
|
+
from .const import (
|
|
13
|
+
ONLINE_RUNNING,
|
|
14
|
+
ONLINE_STANDBY,
|
|
15
|
+
RECORDING_TYPE_SHOW,
|
|
16
|
+
RECORDING_TYPE_SEASON,
|
|
17
|
+
RECORDING_TYPE_SINGLE,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
__all__ = [
|
|
21
|
+
"LGHorizonApi",
|
|
22
|
+
"LGHorizonBox",
|
|
23
|
+
"LGHorizonRecordingListSeasonShow",
|
|
24
|
+
"LGHorizonRecordingSingle",
|
|
25
|
+
"LGHorizonRecordingShow",
|
|
26
|
+
"LGHorizonRecordingEpisode",
|
|
27
|
+
"LGHorizonCustomer",
|
|
28
|
+
"LGHorizonApiUnauthorizedError",
|
|
29
|
+
"LGHorizonApiConnectionError",
|
|
30
|
+
"LGHorizonApiLockedError",
|
|
31
|
+
"ONLINE_RUNNING",
|
|
32
|
+
"ONLINE_STANDBY",
|
|
33
|
+
"RECORDING_TYPE_SHOW",
|
|
34
|
+
"RECORDING_TYPE_SEASON",
|
|
35
|
+
"RECORDING_TYPE_SINGLE",
|
|
36
|
+
] # noqa
|
|
@@ -3,34 +3,33 @@
|
|
|
3
3
|
import random
|
|
4
4
|
import json
|
|
5
5
|
import urllib.parse
|
|
6
|
-
from datetime import datetime as dt, timezone
|
|
7
|
-
|
|
8
|
-
import time
|
|
9
6
|
|
|
10
7
|
from typing import cast, Dict, Optional
|
|
11
8
|
|
|
12
|
-
from .
|
|
13
|
-
from .
|
|
14
|
-
from .
|
|
9
|
+
from .models.lghorizon_device_state import LGHorizonDeviceState, LGHorizonRunningState
|
|
10
|
+
from .models.lghorizon_message import LGHorizonStatusMessage, LGHorizonUIStatusMessage
|
|
11
|
+
from .models.lghorizon_sources import (
|
|
15
12
|
LGHorizonSourceType,
|
|
16
13
|
LGHorizonLinearSource,
|
|
17
14
|
LGHorizonVODSource,
|
|
18
15
|
LGHorizonReplaySource,
|
|
19
16
|
LGHorizonNDVRSource,
|
|
20
17
|
LGHorizonReviewBufferSource,
|
|
21
|
-
LGHorizonRecordingSource,
|
|
22
18
|
)
|
|
23
|
-
from .
|
|
24
|
-
from .
|
|
19
|
+
from .models.lghorizon_auth import LGHorizonAuth
|
|
20
|
+
from .models.lghorizon_events import (
|
|
21
|
+
LGHorizonReplayEvent,
|
|
22
|
+
LGHorizonVOD,
|
|
23
|
+
)
|
|
25
24
|
|
|
26
|
-
from .
|
|
27
|
-
from .
|
|
28
|
-
from .
|
|
25
|
+
from .models.lghorizon_recordings import LGHorizonRecordingSingle
|
|
26
|
+
from .models.lghorizon_channel import LGHorizonChannel
|
|
27
|
+
from .models.lghorizon_ui_status import (
|
|
29
28
|
LGHorizonUIStateType,
|
|
30
29
|
LGHorizonAppsState,
|
|
31
30
|
LGHorizonPlayerState,
|
|
32
31
|
)
|
|
33
|
-
from .
|
|
32
|
+
from .models.lghorizon_customer import LGHorizonCustomer
|
|
34
33
|
|
|
35
34
|
|
|
36
35
|
class LGHorizonDeviceStateProcessor:
|
|
@@ -100,9 +99,6 @@ class LGHorizonDeviceStateProcessor:
|
|
|
100
99
|
return
|
|
101
100
|
await device_state.reset()
|
|
102
101
|
device_state.source_type = player_state.source_type
|
|
103
|
-
device_state.ui_state_type = LGHorizonUIStateType.MAINUI
|
|
104
|
-
device_state.speed = player_state.speed
|
|
105
|
-
|
|
106
102
|
match player_state.source_type:
|
|
107
103
|
case LGHorizonSourceType.LINEAR:
|
|
108
104
|
await self._process_linear_state(device_state, player_state)
|
|
@@ -120,10 +116,9 @@ class LGHorizonDeviceStateProcessor:
|
|
|
120
116
|
device_state: LGHorizonDeviceState,
|
|
121
117
|
apps_state: LGHorizonAppsState,
|
|
122
118
|
) -> None:
|
|
123
|
-
device_state.
|
|
124
|
-
device_state.
|
|
119
|
+
device_state.channel_id = apps_state.id
|
|
120
|
+
device_state.title = apps_state.app_name
|
|
125
121
|
device_state.image = apps_state.logo_path
|
|
126
|
-
device_state.ui_state_type = LGHorizonUIStateType.APPS
|
|
127
122
|
|
|
128
123
|
async def _process_linear_state(
|
|
129
124
|
self,
|
|
@@ -145,22 +140,12 @@ class LGHorizonDeviceStateProcessor:
|
|
|
145
140
|
service_path,
|
|
146
141
|
)
|
|
147
142
|
replay_event = LGHorizonReplayEvent(event_json)
|
|
148
|
-
device_state.id = replay_event.event_id
|
|
149
143
|
channel = self._channels[replay_event.channel_id]
|
|
150
144
|
device_state.source_type = source.source_type
|
|
151
|
-
device_state.channel_id = channel.
|
|
145
|
+
device_state.channel_id = channel.channel_number
|
|
152
146
|
device_state.channel_name = channel.title
|
|
153
|
-
device_state.
|
|
154
|
-
device_state.
|
|
155
|
-
device_state.episode_number = replay_event.episode_number
|
|
156
|
-
device_state.show_title = replay_event.title
|
|
157
|
-
now_in_ms = int(time.time() * 1000)
|
|
158
|
-
|
|
159
|
-
device_state.last_position_update = int(time.time() * 1000)
|
|
160
|
-
device_state.start_time = replay_event.start_time
|
|
161
|
-
device_state.end_time = replay_event.end_time
|
|
162
|
-
device_state.duration = replay_event.end_time - replay_event.start_time
|
|
163
|
-
device_state.position = now_in_ms - int(replay_event.start_time * 1000)
|
|
147
|
+
device_state.title = replay_event.title
|
|
148
|
+
device_state.sub_title = replay_event.full_episode_title
|
|
164
149
|
|
|
165
150
|
# Add random number to url to force refresh
|
|
166
151
|
join_param = "?"
|
|
@@ -170,6 +155,7 @@ class LGHorizonDeviceStateProcessor:
|
|
|
170
155
|
f"{channel.stream_image}{join_param}{str(random.randrange(1000000))}"
|
|
171
156
|
)
|
|
172
157
|
device_state.image = image_url
|
|
158
|
+
await device_state.reset_progress()
|
|
173
159
|
|
|
174
160
|
async def _process_reviewbuffer_state(
|
|
175
161
|
self,
|
|
@@ -191,20 +177,13 @@ class LGHorizonDeviceStateProcessor:
|
|
|
191
177
|
service_path,
|
|
192
178
|
)
|
|
193
179
|
replay_event = LGHorizonReplayEvent(event_json)
|
|
194
|
-
device_state.id = replay_event.event_id
|
|
195
180
|
channel = self._channels[replay_event.channel_id]
|
|
196
181
|
device_state.source_type = source.source_type
|
|
197
|
-
device_state.channel_id = channel.
|
|
182
|
+
device_state.channel_id = channel.channel_number
|
|
198
183
|
device_state.channel_name = channel.title
|
|
199
|
-
device_state.
|
|
200
|
-
device_state.
|
|
201
|
-
|
|
202
|
-
device_state.show_title = replay_event.title
|
|
203
|
-
device_state.last_position_update = player_state.last_speed_change_time
|
|
204
|
-
device_state.position = player_state.relative_position
|
|
205
|
-
device_state.start_time = replay_event.start_time
|
|
206
|
-
device_state.end_time = replay_event.end_time
|
|
207
|
-
device_state.duration = replay_event.end_time - replay_event.start_time
|
|
184
|
+
device_state.title = replay_event.title
|
|
185
|
+
device_state.sub_title = replay_event.full_episode_title
|
|
186
|
+
|
|
208
187
|
# Add random number to url to force refresh
|
|
209
188
|
join_param = "?"
|
|
210
189
|
if join_param in channel.stream_image:
|
|
@@ -213,6 +192,7 @@ class LGHorizonDeviceStateProcessor:
|
|
|
213
192
|
f"{channel.stream_image}{join_param}{str(random.randrange(1000000))}"
|
|
214
193
|
)
|
|
215
194
|
device_state.image = image_url
|
|
195
|
+
await device_state.reset_progress()
|
|
216
196
|
|
|
217
197
|
async def _process_replay_state(
|
|
218
198
|
self,
|
|
@@ -234,27 +214,15 @@ class LGHorizonDeviceStateProcessor:
|
|
|
234
214
|
service_path,
|
|
235
215
|
)
|
|
236
216
|
replay_event = LGHorizonReplayEvent(event_json)
|
|
237
|
-
device_state.id = replay_event.event_id
|
|
238
|
-
# Iets met buffer doen
|
|
239
|
-
channel = self._channels[replay_event.channel_id]
|
|
240
|
-
padding = channel.replay_pre_padding + channel.replay_post_padding
|
|
241
217
|
device_state.source_type = source.source_type
|
|
242
|
-
device_state.channel_id =
|
|
243
|
-
device_state.
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
device_state.last_position_update = int(time.time() * 1000)
|
|
248
|
-
device_state.start_time = replay_event.start_time
|
|
249
|
-
device_state.end_time = replay_event.end_time
|
|
250
|
-
device_state.duration = (
|
|
251
|
-
replay_event.end_time - replay_event.start_time + padding
|
|
252
|
-
)
|
|
253
|
-
device_state.position = (
|
|
254
|
-
player_state.relative_position + channel.replay_pre_padding
|
|
255
|
-
)
|
|
218
|
+
device_state.channel_id = None
|
|
219
|
+
device_state.title = replay_event.title
|
|
220
|
+
if replay_event.full_episode_title:
|
|
221
|
+
device_state.sub_title = replay_event.full_episode_title
|
|
222
|
+
|
|
256
223
|
# Add random number to url to force refresh
|
|
257
224
|
device_state.image = await self._get_intent_image_url(replay_event.event_id)
|
|
225
|
+
await device_state.reset_progress()
|
|
258
226
|
|
|
259
227
|
async def _process_vod_state(
|
|
260
228
|
self,
|
|
@@ -276,20 +244,11 @@ class LGHorizonDeviceStateProcessor:
|
|
|
276
244
|
service_path,
|
|
277
245
|
)
|
|
278
246
|
vod = LGHorizonVOD(vod_json)
|
|
279
|
-
device_state.
|
|
280
|
-
|
|
281
|
-
device_state.show_title = vod.series_title
|
|
282
|
-
device_state.episode_title = vod.title
|
|
283
|
-
device_state.season_number = vod.season
|
|
284
|
-
device_state.episode_number = vod.episode
|
|
285
|
-
else:
|
|
286
|
-
device_state.show_title = vod.title
|
|
287
|
-
|
|
247
|
+
device_state.title = vod.title
|
|
248
|
+
device_state.sub_title = vod.full_episode_title
|
|
288
249
|
device_state.duration = vod.duration
|
|
289
|
-
device_state.last_position_update = int(time.time() * 1000)
|
|
290
|
-
device_state.position = player_state.relative_position
|
|
291
|
-
|
|
292
250
|
device_state.image = await self._get_intent_image_url(vod.id)
|
|
251
|
+
await device_state.reset_progress()
|
|
293
252
|
|
|
294
253
|
async def _process_ndvr_state(
|
|
295
254
|
self, device_state: LGHorizonDeviceState, player_state: LGHorizonPlayerState
|
|
@@ -308,36 +267,13 @@ class LGHorizonDeviceStateProcessor:
|
|
|
308
267
|
service_path,
|
|
309
268
|
)
|
|
310
269
|
recording = LGHorizonRecordingSingle(recording_json)
|
|
311
|
-
device_state.
|
|
270
|
+
device_state.title = recording.title
|
|
271
|
+
device_state.sub_title = recording.full_episode_title
|
|
312
272
|
device_state.channel_id = recording.channel_id
|
|
313
273
|
if recording.channel_id:
|
|
314
274
|
channel = self._channels[recording.channel_id]
|
|
315
275
|
device_state.channel_name = channel.title
|
|
316
276
|
|
|
317
|
-
device_state.episode_title = recording.episode_title
|
|
318
|
-
device_state.season_number = recording.season_number
|
|
319
|
-
device_state.episode_number = recording.episode_number
|
|
320
|
-
device_state.last_position_update = player_state.last_speed_change_time
|
|
321
|
-
device_state.position = player_state.relative_position
|
|
322
|
-
if recording.start_time:
|
|
323
|
-
device_state.start_time = int(
|
|
324
|
-
dt.fromisoformat(
|
|
325
|
-
recording.start_time.replace("Z", "+00:00")
|
|
326
|
-
).timestamp()
|
|
327
|
-
)
|
|
328
|
-
if recording.end_time:
|
|
329
|
-
device_state.end_time = int(
|
|
330
|
-
dt.fromisoformat(recording.end_time.replace("Z", "+00:00")).timestamp()
|
|
331
|
-
)
|
|
332
|
-
if recording.start_time and recording.end_time:
|
|
333
|
-
device_state.duration = device_state.end_time - device_state.start_time
|
|
334
|
-
if recording.source == LGHorizonRecordingSource.SHOW:
|
|
335
|
-
device_state.show_title = recording.title
|
|
336
|
-
else:
|
|
337
|
-
device_state.show_title = recording.show_title
|
|
338
|
-
|
|
339
|
-
device_state.image = await self._get_intent_image_url(recording.id)
|
|
340
|
-
|
|
341
277
|
async def _get_intent_image_url(self, intent_id: str) -> Optional[str]:
|
|
342
278
|
"""Get intent image url."""
|
|
343
279
|
service_config = await self._auth.get_service_config()
|