11from __future__ import annotations
22
3- import datetime
4- import time
3+ from datetime import datetime , timedelta , timezone
54from enum import Enum
65from typing import List , Optional , Tuple , Union
76
3231LIVE_PRIMARY_HLS = "https://siriusxm-priprodlive.akamaized.net"
3332
3433
34+ def parse_xm_datetime (dt_string : str ):
35+ dt_string = dt_string .replace ("+0000" , "" )
36+ dt = datetime .fromisoformat (dt_string )
37+ return dt .replace (tzinfo = timezone .utc )
38+
39+
40+ def parse_xm_timestamp (timestamp : int ):
41+ return datetime .utcfromtimestamp (timestamp / 1000 ).replace (tzinfo = timezone .utc )
42+
43+
3544class QualitySize (str , Enum ):
3645 SMALL_64k = "SMALL"
3746 MEDIUM_128k = "MEDIUM"
@@ -96,15 +105,15 @@ def from_dict(data: dict) -> XMCategory:
96105
97106class XMMarker (BaseModel ):
98107 guid : str
99- time : int
100- duration : int
108+ time : datetime
109+ duration : timedelta
101110
102111 @staticmethod
103112 def from_dict (data : dict ) -> XMMarker :
104113 return XMMarker (
105114 guid = data ["assetGUID" ],
106- time = data ["time" ],
107- duration = data ["duration" ],
115+ time = parse_xm_timestamp ( data ["time" ]) ,
116+ duration = timedelta ( seconds = data ["duration" ]) ,
108117 )
109118
110119
@@ -162,8 +171,8 @@ class XMEpisodeMarker(XMMarker):
162171 def from_dict (data : dict ) -> XMEpisodeMarker :
163172 return XMEpisodeMarker (
164173 guid = data ["assetGUID" ],
165- time = data ["time" ],
166- duration = data ["duration" ],
174+ time = parse_xm_timestamp ( data ["time" ]) ,
175+ duration = timedelta ( seconds = data ["duration" ]) ,
167176 episode = XMEpisode .from_dict (data ["episode" ]),
168177 )
169178
@@ -250,23 +259,20 @@ def from_dict(data: dict) -> XMCutMarker:
250259
251260 return XMCutMarker (
252261 guid = data ["assetGUID" ],
253- time = data ["time" ],
254- duration = data ["duration" ],
262+ time = parse_xm_timestamp ( data ["time" ]) ,
263+ duration = timedelta ( seconds = data ["duration" ]) ,
255264 cut = cut ,
256265 )
257266
258267
259268class XMPosition (BaseModel ):
260- timestamp : datetime . datetime
269+ timestamp : datetime
261270 position : str
262271
263272 @staticmethod
264273 def from_dict (data : dict ) -> XMPosition :
265- dt_string = data ["timestamp" ].replace ("+0000" , "" )
266- dt = datetime .datetime .fromisoformat (dt_string )
267-
268274 return XMPosition (
269- timestamp = dt . replace ( tzinfo = datetime . timezone . utc ),
275+ timestamp = parse_xm_datetime ( data [ "timestamp" ] ),
270276 position = data ["position" ],
271277 )
272278
@@ -352,7 +358,7 @@ class XMLiveChannel(BaseModel):
352358 custom_hls_infos : List [XMHLSInfo ]
353359 episode_markers : List [XMEpisodeMarker ]
354360 cut_markers : List [XMCutMarker ]
355- tune_time : Optional [int ] = None
361+ tune_time : Optional [datetime ] = None
356362 # ... plus many unused
357363
358364 _stream_quality : QualitySize = PrivateAttr (QualitySize .LARGE_256k )
@@ -374,10 +380,10 @@ def from_dict(data: dict) -> XMLiveChannel:
374380 )
375381
376382 return XMLiveChannel (
377- id = data ["channelId" ],
383+ id = data ["moduleResponse" ][ "liveChannelData" ][ " channelId" ],
378384 hls_infos = hls_infos ,
379385 custom_hls_infos = custom_hls_infos ,
380- tune_time = data ["wallClockRenderTime" ],
386+ tune_time = parse_xm_datetime ( data ["wallClockRenderTime" ]) ,
381387 episode_markers = episode_markers ,
382388 cut_markers = cut_markers ,
383389 )
@@ -475,7 +481,7 @@ def sort_markers(markers: List[XMMarker]) -> List[XMMarker]:
475481 return sorted (markers , key = lambda x : x .time )
476482
477483 def _latest_marker (
478- self , marker_attr : str , now : Optional [int ] = None
484+ self , marker_attr : str , now : Optional [datetime ] = None
479485 ) -> Union [XMMarker , None ]:
480486 """Returns the latest `XMMarker` based on type relative to now"""
481487
@@ -484,7 +490,7 @@ def _latest_marker(
484490 return None
485491
486492 if now is None :
487- now = int ( time . time () * 1000 )
493+ now = datetime . now ( timezone . utc )
488494
489495 latest = None
490496 for marker in markers :
@@ -495,7 +501,7 @@ def _latest_marker(
495501 return latest
496502
497503 def get_latest_episode (
498- self , now : Optional [int ] = None
504+ self , now : Optional [datetime ] = None
499505 ) -> Union [XMEpisodeMarker , None ]:
500506 """Returns the latest :class:`XMEpisodeMarker` based
501507 on type relative to now
@@ -507,7 +513,9 @@ def get_latest_episode(
507513 """
508514 return self ._latest_marker ("episode_markers" , now ) # type: ignore
509515
510- def get_latest_cut (self , now : Optional [int ] = None ) -> Union [XMCutMarker , None ]:
516+ def get_latest_cut (
517+ self , now : Optional [datetime ] = None
518+ ) -> Union [XMCutMarker , None ]:
511519 """Returns the latest :class:`XMCutMarker` based
512520 on type relative to now
513521
0 commit comments