diff --git a/data/internal_config/fakehdhr.ini b/data/internal_config/fakehdhr.ini new file mode 100644 index 0000000..edb9cb5 --- /dev/null +++ b/data/internal_config/fakehdhr.ini @@ -0,0 +1,24 @@ +[main] +uuid = fHDHR_None +cache_dir = fHDHR_None + +[empty] +epg_update_frequency = 43200 + +[fakehdhr] +address = 0.0.0.0 +port = 5004 +discovery_address = 0.0.0.0 + +[ffmpeg] +ffmpeg_path = ffmpeg +bytes_per_read = 1152000 + +[direct_stream] +chunksize = 1048576 + +[dev] +reporting_model = HDHR4-2DT +reporting_firmware_name = hdhomerun4_dvbt +reporting_firmware_ver = 20150826 +reporting_tuner_type = Antenna diff --git a/data/internal_config/serviceconf.ini b/data/internal_config/serviceconf.ini new file mode 100644 index 0000000..e0dbd87 --- /dev/null +++ b/data/internal_config/serviceconf.ini @@ -0,0 +1,35 @@ +[main] +servicename = NextPVR +dictpopname = nextpvr +credentials = pin +reponame = FakeHDHR_NextPVR + +[fakehdhr] +friendlyname = fHDHR-NextPVR +stream_type = direct +epg_method = proxy +tuner_count = 4 + +[nextpvr] +address = localhost +port = 8866 +ssl = fHDHR_False +pin = fHDHR_None +weight = 300 +sidfile = fHDHR_None +epg_update_frequency = 43200 + +[zap2it] +delay = 5 +postalcode = fHDHR_None +affiliate_id = gapzap +country = USA +device = - +headendid = lineupId +isoverride = True +languagecode = en +pref = +timespan = 6 +timezone = +userid = - +epg_update_frequency = 43200 diff --git a/epghandler/__init__.py b/epghandler/__init__.py index 5e4c27c..d51d226 100644 --- a/epghandler/__init__.py +++ b/epghandler/__init__.py @@ -2,11 +2,9 @@ import os import sys import time from io import BytesIO -import json import xml.etree.ElementTree as ET -from . import zap2it -from . import emptyepg +from . import epgtypes def sub_el(parent, name, text=None, **kwargs): @@ -26,40 +24,20 @@ class EPGhandler(): def __init__(self, config, serviceproxy): self.config = config.config - self.serviceproxy = serviceproxy - self.zapepg = zap2it.ZapEPG(config) - self.emptyepg = emptyepg.EmptyEPG(config) - - self.epg_cache = None - - def get_epg(self): - if self.config["fakehdhr"]["epg_method"] == "empty": - epgdict = self.emptyepg.EmptyEPG() - elif self.config["fakehdhr"]["epg_method"] == "proxy": - epgdict = self.serviceproxy.epg_cache_open() - elif self.config["fakehdhr"]["epg_method"] == "zap2it": - epgdict = self.zapepg.epg_cache_open() - return epgdict - - def epg_cache_open(self): - epg_cache = None - if os.path.isfile(self.empty_cache_file): - with open(self.empty_cache_file, 'r') as epgfile: - epg_cache = json.load(epgfile) - return epg_cache + self.epgtypes = epgtypes.EPGTypes(config, serviceproxy) def get_xmltv(self, base_url): - epgdict = self.get_epg() + epgdict = self.epgtypes.get_epg() if not epgdict: return self.dummyxml() epg_method = self.config["fakehdhr"]["epg_method"] out = ET.Element('tv') - out.set('source-info-url', 'NextPVR') - out.set('source-info-name', 'NextPVR') + out.set('source-info-url', self.config["fakehdhr"]["friendlyname"]) + out.set('source-info-name', self.config["main"]["servicename"]) out.set('generator-info-name', 'FAKEHDHR') - out.set('generator-info-url', 'FAKEHDHR/FakeHDHR_NextPVR') + out.set('generator-info-url', 'FAKEHDHR/' + self.config["main"]["reponame"]) for c in list(epgdict.keys()): @@ -76,16 +54,10 @@ class EPGhandler(): sub_el(c_out, 'display-name', text=epgdict[c]['name']) if epgdict[c]["thumbnail"] is not None: - if epg_method == "empty": - sub_el(c_out, 'icon', src=("http://" + str(base_url) + str(epgdict[c]['thumbnail']))) - elif epg_method == "proxy": - sub_el(c_out, 'icon', src=("http://" + str(base_url) + str(epgdict[c]['thumbnail']))) - elif epg_method == "zap2it": - sub_el(c_out, 'icon', src=(str(epgdict[c]['thumbnail']))) - else: - sub_el(c_out, 'icon', src=(str(epgdict[c]['thumbnail']))) + channel_thumbnail = self.epgtypes.thumb_url(epg_method, "channel", base_url, str(epgdict[c]['thumbnail'])) + sub_el(c_out, 'icon', src=(str(channel_thumbnail))) else: - sub_el(c_out, 'icon', src=("http://" + str(base_url) + "/images?source=empty&type=channel&id=empty")) + sub_el(c_out, 'icon', src=("http://" + str(base_url) + "/images?source=empty&type=channel&id=" + c['number'])) for progitem in list(epgdict.keys()): @@ -102,12 +74,7 @@ class EPGhandler(): sub_el(prog_out, 'desc', lang='en', text=program['description']) - if ('movie' in program["genres"] or 'Movie' in program["genres"]) and program['releaseyear']: - sub_el(prog_out, 'sub-title', lang='en', text='Movie: ' + program['releaseyear']) - elif 'episodetitle' in program.keys(): - sub_el(prog_out, 'sub-title', lang='en', text=program['episodetitle']) - else: - sub_el(prog_out, 'sub-title', lang='en', text='Movie: ' + program['sub-title']) + sub_el(prog_out, 'sub-title', lang='en', text='Movie: ' + program['sub-title']) sub_el(prog_out, 'length', units='minutes', text=str(int(program['duration_minutes']))) @@ -128,16 +95,10 @@ class EPGhandler(): text='S%02dE%02d' % (s_, e_)) if program["thumbnail"] is not None: - if epg_method == "empty": - sub_el(prog_out, 'icon', src=("http://" + str(base_url) + str(program['thumbnail']))) - elif epg_method == "proxy": - sub_el(prog_out, 'icon', src=("http://" + str(base_url) + str(program['thumbnail']))) - elif epg_method == "zap2it": - sub_el(prog_out, 'icon', src=(str(program['thumbnail']))) - else: - sub_el(prog_out, 'icon', src=(str(program['thumbnail']))) + content_thumbnail = self.epgtypes.thumb_url(epg_method, "content", base_url, str(epgdict[c]['thumbnail'])) + sub_el(prog_out, 'icon', src=(str(content_thumbnail))) else: - sub_el(prog_out, 'icon', src=("http://" + str(base_url) + "/images?source=empty&type=content&id=empty")) + sub_el(prog_out, 'icon', src=("http://" + str(base_url) + "/images?source=empty&type=content&id=" + program['title'])) if program['rating']: rating_out = sub_el(prog_out, 'rating', system="MPAA") @@ -153,38 +114,25 @@ class EPGhandler(): def dummyxml(self): out = ET.Element('tv') - out.set('source-info-url', 'NextPVR') - out.set('source-info-name', 'NextPVR') + out.set('source-info-url', self.config["fakehdhr"]["friendlyname"]) + out.set('source-info-name', self.config["main"]["servicename"]) out.set('generator-info-name', 'FAKEHDHR') - out.set('generator-info-url', 'FAKEHDHR/FakeHDHR_NextPVR') + out.set('generator-info-url', 'FAKEHDHR/' + self.config["main"]["reponame"]) fakefile = BytesIO() fakefile.write(b'\n') fakefile.write(ET.tostring(out, encoding='UTF-8')) return fakefile.getvalue() - def update(self): - if self.config["fakehdhr"]["epg_method"] == "empty": - self.emptyepg.update_epg() - elif self.config["fakehdhr"]["epg_method"] == "proxy": - self.serviceproxy.update_epg() - elif self.config["fakehdhr"]["epg_method"] == "zap2it": - self.zapepg.update_epg() - def epgServerProcess(config, epghandling): - if config.config["fakehdhr"]["epg_method"] == "empty": - sleeptime = config.config["main"]["empty_epg_update_frequency"] - elif config.config["fakehdhr"]["epg_method"] == "proxy": - sleeptime = config.config["nextpvr"]["epg_update_frequency"] - elif config.config["fakehdhr"]["epg_method"] == "zap2it": - sleeptime = config.config["zap2xml"]["epg_update_frequency"] + sleeptime = int(config.config[config.config["fakehdhr"]["epg_method"]]["epg_update_frequency"]) try: while True: - epghandling.update() + epghandling.epgtypes.update() time.sleep(sleeptime) except KeyboardInterrupt: diff --git a/epghandler/epgtypes/__init__.py b/epghandler/epgtypes/__init__.py new file mode 100644 index 0000000..36c3f05 --- /dev/null +++ b/epghandler/epgtypes/__init__.py @@ -0,0 +1,28 @@ +from . import zap2it +from . import empty + + +class EPGTypes(): + + def __init__(self, config, serviceproxy): + self.config = config.config + self.proxy = serviceproxy + self.zap2it = zap2it.ZapEPG(config, serviceproxy) + self.empty = empty.EmptyEPG(config, serviceproxy) + + def get_epg(self): + method_to_call = getattr(self, self.config["fakehdhr"]["epg_method"]) + func_to_call = getattr(method_to_call, 'epg_cache_open') + epgdict = func_to_call() + return epgdict + + def thumb_url(self, epg_method, thumb_type, base_url, thumbnail): + method_to_call = getattr(self, self.config["fakehdhr"]["epg_method"]) + func_to_call = getattr(method_to_call, 'thumb_url') + thumbnail = func_to_call(thumb_type, base_url, thumbnail) + return thumbnail + + def update(self): + method_to_call = getattr(self, self.config["fakehdhr"]["epg_method"]) + func_to_call = getattr(method_to_call, 'update_epg') + func_to_call() diff --git a/epghandler/emptyepg.py b/epghandler/epgtypes/empty.py similarity index 86% rename from epghandler/emptyepg.py rename to epghandler/epgtypes/empty.py index 106dfa0..6f23d0c 100644 --- a/epghandler/emptyepg.py +++ b/epghandler/epgtypes/empty.py @@ -5,15 +5,16 @@ import datetime class EmptyEPG(): - def __init__(self, config): + def __init__(self, config, serviceproxy): self.config = config.config + self.serviceproxy = serviceproxy self.postalcode = None self.epg_cache = None - self.cache_dir = config.config["main"]["empty_cache"] - self.epg_cache_file = config.config["main"]["empty_cache_file"] + self.cache_dir = config.config["empty"]["empty_cache"] + self.epg_cache_file = config.config["empty"]["empty_cache_file"] self.epg_cache = self.epg_cache_open() def epg_cache_open(self): @@ -23,6 +24,12 @@ class EmptyEPG(): epg_cache = json.load(epgfile) return epg_cache + def thumb_url(self, thumb_type, base_url, thumbnail): + if thumb_type == "channel": + return "http://" + str(base_url) + str(thumbnail) + elif thumb_type == "content": + return "http://" + str(base_url) + str(thumbnail) + def update_epg(self): print('Updating Empty EPG cache file.') @@ -79,7 +86,7 @@ class EmptyEPG(): programguide[str(c["number"])]["listing"].append(clean_prog_dict) self.epg_cache = programguide - with open(self.empty_cache_file, 'w') as epgfile: + with open(self.epg_cache_file, 'w') as epgfile: epgfile.write(json.dumps(programguide, indent=4)) print('Wrote updated Empty EPG cache file.') return programguide diff --git a/epghandler/zap2it.py b/epghandler/epgtypes/zap2it.py similarity index 86% rename from epghandler/zap2it.py rename to epghandler/epgtypes/zap2it.py index 17afb08..d19c077 100644 --- a/epghandler/zap2it.py +++ b/epghandler/epgtypes/zap2it.py @@ -34,19 +34,20 @@ def xmldictmaker(inputdict, req_items, list_items=[], str_items=[]): class ZapEPG(): - def __init__(self, config): + def __init__(self, config, serviceproxy): self.config = config.config + self.serviceproxy = serviceproxy self.postalcode = None self.epg_cache = None self.cache_dir = config.config["main"]["zap_web_cache"] - self.epg_cache_file = config.config["zap2xml"]["epg_cache"] + self.epg_cache_file = config.config["zap2it"]["epg_cache"] self.epg_cache = self.epg_cache_open() def get_location(self): - self.postalcode = self.config["zap2xml"]["postalcode"] + self.postalcode = self.config["zap2it"]["postalcode"] if self.postalcode: url = 'http://ipinfo.io/json' response = urllib.request.urlopen(url) @@ -60,6 +61,12 @@ class ZapEPG(): epg_cache = json.load(epgfile) return epg_cache + def thumb_url(self, thumb_type, base_url, thumbnail): + if thumb_type == "channel": + return thumbnail + elif thumb_type == "content": + return thumbnail + def get_cached(self, cache_key, delay, url): cache_path = self.cache_dir.joinpath(cache_key) if cache_path.is_file(): @@ -83,6 +90,7 @@ class ZapEPG(): raise with open(cache_path, 'wb') as f: f.write(result) + time.sleep(int(delay)) return result def remove_stale_cache(self, todaydate): @@ -106,26 +114,26 @@ class ZapEPG(): # Start time parameter is now rounded down to nearest `zap_timespan`, in s. zap_time = time.mktime(time.localtime()) - zap_time_window = int(self.config["zap2xml"]["timespan"]) * 3600 + zap_time_window = int(self.config["zap2it"]["timespan"]) * 3600 zap_time = int(zap_time - (zap_time % zap_time_window)) # Fetch data in `zap_timespan` chunks. - for i in range(int(7 * 24 / int(self.config["zap2xml"]["timespan"]))): + for i in range(int(7 * 24 / int(self.config["zap2it"]["timespan"]))): i_time = zap_time + (i * zap_time_window) parameters = { - 'aid': self.config["zap2xml"]['affiliate_id'], - 'country': self.config["zap2xml"]['country'], - 'device': self.config["zap2xml"]['device'], - 'headendId': self.config["zap2xml"]['headendid'], + 'aid': self.config["zap2it"]['affiliate_id'], + 'country': self.config["zap2it"]['country'], + 'device': self.config["zap2it"]['device'], + 'headendId': self.config["zap2it"]['headendid'], 'isoverride': "true", - 'languagecode': self.config["zap2xml"]['languagecode'], + 'languagecode': self.config["zap2it"]['languagecode'], 'pref': 'm,p', - 'timespan': self.config["zap2xml"]['timespan'], - 'timezone': self.config["zap2xml"]['timezone'], - 'userId': self.config["zap2xml"]['userid'], + 'timespan': self.config["zap2it"]['timespan'], + 'timezone': self.config["zap2it"]['timezone'], + 'userId': self.config["zap2it"]['userid'], 'postalCode': self.postalcode, - 'lineupId': '%s-%s-DEFAULT' % (self.config["zap2xml"]['country'], self.config["zap2xml"]['device']), + 'lineupId': '%s-%s-DEFAULT' % (self.config["zap2it"]['country'], self.config["zap2it"]['device']), 'time': i_time, 'Activity_ID': 1, 'FromPage': "TV%20Guide", @@ -134,7 +142,7 @@ class ZapEPG(): url = 'https://tvlistings.zap2it.com/api/grid?' url += urllib.parse.urlencode(parameters) - result = self.get_cached(str(i_time), self.config["zap2xml"]['delay'], url) + result = self.get_cached(str(i_time), self.config["zap2it"]['delay'], url) d = json.loads(result) for c in d['channels']: diff --git a/fakehdhr/__init__.py b/fakehdhr/__init__.py index 204d8cd..df68cfa 100644 --- a/fakehdhr/__init__.py +++ b/fakehdhr/__init__.py @@ -7,6 +7,7 @@ import json import time import requests import subprocess +import threading import PIL.Image import PIL.ImageDraw import PIL.ImageFont @@ -31,14 +32,38 @@ class HDHR_Hub(): epghandling = None station_scan = False station_list = [] + http = None def __init__(self): + self.tuner_lock = threading.Lock() + self.tuners = 0 + + def hubprep(self, config, serviceproxy, epghandling): + self.config = config.config + self.max_tuners = int(self.config["fakehdhr"]["tuner_count"]) self.station_scan = False + self.serviceproxy = serviceproxy + self.epghandling = epghandling + + def tuner_usage(self, number): + self.tuner_lock.acquire() + self.tuners += number + if self.tuners < 0: + self.tuners = 0 + elif self.tuners > self.max_tuners: + self.tuners = self.max_tuners + self.tuner_lock.release() + + def get_tuner(self): + if self.tuners <= self.max_tuners: + return True + return False def get_xmltv(self, base_url): return self.epghandling.get_xmltv(base_url) def get_image(self, req_args): + imageid = req_args["id"] if req_args["source"] == "proxy": @@ -247,67 +272,77 @@ class HDHR_HTTP_Server(): @app.route('/watch', methods=['GET']) def watch(): - if 'method' in list(request.args.keys()): - if 'channel' in list(request.args.keys()): - channelUri = hdhr.serviceproxy.get_channel_stream(str(request.args["channel"])) - if not channelUri: - abort(404) + if 'method' in list(request.args.keys()) and 'channel' in list(request.args.keys()): - if request.args["method"] == "direct": - duration = request.args.get('duration', default=0, type=int) + method = str(request.args["method"]) + channel_id = str(request.args["channel"]) - if not duration == 0: - duration += time.time() + tuner = hdhr.get_tuner() + if not tuner: + abort(503) - req = requests.get(channelUri, stream=True) + channelUri = hdhr.serviceproxy.get_channel_stream(channel_id) - def generate(): - try: - yield '' - for chunk in req.iter_content(chunk_size=hdhr.config["direct_stream"]['chunksize']): - if not duration == 0 and not time.time() < duration: - req.close() - break - yield chunk - except GeneratorExit: - req.close() - print("Connection Closed.") + if method == "direct": + duration = request.args.get('duration', default=0, type=int) - return Response(generate(), content_type=req.headers['content-type'], direct_passthrough=True) + if not duration == 0: + duration += time.time() - if request.args["method"] == "ffmpeg": + req = requests.get(channelUri, stream=True) + hdhr.tuner_usage(1) - ffmpeg_command = [hdhr.config["ffmpeg"]["ffmpeg_path"], - "-i", channelUri, - "-c", "copy", - "-f", "mpegts", - "-nostats", "-hide_banner", - "-loglevel", "warning", - "pipe:stdout" - ] + def generate(): + try: + yield '' + for chunk in req.iter_content(chunk_size=int(hdhr.config["direct_stream"]['chunksize'])): + if not duration == 0 and not time.time() < duration: + req.close() + hdhr.tuner_usage(-1) + break + yield chunk + except GeneratorExit: + req.close() + print("Connection Closed.") + hdhr.tuner_usage(-1) - ffmpeg_proc = subprocess.Popen(ffmpeg_command, stdout=subprocess.PIPE) + return Response(generate(), content_type=req.headers['content-type'], direct_passthrough=True) - def generate(): - try: - while True: - videoData = ffmpeg_proc.stdout.read(int(hdhr.config["ffmpeg"]["bytes_per_read"])) - if not videoData: - break - try: - yield videoData - except Exception as e: - ffmpeg_proc.terminate() - ffmpeg_proc.communicate() - print("Connection Closed: " + str(e)) - except GeneratorExit: - ffmpeg_proc.terminate() - ffmpeg_proc.communicate() - print("Connection Closed.") + elif method == "ffmpeg": - return Response(stream_with_context(generate()), mimetype="audio/mpeg") - abort(404) + ffmpeg_command = [hdhr.config["ffmpeg"]["ffmpeg_path"], + "-i", channelUri, + "-c", "copy", + "-f", "mpegts", + "-nostats", "-hide_banner", + "-loglevel", "warning", + "pipe:stdout" + ] + + ffmpeg_proc = subprocess.Popen(ffmpeg_command, stdout=subprocess.PIPE) + hdhr.tuner_usage(1) + + def generate(): + try: + while True: + videoData = ffmpeg_proc.stdout.read(int(hdhr.config["ffmpeg"]["bytes_per_read"])) + if not videoData: + break + try: + yield videoData + except Exception as e: + ffmpeg_proc.terminate() + ffmpeg_proc.communicate() + print("Connection Closed: " + str(e)) + hdhr.tuner_usage(-1) + except GeneratorExit: + ffmpeg_proc.terminate() + ffmpeg_proc.communicate() + print("Connection Closed.") + hdhr.tuner_usage(-1) + + return Response(stream_with_context(generate()), mimetype="audio/mpeg") @app.route('/lineup.post', methods=['POST']) def lineup_post(): @@ -334,17 +369,17 @@ class HDHR_HTTP_Server(): self.config = config.config def run(self): - http = WSGIServer(( + self.http = WSGIServer(( self.config["fakehdhr"]["address"], int(self.config["fakehdhr"]["port"]) ), self.app.wsgi_app) - http.serve_forever() + try: + self.http.serve_forever() + except KeyboardInterrupt: + self.http.stop() def interface_start(config, serviceproxy, epghandling): - hdhr.config = config.config - hdhr.station_scan = False - hdhr.serviceproxy = serviceproxy - hdhr.epghandling = epghandling + hdhr.hubprep(config, serviceproxy, epghandling) fakhdhrserver = HDHR_HTTP_Server(config) fakhdhrserver.run() diff --git a/fhdhrconfig/__init__.py b/fhdhrconfig/__init__.py index 01e3401..6b2832e 100644 --- a/fhdhrconfig/__init__.py +++ b/fhdhrconfig/__init__.py @@ -17,67 +17,16 @@ class HDHRConfig(): config_handler = configparser.ConfigParser() script_dir = None - config = { - "main": { - 'uuid': None, - "cache_dir": None, - "empty_epg_update_frequency": 43200, - }, - "nextpvr": { - "address": "localhost", - "port": 8866, - "ssl": False, - "pin": None, - "weight": 300, # subscription priority - "sidfile": None, - "epg_update_frequency": 43200, - }, - "fakehdhr": { - "address": "0.0.0.0", - "port": 5004, - "discovery_address": "0.0.0.0", - "tuner_count": 4, # number of tuners in tvh - "concurrent_listeners": 10, - "friendlyname": "fHDHR-NextPVR", - "stream_type": "direct", - "epg_method": "proxy", - "font": None, - }, - "zap2xml": { - "delay": 5, - "postalcode": None, - "affiliate_id": 'gapzap', - "country": 'USA', - "device": '-', - "headendid": "lineupId", - "isoverride": True, - "languagecode": 'en', - "pref": "", - "timespan": 6, - "timezone": "", - "userid": "-", - "epg_update_frequency": 43200, - }, - "ffmpeg": { - 'ffmpeg_path': "ffmpeg", - 'bytes_per_read': '1152000', - "font": None, - }, - "direct_stream": { - 'chunksize': 1024*1024 # usually you don't need to edit this - }, - "dev": { - 'reporting_model': 'HDHR4-2DT', - 'reporting_firmware_name': 'hdhomerun4_dvbt', - 'reporting_firmware_ver': '20150826', - 'reporting_tuner_type': "Antenna", - } - } + config = {} def __init__(self, script_dir, args): self.get_config_path(script_dir, args) + self.import_default_config(script_dir) + self.import_service_config(script_dir) self.import_config() - self.config_adjustments(script_dir) + self.critical_config(script_dir) + self.config_adjustments_this() + self.config_adjustments() def get_config_path(self, script_dir, args): if args.cfg: @@ -95,6 +44,42 @@ class HDHRConfig(): for (each_key, each_val) in self.config_handler.items(each_section): self.config[each_section.lower()][each_key.lower()] = each_val + def import_default_config(self, script_dir): + config_handler = configparser.ConfigParser() + data_dir = pathlib.Path(script_dir).joinpath('data') + internal_config_dir = pathlib.Path(data_dir).joinpath('internal_config') + serviceconf = pathlib.Path(internal_config_dir).joinpath('fakehdhr.ini') + config_handler.read(serviceconf) + for each_section in config_handler.sections(): + if each_section not in list(self.config.keys()): + self.config[each_section] = {} + for (each_key, each_val) in config_handler.items(each_section): + if each_val == "fHDHR_None": + each_val = None + elif each_val == "fHDHR_True": + each_val = True + elif each_val == "fHDHR_False": + each_val = False + self.config[each_section.lower()][each_key.lower()] = each_val + + def import_service_config(self, script_dir): + config_handler = configparser.ConfigParser() + data_dir = pathlib.Path(script_dir).joinpath('data') + internal_config_dir = pathlib.Path(data_dir).joinpath('internal_config') + serviceconf = pathlib.Path(internal_config_dir).joinpath('serviceconf.ini') + config_handler.read(serviceconf) + for each_section in config_handler.sections(): + if each_section not in list(self.config.keys()): + self.config[each_section] = {} + for (each_key, each_val) in config_handler.items(each_section): + if each_val == "fHDHR_None": + each_val = None + elif each_val == "fHDHR_True": + each_val = True + elif each_val == "fHDHR_False": + each_val = False + self.config[each_section.lower()][each_key.lower()] = each_val + def write(self, section, key, value): self.config[section][key] = value self.config_handler.set(section, key, value) @@ -102,7 +87,7 @@ class HDHRConfig(): with open(self.config_file, 'w') as config_file: self.config_handler.write(config_file) - def config_adjustments(self, script_dir): + def critical_config(self, script_dir): self.config["main"]["script_dir"] = script_dir @@ -120,41 +105,17 @@ class HDHRConfig(): clean_exit() cache_dir = self.config["main"]["cache_dir"] - if not self.config["nextpvr"]["pin"]: - print("NextPVR Login Credentials Missing. Exiting...") - clean_exit() - empty_cache = pathlib.Path(cache_dir).joinpath('empty_cache') - self.config["main"]["empty_cache"] = empty_cache + self.config["empty"]["empty_cache"] = empty_cache if not empty_cache.is_dir(): empty_cache.mkdir() - self.config["main"]["empty_cache_file"] = pathlib.Path(empty_cache).joinpath('epg.json') - - nextpvr_cache = pathlib.Path(cache_dir).joinpath('nextpvr') - self.config["main"]["nextpvr_cache"] = nextpvr_cache - if not nextpvr_cache.is_dir(): - nextpvr_cache.mkdir() - self.config["nextpvr"]["sidfile"] = pathlib.Path(nextpvr_cache).joinpath('sid.txt') - self.config["nextpvr"]["epg_cache"] = pathlib.Path(nextpvr_cache).joinpath('epg.json') - - zap_cache = pathlib.Path(cache_dir).joinpath('zap2it') - self.config["main"]["zap_cache"] = zap_cache - if not zap_cache.is_dir(): - zap_cache.mkdir() - self.config["zap2xml"]["epg_cache"] = pathlib.Path(zap_cache).joinpath('epg.json') - zap_web_cache = pathlib.Path(zap_cache).joinpath('zap_web_cache') - self.config["main"]["zap_web_cache"] = zap_web_cache - if not zap_web_cache.is_dir(): - zap_web_cache.mkdir() + self.config["empty"]["empty_cache_file"] = pathlib.Path(empty_cache).joinpath('epg.json') www_dir = pathlib.Path(data_dir).joinpath('www') self.config["main"]["www_dir"] = www_dir self.config["main"]["favicon"] = pathlib.Path(www_dir).joinpath('favicon.ico') - www_image_dir = pathlib.Path(www_dir).joinpath('images') - self.config["main"]["www_image_dir"] = www_image_dir - self.config["main"]["image_def_channel"] = pathlib.Path(www_image_dir).joinpath("default-channel-thumb.png") - self.config["main"]["image_def_content"] = pathlib.Path(www_image_dir).joinpath("default-content-thumb.png") + def config_adjustments(self): # generate UUID here for when we are not using docker if self.config["main"]["uuid"] is None: @@ -165,6 +126,52 @@ class HDHRConfig(): self.write('main', 'uuid', self.config["main"]["uuid"]) print("UUID set to: " + self.config["main"]["uuid"] + "...") + if not self.config["fakehdhr"]["discovery_address"]: + if self.config["fakehdhr"]["address"] != "0.0.0.0": + self.config["fakehdhr"]["discovery_address"] = self.config["fakehdhr"]["address"] + print("Server is set to run on " + str(self.config["fakehdhr"]["address"]) + ":" + str(self.config["fakehdhr"]["port"])) + + def config_adjustments_this(self): + self.config["proxy"] = self.config.pop(self.config["main"]["dictpopname"]) + self.config_adjustments_proxy() + self.config_adjustments_zap2it() + + def config_adjustments_proxy(self): + cache_dir = self.config["main"]["cache_dir"] + + credentials_list = self.config["main"]["credentials"].split(",") + creds_missing = False + if len(credentials_list): + for cred_item in credentials_list: + if not self.config["proxy"][cred_item]: + creds_missing = True + if creds_missing: + print(self.config["main"]["servicename"] + " Login Credentials Missing. Exiting...") + clean_exit() + + proxy_cache = pathlib.Path(cache_dir).joinpath('proxy') + self.config["main"]["proxy_cache"] = proxy_cache + if not proxy_cache.is_dir(): + proxy_cache.mkdir() + self.config["proxy"]["sidfile"] = pathlib.Path(proxy_cache).joinpath('sid.txt') + self.config["proxy"]["epg_cache"] = pathlib.Path(proxy_cache).joinpath('epg.json') + proxy_web_cache = pathlib.Path(proxy_cache).joinpath('proxy_web_cache') + self.config["main"]["proxy_web_cache"] = proxy_web_cache + if not proxy_web_cache.is_dir(): + proxy_web_cache.mkdir() + + def config_adjustments_zap2it(self): + cache_dir = self.config["main"]["cache_dir"] + + zap_cache = pathlib.Path(cache_dir).joinpath('zap2it') + self.config["main"]["zap_cache"] = zap_cache + if not zap_cache.is_dir(): + zap_cache.mkdir() + self.config["zap2it"]["epg_cache"] = pathlib.Path(zap_cache).joinpath('epg.json') + zap_web_cache = pathlib.Path(zap_cache).joinpath('zap_web_cache') + self.config["main"]["zap_web_cache"] = zap_web_cache + if not zap_web_cache.is_dir(): + zap_web_cache.mkdir() diff --git a/main.py b/main.py index d09b739..7d2d9b5 100644 --- a/main.py +++ b/main.py @@ -55,11 +55,20 @@ if __name__ == '__main__': fhdhrServer = Process(target=fakehdhr.interface_start, args=(config, serviceproxy, epghandling)) fhdhrServer.start() - print("Starting SSDP server...") - ssdpServer = Process(target=ssdpserver.ssdpServerProcess, args=(config,)) - ssdpServer.daemon = True - ssdpServer.start() + if (config.config["fakehdhr"]["discovery_address"] and + config.config["fakehdhr"]["discovery_address"] != "0.0.0.0"): + print("Starting SSDP server...") + ssdpServer = Process(target=ssdpserver.ssdpServerProcess, args=(config,)) + ssdpServer.daemon = True + ssdpServer.start() + else: + ssdpServer = None + print("Not Starting SSDP server...") except KeyboardInterrupt: print('^C received, shutting down the server') + epgServer.terminate() + fhdhrServer.terminate() + if ssdpServer: + ssdpServer.terminate() clean_exit() diff --git a/proxyservice/__init__.py b/proxyservice/__init__.py index e228684..76aea28 100644 --- a/proxyservice/__init__.py +++ b/proxyservice/__init__.py @@ -35,12 +35,12 @@ class NextPVR_Auth(): sidfile = None def __init__(self, config): - self.sidfile = config.config["nextpvr"]["sidfile"] - self.config["npvrPIN"] = config.config["nextpvr"]["pin"] + self.sidfile = config.config["proxy"]["sidfile"] + self.config["npvrPIN"] = config.config["proxy"]["pin"] self.config["npvrURL"] = ('%s%s:%s' % - ("https://" if config.config["nextpvr"]["ssl"] else "http://", - config.config["nextpvr"]["address"], - str(config.config["nextpvr"]["port"]), + ("https://" if config.config["proxy"]["ssl"] else "http://", + config.config["proxy"]["address"], + str(config.config["proxy"]["port"]), )) def _check_sid(self): @@ -100,7 +100,7 @@ class proxyserviceFetcher(): self.config = config.config self.epg_cache = None - self.epg_cache_file = config.config["nextpvr"]["epg_cache"] + self.epg_cache_file = config.config["proxy"]["epg_cache"] self.servicename = "NextPVRProxy" @@ -118,6 +118,12 @@ class proxyserviceFetcher(): epg_cache = json.load(epgfile) return epg_cache + def thumb_url(self, thumb_type, base_url, thumbnail): + if thumb_type == "channel": + return "http://" + str(base_url) + str(thumbnail) + elif thumb_type == "content": + return "http://" + str(base_url) + str(thumbnail) + def url_assembler(self): pass @@ -125,9 +131,9 @@ class proxyserviceFetcher(): self.auth._check_sid() url = ('%s%s:%s/service?method=channel.list&sid=%s' % - ("https://" if self.config["nextpvr"]["ssl"] else "http://", - self.config["nextpvr"]["address"], - str(self.config["nextpvr"]["port"]), + ("https://" if self.config["proxy"]["ssl"] else "http://", + self.config["proxy"]["address"], + str(self.config["proxy"]["port"]), self.auth.config['sid'] )) @@ -183,9 +189,9 @@ class proxyserviceFetcher(): def get_channel_stream(self, id): url = ('%s%s:%s/live?channel=%s&client=%s' % - ("https://" if self.config["nextpvr"]["ssl"] else "http://", - self.config["nextpvr"]["address"], - str(self.config["nextpvr"]["port"]), + ("https://" if self.config["proxy"]["ssl"] else "http://", + self.config["proxy"]["address"], + str(self.config["proxy"]["port"]), str(id), str(id), )) @@ -195,9 +201,9 @@ class proxyserviceFetcher(): streamdict = {} for c in self.get_channels(): url = ('%s%s:%s/live?channel=%s&client=%s' % - ("https://" if self.config["nextpvr"]["ssl"] else "http://", - self.config["nextpvr"]["address"], - str(self.config["nextpvr"]["port"]), + ("https://" if self.config["proxy"]["ssl"] else "http://", + self.config["proxy"]["address"], + str(self.config["proxy"]["port"]), str(c["number"]), str(c["number"]), )) @@ -206,9 +212,9 @@ class proxyserviceFetcher(): def get_channel_thumbnail(self, channel_id): channel_thumb_url = ("%s%s:%s/service?method=channel.icon&channel_id=%s" % - ("https://" if self.config["nextpvr"]["ssl"] else "http://", - self.config["nextpvr"]["address"], - str(self.config["nextpvr"]["port"]), + ("https://" if self.config["proxy"]["ssl"] else "http://", + self.config["proxy"]["address"], + str(self.config["proxy"]["port"]), str(channel_id) )) return channel_thumb_url @@ -216,9 +222,9 @@ class proxyserviceFetcher(): def get_content_thumbnail(self, content_id): self.auth._check_sid() item_thumb_url = ("%s%s:%s/service?method=channel.show.artwork&sid=%s&event_id=%s" % - ("https://" if self.config["nextpvr"]["ssl"] else "http://", - self.config["nextpvr"]["address"], - str(self.config["nextpvr"]["port"]), + ("https://" if self.config["proxy"]["ssl"] else "http://", + self.config["proxy"]["address"], + str(self.config["proxy"]["port"]), self.auth.config['sid'], str(content_id) )) @@ -246,9 +252,9 @@ class proxyserviceFetcher(): } epg_url = ('%s%s:%s/service?method=channel.listings&channel_id=%s' % - ("https://" if self.config["nextpvr"]["ssl"] else "http://", - self.config["nextpvr"]["address"], - str(self.config["nextpvr"]["port"]), + ("https://" if self.config["proxy"]["ssl"] else "http://", + self.config["proxy"]["address"], + str(self.config["proxy"]["port"]), str(cdict["id"]), )) epg_req = urllib.request.urlopen(epg_url)