Browse Source

add zaptv epg guide data

main
Brett Langdon 2 years ago
parent
commit
ad7ac3341e
No known key found for this signature in database GPG Key ID: 9BAD4322A65AD78B
4 changed files with 163 additions and 18 deletions
  1. +1
    -0
      src/dlhdhr/config.py
  2. +32
    -16
      src/dlhdhr/dlhd/channels.py
  3. +3
    -2
      src/dlhdhr/epg/__init__.py
  4. +127
    -0
      src/dlhdhr/epg/zaptv.py

+ 1
- 0
src/dlhdhr/config.py View File

@ -21,3 +21,4 @@ COUNTRY_EXCLUDE: set[str] | None = _set_or_none("DLHDHR_COUNTRY_EXCLUDE")
COUNTRY_ALLOW: set[str] | None = _set_or_none("DLHDHR_COUNTRY_ALLOW")
ZAP2IT_REFRESH_DELAY: int = int(os.getenv("DLHDHR_ZAP2IT_REFRESH_DELAY", "3600"))
ZAPTV_REFRESH_DELAY: int = int(os.getenv("DLHDHR_ZAPTV_REFRESH_DELAY", "3600"))

+ 32
- 16
src/dlhdhr/dlhd/channels.py View File

@ -39,10 +39,18 @@ _CHANNELS = [
),
DLHDChannel(number="36", name="Sky Sports Arena UK", country_code="uk", xmltv_id="SkySportsArena.uk", call_sign=""),
DLHDChannel(
number="37", name="Sky Sports Action UK", country_code="uk", xmltv_id="SkySportsAction.uk", call_sign=""
number="37",
name="Sky Sports Action UK",
country_code="uk",
xmltv_id="SkySportsAction.uk",
call_sign="sky-action",
),
DLHDChannel(
number="38", name="Sky Sports Main Event", country_code="uk", xmltv_id="SkySportsMainEvent.uk", call_sign=""
number="38",
name="Sky Sports Main Event",
country_code="uk",
xmltv_id="SkySportsMainEvent.uk",
call_sign="sky-sports-main-event",
),
DLHDChannel(number="39", name="Fox Sports 1 USA", country_code="us", xmltv_id="FoxSports1.us", call_sign=""),
DLHDChannel(number="40", name="Tennis Channel", country_code="us", xmltv_id="TennisChannel.us", call_sign="TENNIS"),
@ -67,7 +75,9 @@ _CHANNELS = [
),
DLHDChannel(number="57", name="EuroSport 1 Poland", country_code="pl", xmltv_id="", call_sign=""),
DLHDChannel(number="58", name="EuroSport 2 Poland", country_code="pl", xmltv_id="Eurosport2.pl", call_sign=""),
DLHDChannel(number="60", name="Sky Sports F1 UK", country_code="uk", xmltv_id="SkySportsF1.uk", call_sign=""),
DLHDChannel(
number="60", name="Sky Sports F1 UK", country_code="uk", xmltv_id="SkySportsF1.uk", call_sign="sky-sports-f1"
),
DLHDChannel(number="61", name="beIN Sports MENA English 1", country_code="", xmltv_id="", call_sign=""),
DLHDChannel(number="62", name="beIN SPORTS 1 Turkey", country_code="tr", xmltv_id="beINSports1.tr", call_sign=""),
DLHDChannel(number="63", name="beIN SPORTS 2 Turkey", country_code="tr", xmltv_id="beINSports2.tr", call_sign=""),
@ -77,7 +87,13 @@ _CHANNELS = [
),
DLHDChannel(number="66", name="TUDN USA", country_code="us", xmltv_id="TUDN.us", call_sign="TUDN"),
DLHDChannel(number="67", name="beIN SPORTS 4 Turkey", country_code="tr", xmltv_id="beINSports4.tr", call_sign=""),
DLHDChannel(number="70", name="Sky Sports Golf UK", country_code="uk", xmltv_id="SkySportsGolf.uk", call_sign=""),
DLHDChannel(
number="70",
name="Sky Sports Golf UK",
country_code="uk",
xmltv_id="SkySportsGolf.uk",
call_sign="sky-sports-golf",
),
DLHDChannel(
number="71", name="Eleven Sports 1 Poland", country_code="pl", xmltv_id="ElevenSport1.pl", call_sign=""
),
@ -151,7 +167,7 @@ _CHANNELS = [
name="Sky sports Premier League",
country_code="uk",
xmltv_id="SkySportsPremiereLeague.uk",
call_sign="",
call_sign="sky-sports-premier-league",
),
DLHDChannel(number="131", name="Telemundo", country_code="us", xmltv_id="WKAQ.us", call_sign="WNJU"),
DLHDChannel(number="132", name="Univision", country_code="ca", xmltv_id="UnivisionCanada.ca", call_sign=""),
@ -276,16 +292,16 @@ _CHANNELS = [
DLHDChannel(number="345", name="CNN USA", country_code="us", xmltv_id="CNN.us", call_sign="CNN"),
DLHDChannel(number="346", name="Willow Cricket", country_code="", xmltv_id="WillowCricket.us", call_sign=""),
DLHDChannel(number="347", name="Fox News", country_code="us", xmltv_id="FoxNews.us", call_sign=""),
DLHDChannel(number="348", name="Dave", country_code="uk", xmltv_id="Dave.uk", call_sign=""),
DLHDChannel(number="349", name="BBC News Channel HD", country_code="uk", xmltv_id="", call_sign=""),
DLHDChannel(number="350", name="ITV 1 UK", country_code="uk", xmltv_id="ITV1.uk", call_sign=""),
DLHDChannel(number="351", name="ITV 2 UK", country_code="uk", xmltv_id="ITV2.uk", call_sign=""),
DLHDChannel(number="352", name="ITV 3 UK", country_code="uk", xmltv_id="ITV3.uk", call_sign=""),
DLHDChannel(number="353", name="ITV 4 UK", country_code="uk", xmltv_id="ITV4.uk", call_sign=""),
DLHDChannel(number="354", name="Channel 4 UK", country_code="uk", xmltv_id="Channel4.uk", call_sign=""),
DLHDChannel(number="355", name="Channel 5 UK", country_code="uk", xmltv_id="Channel5.uk", call_sign=""),
DLHDChannel(number="356", name="BBC One UK", country_code="uk", xmltv_id="BBC1.uk", call_sign=""),
DLHDChannel(number="357", name="BBC Two UK", country_code="uk", xmltv_id="BBC2.uk", call_sign=""),
DLHDChannel(number="348", name="Dave", country_code="uk", xmltv_id="Dave.uk", call_sign="dave"),
DLHDChannel(number="349", name="BBC News Channel HD", country_code="uk", xmltv_id="", call_sign="bbc-news-channel"),
DLHDChannel(number="350", name="ITV 1 UK", country_code="uk", xmltv_id="ITV1.uk", call_sign="itv1"),
DLHDChannel(number="351", name="ITV 2 UK", country_code="uk", xmltv_id="ITV2.uk", call_sign="itv2"),
DLHDChannel(number="352", name="ITV 3 UK", country_code="uk", xmltv_id="ITV3.uk", call_sign="itv3"),
DLHDChannel(number="353", name="ITV 4 UK", country_code="uk", xmltv_id="ITV4.uk", call_sign="itv4"),
DLHDChannel(number="354", name="Channel 4 UK", country_code="uk", xmltv_id="Channel4.uk", call_sign="channel-4"),
DLHDChannel(number="355", name="Channel 5 UK", country_code="uk", xmltv_id="Channel5.uk", call_sign="channel-5"),
DLHDChannel(number="356", name="BBC One UK", country_code="uk", xmltv_id="BBC1.uk", call_sign="bbc-one"),
DLHDChannel(number="357", name="BBC Two UK", country_code="uk", xmltv_id="BBC2.uk", call_sign="bbc-two"),
DLHDChannel(number="358", name="BBC Three UK", country_code="uk", xmltv_id="BBC3.uk", call_sign=""),
DLHDChannel(number="359", name="BBC Four UK", country_code="uk", xmltv_id="BBC4.uk", call_sign=""),
DLHDChannel(number="360", name="5 USA", country_code="us", xmltv_id="", call_sign=""),
@ -791,7 +807,7 @@ _CHANNELS = [
),
DLHDChannel(number="682", name="Sky Showcase UK", country_code="uk", xmltv_id="SkyShowcase.uk", call_sign=""),
DLHDChannel(number="683", name="Sky Arts UK", country_code="uk", xmltv_id="SkyArts.uk", call_sign=""),
DLHDChannel(number="684", name="Sky Comedy UK", country_code="uk", xmltv_id="SkyComedy.uk", call_sign=""),
DLHDChannel(number="684", name="Sky Comedy UK", country_code="uk", xmltv_id="SkyComedy.uk", call_sign="sky-comedy"),
DLHDChannel(number="685", name="Showtime SHOxBET USA", country_code="us", xmltv_id="ShowtimeXBet.us", call_sign=""),
DLHDChannel(number="686", name="Sky History", country_code="gb", xmltv_id="HistoryChannel.uk", call_sign=""),
DLHDChannel(number="687", name="Gold UK", country_code="gb", xmltv_id="StarGold.uk", call_sign=""),


+ 3
- 2
src/dlhdhr/epg/__init__.py View File

@ -6,18 +6,19 @@ from xml.etree.ElementTree import Element, tostring
from dlhdhr.dlhd import DLHDChannel
from dlhdhr.epg.zap2it import Zap2it
from dlhdhr.epg.program import Program
from dlhdhr.epg.zaptv import ZapTV
@dataclass()
class EPG:
zap2it: Zap2it = field(default_factory=Zap2it)
zaptv: ZapTV = field(default_factory=ZapTV)
async def get_channel_programs(self, channel: DLHDChannel) -> list[Program]:
if channel.country_code == "us":
return await self.zap2it.get_channel_programs(channel)
elif channel.country_code == "uk":
# TODO: TV24? TV Guide?
return []
return await self.zaptv.get_channel_programs(channel)
return []


+ 127
- 0
src/dlhdhr/epg/zaptv.py View File

@ -0,0 +1,127 @@
import datetime
from dataclasses import dataclass, field
import time
import httpx
from dlhdhr import config
from dlhdhr.dlhd.channels import DLHDChannel
from dlhdhr.epg.program import Program
from dlhdhr.epg.program import Rating
@dataclass()
class ZapTV:
_BASE_URL = "https://www.zaptv.co.uk/api/"
_listings: dict[str, Program] = field(default_factory=dict)
_last_fetch: float = 0
def _get_client(self) -> httpx.AsyncClient:
return httpx.AsyncClient(
base_url=self._BASE_URL,
timeout=2.0,
verify=True,
max_redirects=1,
headers={
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:120.0) Gecko/20100101 Firefox/120.0",
"Referer": "https://www.zaptv.co.uk/",
"Accept": "application/json",
},
)
def _cleanup_listings(self) -> None:
now = datetime.datetime.now(datetime.UTC)
updated: dict[str, list[Program]] = {}
for call_sign, programs in self._listings.items():
updated_programs = [p for p in programs if p.end_time > now]
if updated_programs:
updated[call_sign] = updated_programs
self._listings = updated
async def _fetch_listings(self) -> dict[str, list[Program]]:
listings: dict[str, list[Program]] = {}
now = datetime.datetime.now(datetime.UTC)
async with self._get_client() as client:
channels = set()
events = {}
# TODO: Can we fetch tomorrows data as well?
res = await client.get(f"/schedules/today")
res.raise_for_status()
data = res.json()
for d in data:
channel = d["channel"]
code = channel["code"]
channels.add(code)
if code not in events:
events[code] = {}
for broadcast in d["broadcasts"]:
events[code][broadcast["uid"]] = broadcast
for code in channels:
programs = []
for evt_data in events[code].values():
end_time = datetime.datetime.fromisoformat(evt_data["endsAt"])
if end_time < now:
continue
ep_data = evt_data["metadata"].get("episode") or {}
season = ep_data.get("season") or None
if season is not None:
season = str(season)
episode = ep_data.get("number") or None
if episode is not None:
episode = str(episode)
release_year = evt_data["metadata"].get("year")
if release_year is not None:
release_year = str(release_year)
programs.append(
Program(
start_time=datetime.datetime.fromisoformat(evt_data["startsAt"]),
end_time=end_time,
title=evt_data["title"],
description="",
season=season,
episode=episode,
tags=[],
release_year=release_year,
thumbnail=evt_data["image"] or None,
rating=None,
)
)
listings[code] = sorted(programs, key=lambda p: p.start_time, reverse=True)
return listings
async def _refresh_listings(self) -> dict[str, list[Program]]:
self._cleanup_listings()
now = time.time()
if self._listings and now - self._last_fetch > config.ZAPTV_REFRESH_DELAY:
return self._listings
programs = await self._fetch_listings()
for code, programs in programs.items():
if code in self._listings:
self._listings[code].extend(programs)
else:
self._listings[code] = programs
return self._listings
async def get_channel_programs(self, channel: DLHDChannel) -> list[Program]:
if not channel.call_sign:
return []
await self._refresh_listings()
if channel.call_sign not in self._listings:
return []
return self._listings[channel.call_sign]

Loading…
Cancel
Save