Guest User

Untitled

a guest
Aug 12th, 2015
25
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 9.60 KB | None | 0 0
  1. """
  2. Download EPG data from Horizon and output XMLTV stuff
  3. """
  4.  
  5. import xml.dom.minidom
  6. import time
  7. import datetime
  8. import calendar
  9. import sys
  10. import json
  11. import socket
  12. import http.client
  13.  
  14. def debug_json(data):
  15.   print(json.dumps(data, sort_keys=True, indent=4))
  16.  
  17. class XMLTVDocument(object):
  18.   # this renames some of the channels
  19.   add_display_name = {}
  20.   category_map = {
  21.     'nieuws': 'News / Current affairs',
  22.     'sport': 'Sports',
  23.     'vrije tijd': 'Leisure hobbies',
  24.     'gezondheid': 'Fitness and health',
  25.     'discussie': 'Discussion / Interview / Debate',
  26.     'tv drama': 'Movie / Drama',
  27.     'komedie': 'Comedy',
  28.     'show': 'Show / Game show',
  29.     'documentaire': 'Documentary'}
  30.  
  31.   def __init__(self):
  32.     impl = xml.dom.minidom.getDOMImplementation()
  33.     doctype = impl.createDocumentType('tv', None, 'xmltv.dtd')
  34.     self.document = impl.createDocument(None, 'tv', doctype)
  35.     self.document.documentElement.setAttribute('source-info-url', 'https://horizon.tv')
  36.     self.document.documentElement.setAttribute('source-info-name', 'UPC Horizon API')
  37.     self.document.documentElement.setAttribute('generator-info-name', 'HorEPG v1.0')
  38.     self.document.documentElement.setAttribute('generator-info-url', 'beralt.nl/horepg')
  39.   def addChannel(self, channel_id, display_name, icons):
  40.     element = self.document.createElement('channel')
  41.     element.setAttribute('id', channel_id)
  42.  
  43.     if display_name in XMLTVDocument.add_display_name:
  44.       for name in XMLTVDocument.add_display_name[display_name]:
  45.         dn_element = self.document.createElement('display-name')
  46.         dn_text = self.document.createTextNode(name)
  47.         dn_element.appendChild(dn_text)
  48.         element.appendChild(dn_element)
  49.     else:
  50.       if type(display_name) == list:
  51.         for name in display_name:
  52.           dn_element = self.document.createElement('display-name')
  53.           dn_text = self.document.createTextNode(name)
  54.           dn_element.appendChild(dn_text)
  55.           element.appendChild(dn_element)
  56.       else:
  57.         dn_element = self.document.createElement('display-name')
  58.         dn_text = self.document.createTextNode(display_name)
  59.         dn_element.appendChild(dn_text)
  60.         element.appendChild(dn_element)
  61.  
  62.     self.document.documentElement.appendChild(element)
  63.   def addProgramme(self, programme):
  64.     if 'program' in programme:
  65.       if 'title' in programme['program']:
  66.         element = self.document.createElement('programme')
  67.  
  68.         element.setAttribute('start', XMLTVDocument.convert_time(int(programme['startTime']) / 1000))
  69.         element.setAttribute('stop',  XMLTVDocument.convert_time(int(programme['endTime']) / 1000))
  70.         element.setAttribute('channel', programme['stationId'])
  71.  
  72.         # quick tags
  73.         self.quick_tag(element, 'title', programme['program']['title'])
  74.         if 'seriesEpisodeNumber' in programme['program']:
  75.           self.quick_tag(element, 'episode-num', programme['program']['seriesEpisodeNumber'], {'system': 'onscreen'})
  76.  
  77.         # fallback to shorter descriptions
  78.         if 'longDescription' in programme['program']:
  79.           self.quick_tag(element, 'desc', programme['program']['longDescription'])
  80.         elif 'description' in programme['program']:
  81.           self.quick_tag(element, 'desc', programme['program']['description'])
  82.         elif 'shortDescription' in programme['program']:
  83.           self.quick_tag(element, 'desc', programme['program']['shortDescription'])
  84.  
  85.         if 'secondaryTitle' in programme['program']:
  86.           self.quick_tag(element, 'sub-title', programme['program']['secondaryTitle'])
  87.  
  88.         # categories
  89.         if 'categories' in programme['program']:
  90.           for cat in programme['program']['categories']:
  91.             if '/' not in cat['title']:
  92.               cat_title = XMLTVDocument.map_category(cat['title'].lower())
  93.               if cat_title:
  94.                 self.quick_tag(element, 'category', cat_title)
  95.               else:
  96.                 self.quick_tag(element, 'category', cat['title'])
  97.  
  98.         self.document.documentElement.appendChild(element)
  99.       else:
  100.         #raise Exception('The given program had no title')
  101.         print('The program had no title', file=sys.stderr)
  102.     else:
  103.       #raise Exception('The listing had no program')
  104.       print('The listing had no program', file=sys.stderr)
  105.   def map_category(cat):
  106.     if cat in XMLTVDocument.category_map:
  107.       return XMLTVDocument.category_map[cat]
  108.     return False
  109.   def quick_tag(self, parent, tag, content, attributes = False):
  110.     element = self.document.createElement(tag)
  111.     text = self.document.createTextNode(content)
  112.     element.appendChild(text)
  113.     if attributes:
  114.       for k, v in attributes.items():
  115.         element.setAttribute(k, v)
  116.     parent.appendChild(element)
  117.   def convert_time(t):
  118.     return time.strftime('%Y%m%d%H%M%S', time.gmtime(t))
  119.  
  120. class ChannelMap(object):
  121.   host = 'www.horizon.tv'
  122.   path = '/oesp/api/NL/nld/web/channels/'
  123.  
  124.   def __init__(self):
  125.     conn = http.client.HTTPSConnection(ChannelMap.host)
  126.     conn.request('GET', ChannelMap.path)
  127.     response = conn.getresponse()
  128.     if(response.status == 200):
  129.       raw = response.read()
  130.     else:
  131.       raise Exception('Failed to GET channel url')
  132.     # load json
  133.     data = json.loads(raw.decode('utf-8'))
  134.     #setup channel map
  135.     self.channel_map = {}
  136.     for channel in data['channels']:
  137.       for schedule in channel['stationSchedules']:
  138.         station = schedule['station']
  139.         self.channel_map[station['id']] = station
  140.   def dump(self, xmltv):
  141.     for k, v in self.channel_map.items():
  142.       xmltv.addChannel(v['id'], v['title'])
  143.   def lookup(self, channel_id):
  144.     if channel_id in self.channel_map:
  145.       return self.channel_map[channel_id]
  146.     return False
  147.   def lookup_by_title(self, title):
  148.     for channel_id, channel in self.channel_map.items():
  149.       if channel['title'] == title:
  150.         return channel_id
  151.     return False
  152.  
  153. class Listings(object):
  154.   host = 'www.horizon.tv'
  155.   path = '/oesp/api/NL/nld/web/listings'
  156.  
  157.   """
  158.  Defaults to only few days for given channel
  159.  """
  160.   def __init__(self):
  161.     self.conn = http.client.HTTPSConnection(Listings.host)
  162.   def obtain(self, xmltv, channel_id, start = False, end = False):
  163.     if start == False:
  164.       start = int(time.time() * 1000)
  165.     if end == False:
  166.       end = start + (86400 * 2 * 1000)
  167.     self.path = Listings.path + '?byStationId=' + channel_id + '&byStartTime=' + str(start) + '~' + str(end) + '&sort=startTime'
  168.     self.conn.request('GET', self.path)
  169.     response = self.conn.getresponse()
  170.     if response.status != 200:
  171.       raise Exception('Failed to GET listings url:', response.status, response.reason)
  172.     return self.parse(response.read(), xmltv)
  173.   def parse(self, raw, xmltv):
  174.     # parse raw data
  175.     data = json.loads(raw.decode('utf-8'))
  176.     for listing in data['listings']:
  177.       xmltv.addProgramme(listing)
  178.     return len(data['listings'])
  179.  
  180. class TVHXMLTVSocket(object):
  181.   def __init__(self, path):
  182.     self.path = path
  183.   def __enter__(self):
  184.     return self
  185.   def __exit__(self, type, value, traceback):
  186.     self.sock.close()
  187.   def send(self, data):
  188.     self.sock = socket.socket(socket.AF_UNIX)
  189.     self.sock.connect(self.path)
  190.     self.sock.sendall(data)
  191.     self.sock.close()
  192.  
  193. # the wanted channels
  194. 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']
  195.  
  196. with TVHXMLTVSocket('/home/hts/.hts/tvheadend/epggrab/xmltv.sock') as tvh_client:
  197.   chmap = ChannelMap()
  198.   listings = Listings()
  199.   # add listings for each of the channels
  200.   for channel_id, channel in chmap.channel_map.items():
  201.     if channel['title'] in wanted_channels:
  202.       now = datetime.date.today().timetuple()
  203.       nr = 0
  204.       xmltv = XMLTVDocument()
  205.       xmltv.addChannel(channel_id, channel['title'], channel['images'])
  206.       for i in range(0, 9):
  207.         start = int((calendar.timegm(now) + 86400 * i) * 1000) # milis
  208.         end = start + (86400 * 1000)
  209.         nr = nr + listings.obtain(xmltv, channel_id, start, end)
  210.         print('Added {:d} programmes for channel {:s}'.format(nr, channel['title']), file=sys.stderr)
  211.       # send this channel to tvh for processing
  212.       tvh_client.send(xmltv.document.toprettyxml(encoding='UTF-8'))
Advertisement
Add Comment
Please, Sign In to add comment