Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- """
- Download EPG data from Horizon and output XMLTV stuff
- """
- import xml.dom.minidom
- import time
- import datetime
- import calendar
- import sys
- import json
- import socket
- import http.client
- def debug_json(data):
- print(json.dumps(data, sort_keys=True, indent=4))
- class XMLTVDocument(object):
- # this renames some of the channels
- add_display_name = {}
- category_map = {
- 'nieuws': 'News / Current affairs',
- 'sport': 'Sports',
- 'vrije tijd': 'Leisure hobbies',
- 'gezondheid': 'Fitness and health',
- 'discussie': 'Discussion / Interview / Debate',
- 'tv drama': 'Movie / Drama',
- 'komedie': 'Comedy',
- 'show': 'Show / Game show',
- 'documentaire': 'Documentary'}
- def __init__(self):
- impl = xml.dom.minidom.getDOMImplementation()
- doctype = impl.createDocumentType('tv', None, 'xmltv.dtd')
- self.document = impl.createDocument(None, 'tv', doctype)
- self.document.documentElement.setAttribute('source-info-url', 'https://horizon.tv')
- self.document.documentElement.setAttribute('source-info-name', 'UPC Horizon API')
- self.document.documentElement.setAttribute('generator-info-name', 'HorEPG v1.0')
- self.document.documentElement.setAttribute('generator-info-url', 'beralt.nl/horepg')
- def addChannel(self, channel_id, display_name, icons):
- element = self.document.createElement('channel')
- element.setAttribute('id', channel_id)
- if display_name in XMLTVDocument.add_display_name:
- for name in XMLTVDocument.add_display_name[display_name]:
- dn_element = self.document.createElement('display-name')
- dn_text = self.document.createTextNode(name)
- dn_element.appendChild(dn_text)
- element.appendChild(dn_element)
- else:
- if type(display_name) == list:
- for name in display_name:
- dn_element = self.document.createElement('display-name')
- dn_text = self.document.createTextNode(name)
- dn_element.appendChild(dn_text)
- element.appendChild(dn_element)
- else:
- dn_element = self.document.createElement('display-name')
- dn_text = self.document.createTextNode(display_name)
- dn_element.appendChild(dn_text)
- element.appendChild(dn_element)
- self.document.documentElement.appendChild(element)
- def addProgramme(self, programme):
- if 'program' in programme:
- if 'title' in programme['program']:
- element = self.document.createElement('programme')
- element.setAttribute('start', XMLTVDocument.convert_time(int(programme['startTime']) / 1000))
- element.setAttribute('stop', XMLTVDocument.convert_time(int(programme['endTime']) / 1000))
- element.setAttribute('channel', programme['stationId'])
- # quick tags
- self.quick_tag(element, 'title', programme['program']['title'])
- if 'seriesEpisodeNumber' in programme['program']:
- self.quick_tag(element, 'episode-num', programme['program']['seriesEpisodeNumber'], {'system': 'onscreen'})
- # fallback to shorter descriptions
- if 'longDescription' in programme['program']:
- self.quick_tag(element, 'desc', programme['program']['longDescription'])
- elif 'description' in programme['program']:
- self.quick_tag(element, 'desc', programme['program']['description'])
- elif 'shortDescription' in programme['program']:
- self.quick_tag(element, 'desc', programme['program']['shortDescription'])
- if 'secondaryTitle' in programme['program']:
- self.quick_tag(element, 'sub-title', programme['program']['secondaryTitle'])
- # categories
- if 'categories' in programme['program']:
- for cat in programme['program']['categories']:
- if '/' not in cat['title']:
- cat_title = XMLTVDocument.map_category(cat['title'].lower())
- if cat_title:
- self.quick_tag(element, 'category', cat_title)
- else:
- self.quick_tag(element, 'category', cat['title'])
- self.document.documentElement.appendChild(element)
- else:
- #raise Exception('The given program had no title')
- print('The program had no title', file=sys.stderr)
- else:
- #raise Exception('The listing had no program')
- print('The listing had no program', file=sys.stderr)
- def map_category(cat):
- if cat in XMLTVDocument.category_map:
- return XMLTVDocument.category_map[cat]
- return False
- def quick_tag(self, parent, tag, content, attributes = False):
- element = self.document.createElement(tag)
- text = self.document.createTextNode(content)
- element.appendChild(text)
- if attributes:
- for k, v in attributes.items():
- element.setAttribute(k, v)
- parent.appendChild(element)
- def convert_time(t):
- return time.strftime('%Y%m%d%H%M%S', time.gmtime(t))
- class ChannelMap(object):
- host = 'www.horizon.tv'
- path = '/oesp/api/NL/nld/web/channels/'
- def __init__(self):
- conn = http.client.HTTPSConnection(ChannelMap.host)
- conn.request('GET', ChannelMap.path)
- response = conn.getresponse()
- if(response.status == 200):
- raw = response.read()
- else:
- raise Exception('Failed to GET channel url')
- # load json
- data = json.loads(raw.decode('utf-8'))
- #setup channel map
- self.channel_map = {}
- for channel in data['channels']:
- for schedule in channel['stationSchedules']:
- station = schedule['station']
- self.channel_map[station['id']] = station
- def dump(self, xmltv):
- for k, v in self.channel_map.items():
- xmltv.addChannel(v['id'], v['title'])
- def lookup(self, channel_id):
- if channel_id in self.channel_map:
- return self.channel_map[channel_id]
- return False
- def lookup_by_title(self, title):
- for channel_id, channel in self.channel_map.items():
- if channel['title'] == title:
- return channel_id
- return False
- class Listings(object):
- host = 'www.horizon.tv'
- path = '/oesp/api/NL/nld/web/listings'
- """
- Defaults to only few days for given channel
- """
- def __init__(self):
- self.conn = http.client.HTTPSConnection(Listings.host)
- def obtain(self, xmltv, channel_id, start = False, end = False):
- if start == False:
- start = int(time.time() * 1000)
- if end == False:
- end = start + (86400 * 2 * 1000)
- self.path = Listings.path + '?byStationId=' + channel_id + '&byStartTime=' + str(start) + '~' + str(end) + '&sort=startTime'
- self.conn.request('GET', self.path)
- response = self.conn.getresponse()
- if response.status != 200:
- raise Exception('Failed to GET listings url:', response.status, response.reason)
- return self.parse(response.read(), xmltv)
- def parse(self, raw, xmltv):
- # parse raw data
- data = json.loads(raw.decode('utf-8'))
- for listing in data['listings']:
- xmltv.addProgramme(listing)
- return len(data['listings'])
- class TVHXMLTVSocket(object):
- def __init__(self, path):
- self.path = path
- def __enter__(self):
- return self
- def __exit__(self, type, value, traceback):
- self.sock.close()
- def send(self, data):
- self.sock = socket.socket(socket.AF_UNIX)
- self.sock.connect(self.path)
- self.sock.sendall(data)
- self.sock.close()
- # the wanted channels
- wanted_channels = ['NPO 1 HD','NPO 2 HD','NPO 3 HD','RTL 4 HD','RTL 5 HD','SBS6 HD','RTL 7 HD','Veronica HD / Disney XD','Net5 HD','RTL 8 HD','FOX HD','Ziggo TV','Zender van de Maand','Comedy Central HD','Nickelodeon HD','Disney Channel','Discovery HD','National Geographic Channel HD','SBS9 HD','Eurosport HD','TLC HD','MTV HD','24Kitchen HD','XITE','FOXlife HD','HISTORY HD','Comedy Central Family','één HD','Canvas HD','Ketnet','ARD HD','ZDF HD','WDR','NDR','RTL','Sat. 1','BBC One HD','BBC Two HD','BBC Three / CBBC','BBC Four / Cbeebies','BBC Entertainment','TV5 Monde','Arte','TVE','Mediaset Italia','RTV-7','TRT Türk','2M','RTL Crime','CI','ID','Comedy Central Extra','Shorts TV','E! HD','NPO Best','NPO 101','NPO Humor TV','AMC','CBS Reality','Fashion TV HD','MyZen HD','Horse & Country TV','RTL Lounge','Discovery Science','Discovery World','Nat Geo Wild HD','Animal Planet HD','Travel Channel HD','Nostalgienet','NPO Doc','NPO Cultura','Family7','Disney XD','Disney Junior','Nicktoons','Nick Hits','Pebble TV','Nick Jr.','Cartoon Network','JimJam','Boomerang','Baby TV','NPO Zapp Xtra','RTL Telekids','Eurosport 2 HD','Extreme Sports Channel','Motors TV','NPO Nieuws','NPO Politiek','CNN','BBC World News','Euronews','Aljazeera English','CNBC Europe','CCTV News','The Indonesian Channel','TV538','MTV Music 24','DanceTrippin','SLAMTV','MTV Brand New','Stingray LiteTV','VH1 Classic','Brava NL Klassiek','Mezzo','DJAZZ.tv','TV Oranje','100% NL TV','192TV','MTV Live HD','Eventkanaal','Ziggo Zenderoverzicht HD']
- with TVHXMLTVSocket('/home/hts/.hts/tvheadend/epggrab/xmltv.sock') as tvh_client:
- chmap = ChannelMap()
- listings = Listings()
- # add listings for each of the channels
- for channel_id, channel in chmap.channel_map.items():
- if channel['title'] in wanted_channels:
- now = datetime.date.today().timetuple()
- nr = 0
- xmltv = XMLTVDocument()
- xmltv.addChannel(channel_id, channel['title'], channel['images'])
- for i in range(0, 9):
- start = int((calendar.timegm(now) + 86400 * i) * 1000) # milis
- end = start + (86400 * 1000)
- nr = nr + listings.obtain(xmltv, channel_id, start, end)
- print('Added {:d} programmes for channel {:s}'.format(nr, channel['title']), file=sys.stderr)
- # send this channel to tvh for processing
- tvh_client.send(xmltv.document.toprettyxml(encoding='UTF-8'))
Advertisement
Add Comment
Please, Sign In to add comment