Skip to content

Commit

Permalink
Updated to enable return of JSON structures
Browse files Browse the repository at this point in the history
Also return Python objects through use of classes for Media control informatiom, EPG and Programme/Recording
  • Loading branch information
RogerSelwyn committed Apr 29, 2020
1 parent 4413ad4 commit a17df4f
Show file tree
Hide file tree
Showing 8 changed files with 397 additions and 112 deletions.
96 changes: 82 additions & 14 deletions pyskyqremote/channel.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,85 @@
"""Structure of a standard EPG prorgramme."""

from dataclasses import dataclass, field
from datetime import datetime
import json

class Programme:
"""SkyQ Programme Class."""

def __init__(
self, programmeuuid, starttime, endtime, title, season, episode, imageUrl
):
"""Programme structure for SkyQ."""
self.progammeuuid = programmeuuid
self.starttime = starttime
self.endtime = endtime
self.title = title
self.season = season
self.episode = episode
self.imageUrl = imageUrl
from .programme import Programme


@dataclass
class Channel:
"""SkyQ Channel Class."""

sid: str = field(
init=True, repr=True, compare=False,
)
channelno: str = field(
init=True, repr=True, compare=False,
)
channelname: str = field(
init=True, repr=True, compare=False,
)
channelImageUrl: str = field(
init=True, repr=True, compare=False,
)
programmes: set = field(
init=True, repr=True, compare=False,
)

def as_json(self) -> str:
"""Return a JSON string respenting this Channel."""
return json.dumps(self, cls=_ChannelJSONEncoder)


def ChannelDecoder(obj):
"""Decode channel object from json."""
channel = json.loads(obj, object_hook=_json_decoder_hook)
if "__type__" in channel and channel["__type__"] == "__channel__":
return Channel(programmes=channel["programmes"], **channel["attributes"])
return channel


def _json_decoder_hook(obj):
"""Decode JSON into appropriate types used in this library."""
if "starttime" in obj:
obj["starttime"] = datetime.strptime(obj["starttime"], "%Y-%m-%dT%H:%M:%SZ")
if "endtime" in obj:
obj["endtime"] = datetime.strptime(obj["endtime"], "%Y-%m-%dT%H:%M:%SZ")
if "__type__" in obj and obj["__type__"] == "__programme__":
obj = Programme(**obj["attributes"])
return obj


class _ChannelJSONEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, Channel):
type_ = "__channel__"
programmes = obj.programmes
attributes = {}
for k, v in vars(obj).items():
if k not in {"programmes"}:
attributes.update({k: v})
return {
"__type__": type_,
"attributes": attributes,
"programmes": programmes,
}

if isinstance(obj, set):
return list(obj)

if isinstance(obj, Programme):
attributes = {}
for k, v in vars(obj).items():
if type(v) is datetime:
v = v.strftime("%Y-%m-%dT%H:%M:%SZ")
attributes.update({k: v})

result = {
"__type__": "__programme__",
"attributes": attributes,
}
return result

json.JSONEncoder.default(self, obj) # pragma: no cover
20 changes: 10 additions & 10 deletions pyskyqremote/country/remote_gb.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
import logging
import requests

from pyskyqremote.channel import Programme

from ..const import RESPONSE_OK
from ..channel import Channel
from ..programme import Programme

from .const_gb import CHANNEL_IMAGE_URL, PVR_IMAGE_URL, SCHEDULE_URL, LIVE_IMAGE_URL

_LOGGER = logging.getLogger(__name__)
Expand All @@ -18,7 +19,7 @@ def __init__(self, host):
"""Initialise UK remote."""
self.channel_image_url = CHANNEL_IMAGE_URL
self.pvr_image_url = PVR_IMAGE_URL
self.epgData = None
self.epgData = set()

self._lastEpgUrl = None
self._host = host
Expand All @@ -38,13 +39,14 @@ def getEpgData(self, sid, channelno, epgDate):

if epgData is None:
return None

if len(epgData[0]["events"]) == 0:
_LOGGER.warning(
f"W0010UK - Programme data not found. Do you need to set 'live_tv' to False? {self._host}"
)
return None

self.epgData = []
programmes = set()
for p in epgData[0]["events"]:
starttime = datetime.utcfromtimestamp(p["st"])
endtime = datetime.utcfromtimestamp(p["st"] + p["d"])
Expand All @@ -63,11 +65,9 @@ def getEpgData(self, sid, channelno, epgDate):
programmeuuid = str(p["programmeuuid"])
imageUrl = LIVE_IMAGE_URL.format(programmeuuid)

programme = vars(
Programme(
programmeuuid, starttime, endtime, title, season, episode, imageUrl
)
programme = Programme(
programmeuuid, starttime, endtime, title, season, episode, imageUrl
)

self.epgData.append(programme)
programmes.add(programme)
self.epgData = Channel(sid, channelno, None, None, sorted(programmes))
return self.epgData
19 changes: 9 additions & 10 deletions pyskyqremote/country/remote_it.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
import logging
import requests

from pyskyqremote.channel import Programme

from ..const import RESPONSE_OK
from ..channel import Channel
from ..programme import Programme

from .const_it import (
CHANNEL_IMAGE_URL,
PVR_IMAGE_URL,
Expand All @@ -24,7 +25,7 @@ def __init__(self, host):
"""Initialise Italy remote."""
self.channel_image_url = CHANNEL_IMAGE_URL
self.pvr_image_url = PVR_IMAGE_URL
self.epgData = None
self.epgData = set()

self._lastEpgUrl = None
self._host = host
Expand Down Expand Up @@ -60,7 +61,7 @@ def getEpgData(self, sid, channelno, epgDate):
)
return None

self.epgData = []
programmes = set()
epgDataLen = len(epgData) - 1
for index, p in enumerate(epgData):
starttime = datetime.strptime(p["starttime"], "%Y-%m-%dT%H:%M:%SZ")
Expand All @@ -84,13 +85,11 @@ def getEpgData(self, sid, channelno, epgDate):
programmeuuid = str(p["content"]["uuid"])
imageUrl = LIVE_IMAGE_URL.format(programmeuuid)

programme = vars(
Programme(
programmeuuid, starttime, endtime, title, season, episode, imageUrl
)
programme = Programme(
programmeuuid, starttime, endtime, title, season, episode, imageUrl
)

self.epgData.append(programme)
programmes.add(programme)
self.epgData = Channel(sid, channelno, None, None, sorted(programmes))
return self.epgData

def _getChannels(self):
Expand Down
54 changes: 54 additions & 0 deletions pyskyqremote/media.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
"""Structure of a media information."""

from dataclasses import dataclass, field
import json
from datetime import datetime


@dataclass
class Media:
"""SkyQ Programme Class."""

channel: str = field(
init=True, repr=True, compare=False,
)
imageUrl: str = field(
init=True, repr=True, compare=False,
)
sid: str = field(
init=True, repr=True, compare=False,
)
pvrId: str = field(
init=True, repr=True, compare=False,
)
live: bool = field(
init=True, repr=True, compare=False,
)

def as_json(self) -> str:
"""Return a JSON string respenting this media info."""
return json.dumps(self, cls=_MediaJSONEncoder)


def MediaDecoder(obj):
"""Decode programme object from json."""
media = json.loads(obj)
if "__type__" in media and media["__type__"] == "__media__":
return Media(**media["attributes"])
return media


class _MediaJSONEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, Media):
attributes = {}
for k, v in vars(obj).items():
if type(v) is datetime:
v = v.strftime("%Y-%m-%dT%H:%M:%SZ")
attributes.update({k: v})

result = {
"__type__": "__media__",
"attributes": attributes,
}
return result
110 changes: 110 additions & 0 deletions pyskyqremote/programme.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
"""Structure of a standard EPG prorgramme."""

from dataclasses import dataclass, field
from datetime import datetime
import json


@dataclass(order=True)
class Programme:
"""SkyQ Programme Class."""

programmeuuid: str = field(
init=True, repr=True, compare=False,
)
starttime: datetime = field(
init=True, repr=True, compare=True,
)
endtime: datetime = field(
init=True, repr=True, compare=False,
)
title: str = field(
init=True, repr=True, compare=False,
)
season: str = field(
init=True, repr=True, compare=False,
)
episode: str = field(
init=True, repr=True, compare=False,
)
imageUrl: str = field(
init=True, repr=True, compare=False,
)

def __hash__(self):
"""Calculate the hash of this object."""
return hash(self.starttime)

def as_json(self) -> str:
"""Return a JSON string respenting this Programmel."""
return json.dumps(self, cls=_ProgrammeJSONEncoder)


@dataclass
class RecordedProgramme(Programme):
"""SkyQ Programme Class."""

channel: str = field(
init=True, repr=True, compare=False,
)

def as_json(self) -> str:
"""Return a JSON string respenting this recording."""
return json.dumps(self, cls=_RecordingJSONEncoder)


def ProgrammeDecoder(obj):
"""Decode programme object from json."""
programme = json.loads(obj, object_hook=_json_decoder_hook)
if "__type__" in programme and programme["__type__"] == "__programme__":
return Programme(**programme["attributes"])
return programme


def RecordedProgrammeDecoder(obj):
"""Decode recorded programme object from json."""
recording = json.loads(obj, object_hook=_json_decoder_hook)
if "__type__" in recording and recording["__type__"] == "__recording__":
return RecordedProgramme(**recording["attributes"])
return recording


def _json_decoder_hook(obj):
"""Decode JSON into appropriate types used in this library."""
if "starttime" in obj:
obj["starttime"] = datetime.strptime(obj["starttime"], "%Y-%m-%dT%H:%M:%SZ")
if "endtime" in obj:
obj["endtime"] = datetime.strptime(obj["endtime"], "%Y-%m-%dT%H:%M:%SZ")
return obj


class _ProgrammeJSONEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, Programme):
attributes = {}
for k, v in vars(obj).items():
if type(v) is datetime:
v = v.strftime("%Y-%m-%dT%H:%M:%SZ")
attributes.update({k: v})

result = {
"__type__": "__programme__",
"attributes": attributes,
}
return result


class _RecordingJSONEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, Programme):
attributes = {}
for k, v in vars(obj).items():
if type(v) is datetime:
v = v.strftime("%Y-%m-%dT%H:%M:%SZ")
attributes.update({k: v})

result = {
"__type__": "__recording__",
"attributes": attributes,
}
return result
Loading

0 comments on commit a17df4f

Please sign in to comment.