1
0
mirror of https://github.com/fHDHR/fHDHR_NextPVR.git synced 2025-12-06 11:07:01 -05:00

Merge pull request #39 from deathbybandaid/dev

Core Rewrite
This commit is contained in:
Deathbybandaid 2020-10-20 18:39:59 -04:00 committed by GitHub
commit c31165742e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 584 additions and 456 deletions

View File

@ -1,2 +1,2 @@
# coding=utf-8 # coding=utf-8
fHDHR_VERSION = "v0.2.0-beta" fHDHR_VERSION = "v0.2.2-beta"

View File

@ -1,87 +1,10 @@
from gevent.pywsgi import WSGIServer from gevent.pywsgi import WSGIServer
from flask import Flask, send_from_directory, request, abort, Response, stream_with_context from flask import Flask, send_from_directory, request, abort, Response, stream_with_context
from . import fHDHRdevice, fHDHRpages from . import hub
class HDHR_Hub(): fhdhrhub = hub.fHDHR_Hub()
def __init__(self):
pass
def hubprep(self, settings, origserv, epghandling):
self.config = settings
self.index = fHDHRpages.Index_HTML(settings)
self.devicexml = fHDHRdevice.Device_XML(settings)
self.discoverjson = fHDHRdevice.Discover_JSON(settings)
self.lineupxml = fHDHRdevice.Lineup_XML(settings, origserv)
self.lineupjson = fHDHRdevice.Lineup_JSON(settings, origserv)
self.lineupstatusjson = fHDHRdevice.Lineup_Status_JSON(settings, origserv)
self.images = fHDHRdevice.imageHandler(settings, epghandling)
self.tuners = fHDHRdevice.Tuners(settings)
self.watch = fHDHRdevice.WatchStream(settings, origserv, self.tuners)
self.station_scan = fHDHRdevice.Station_Scan(settings, origserv)
self.xmltv = fHDHRdevice.xmlTV_XML(settings, epghandling)
self.m3u = fHDHRdevice.channels_M3U(settings, origserv)
self.htmlerror = fHDHRdevice.HTMLerror(settings)
self.debug = fHDHRdevice.Debug_JSON(settings, origserv, epghandling)
self.origserv = origserv
self.epghandling = epghandling
def tuner_grab(self):
self.tuners.tuner_grab()
def tuner_close(self):
self.tuners.tuner_close()
def get_xmltv(self, base_url):
return self.xmltv.get_xmltv_xml(base_url)
def get_device_xml(self, base_url):
return self.devicexml.get_device_xml(base_url)
def get_discover_json(self, base_url):
return self.discoverjson.get_discover_json(base_url)
def get_lineup_status_json(self):
return self.lineupstatusjson.get_lineup_json(self.station_scan.scanning())
def get_lineup_xml(self, base_url):
return self.lineupxml.get_lineup_xml(base_url)
def get_lineup_json(self, base_url):
return self.lineupjson.get_lineup_json(base_url)
def get_debug_json(self, base_url):
return self.debug.get_debug_json(base_url, self.tuners)
def get_html_error(self, message):
return self.htmlerror.get_html_error(message)
def post_lineup_scan_start(self):
self.station_scan.scan()
def get_image(self, request_args):
return self.images.get_image(request_args)
def get_channels_m3u(self, base_url):
return self.m3u.get_channels_m3u(base_url)
def get_stream_info(self, stream_args):
return self.watch.get_stream_info(stream_args)
def get_stream(self, stream_args):
return self.watch.get_stream(stream_args)
def get_index_html(self, base_url):
return self.index.get_index_html(base_url)
hdhr = HDHR_Hub()
class HDHR_HTTP_Server(): class HDHR_HTTP_Server():
@ -90,18 +13,18 @@ class HDHR_HTTP_Server():
@app.route('/') @app.route('/')
def root_path(): def root_path():
base_url = request.headers["host"] base_url = request.headers["host"]
return hdhr.get_index_html(base_url) return fhdhrhub.get_index_html(base_url)
@app.route('/favicon.ico', methods=['GET']) @app.route('/favicon.ico', methods=['GET'])
def favicon(): def favicon():
return send_from_directory(hdhr.config.dict["filedir"]["www_dir"], return send_from_directory(fhdhrhub.config.dict["filedir"]["www_dir"],
'favicon.ico', 'favicon.ico',
mimetype='image/vnd.microsoft.icon') mimetype='image/vnd.microsoft.icon')
@app.route('/device.xml', methods=['GET']) @app.route('/device.xml', methods=['GET'])
def device_xml(): def device_xml():
base_url = request.headers["host"] base_url = request.headers["host"]
device_xml = hdhr.get_device_xml(base_url) device_xml = fhdhrhub.get_device_xml(base_url)
return Response(status=200, return Response(status=200,
response=device_xml, response=device_xml,
mimetype='application/xml') mimetype='application/xml')
@ -109,14 +32,14 @@ class HDHR_HTTP_Server():
@app.route('/discover.json', methods=['GET']) @app.route('/discover.json', methods=['GET'])
def discover_json(): def discover_json():
base_url = request.headers["host"] base_url = request.headers["host"]
discover_json = hdhr.get_discover_json(base_url) discover_json = fhdhrhub.get_discover_json(base_url)
return Response(status=200, return Response(status=200,
response=discover_json, response=discover_json,
mimetype='application/json') mimetype='application/json')
@app.route('/lineup_status.json', methods=['GET']) @app.route('/lineup_status.json', methods=['GET'])
def lineup_status_json(): def lineup_status_json():
linup_status_json = hdhr.get_lineup_status_json() linup_status_json = fhdhrhub.get_lineup_status_json()
return Response(status=200, return Response(status=200,
response=linup_status_json, response=linup_status_json,
mimetype='application/json') mimetype='application/json')
@ -124,7 +47,7 @@ class HDHR_HTTP_Server():
@app.route('/lineup.xml', methods=['GET']) @app.route('/lineup.xml', methods=['GET'])
def lineup_xml(): def lineup_xml():
base_url = request.headers["host"] base_url = request.headers["host"]
lineupxml = hdhr.get_lineup_xml(base_url) lineupxml = fhdhrhub.get_lineup_xml(base_url)
return Response(status=200, return Response(status=200,
response=lineupxml, response=lineupxml,
mimetype='application/xml') mimetype='application/xml')
@ -132,7 +55,7 @@ class HDHR_HTTP_Server():
@app.route('/lineup.json', methods=['GET']) @app.route('/lineup.json', methods=['GET'])
def lineup_json(): def lineup_json():
base_url = request.headers["host"] base_url = request.headers["host"]
station_list = hdhr.get_lineup_json(base_url) station_list = fhdhrhub.get_lineup_json(base_url)
return Response(status=200, return Response(status=200,
response=station_list, response=station_list,
mimetype='application/json') mimetype='application/json')
@ -140,7 +63,7 @@ class HDHR_HTTP_Server():
@app.route('/xmltv.xml', methods=['GET']) @app.route('/xmltv.xml', methods=['GET'])
def xmltv_xml(): def xmltv_xml():
base_url = request.headers["host"] base_url = request.headers["host"]
xmltv = hdhr.get_xmltv(base_url) xmltv = fhdhrhub.get_xmltv(base_url)
return Response(status=200, return Response(status=200,
response=xmltv, response=xmltv,
mimetype='application/xml') mimetype='application/xml')
@ -148,9 +71,9 @@ class HDHR_HTTP_Server():
@app.route('/api/xmltv') @app.route('/api/xmltv')
def api_xmltv(): def api_xmltv():
DeviceAuth = request.args.get('DeviceAuth', default=None, type=str) DeviceAuth = request.args.get('DeviceAuth', default=None, type=str)
if DeviceAuth == hdhr.config.dict["dev"]["device_auth"]: if DeviceAuth == fhdhrhub.config.dict["dev"]["device_auth"]:
base_url = request.headers["host"] base_url = request.headers["host"]
xmltv = hdhr.get_xmltv(base_url) xmltv = fhdhrhub.get_xmltv(base_url)
return Response(status=200, return Response(status=200,
response=xmltv, response=xmltv,
mimetype='application/xml') mimetype='application/xml')
@ -159,55 +82,64 @@ class HDHR_HTTP_Server():
@app.route('/debug.json', methods=['GET']) @app.route('/debug.json', methods=['GET'])
def debug_json(): def debug_json():
base_url = request.headers["host"] base_url = request.headers["host"]
debugreport = hdhr.get_debug_json(base_url) debugreport = fhdhrhub.get_debug_json(base_url)
return Response(status=200, return Response(status=200,
response=debugreport, response=debugreport,
mimetype='application/json') mimetype='application/json')
@app.route('/api/channels.m3u') @app.route('/api/channels.m3u', methods=['GET'])
@app.route('/channels.m3u', methods=['GET']) @app.route('/channels.m3u', methods=['GET'])
def channels_m3u(): def channels_m3u():
base_url = request.headers["host"] base_url = request.headers["host"]
channels_m3u = hdhr.get_channels_m3u(base_url) channels_m3u = fhdhrhub.get_channels_m3u(base_url)
return Response(status=200, return Response(status=200,
response=channels_m3u, response=channels_m3u,
mimetype='text/plain') mimetype='text/plain')
@app.route('/images', methods=['GET']) @app.route('/images', methods=['GET'])
def images(): def images():
image, imagetype = hdhr.get_image(request.args) image, imagetype = fhdhrhub.get_image(request.args)
return Response(image, content_type=imagetype, direct_passthrough=True) return Response(image, content_type=imagetype, direct_passthrough=True)
@app.route('/auto/<channel>') @app.route('/auto/<channel>')
def auto(channel): def auto(channel):
base_url = request.headers["host"]
stream_args = { stream_args = {
"channel": channel.replace('v', ''), "channel": channel.replace('v', ''),
"method": request.args.get('method', default=hdhr.config.dict["fhdhr"]["stream_type"], type=str), "method": request.args.get('method', default=fhdhrhub.config.dict["fhdhr"]["stream_type"], type=str),
"duration": request.args.get('duration', default=0, type=int), "duration": request.args.get('duration', default=0, type=int),
"accessed": str(request.headers["host"]) + str(request.url_rule), "accessed": fhdhrhub.device.channels.get_fhdhr_stream_url(base_url, channel.replace('v', '')),
} }
stream_args = hdhr.get_stream_info(stream_args) stream_args = fhdhrhub.get_stream_info(stream_args)
if stream_args["channelUri"]: if stream_args["channelUri"]:
if stream_args["method"] == "direct": if stream_args["method"] == "direct":
return Response(hdhr.get_stream(stream_args), content_type=stream_args["content_type"], direct_passthrough=True) return Response(fhdhrhub.get_stream(stream_args), content_type=stream_args["content_type"], direct_passthrough=True)
elif stream_args["method"] == "ffmpeg": elif stream_args["method"] == "ffmpeg":
return Response(stream_with_context(hdhr.get_stream(stream_args)), mimetype="video/mpeg") return Response(stream_with_context(fhdhrhub.get_stream(stream_args)), mimetype="video/mpeg")
abort(503) abort(503)
@app.route('/chanscan', methods=['GET'])
def chanscan():
fhdhrhub.post_lineup_scan_start()
linup_status_json = fhdhrhub.get_lineup_status_json()
return Response(status=200,
response=linup_status_json,
mimetype='application/json')
@app.route('/lineup.post', methods=['POST']) @app.route('/lineup.post', methods=['POST'])
def lineup_post(): def lineup_post():
if 'scan' in list(request.args.keys()): if 'scan' in list(request.args.keys()):
if request.args['scan'] == 'start': if request.args['scan'] == 'start':
hdhr.post_lineup_scan_start() fhdhrhub.post_lineup_scan_start()
return Response(status=200, mimetype='text/html') return Response(status=200, mimetype='text/html')
elif request.args['scan'] == 'abort': elif request.args['scan'] == 'abort':
return Response(status=200, mimetype='text/html') return Response(status=200, mimetype='text/html')
else: else:
print("Unknown scan command " + request.args['scan']) print("Unknown scan command " + request.args['scan'])
currenthtmlerror = hdhr.get_html_error("501 - " + request.args['scan'] + " is not a valid scan command") currenthtmlerror = fhdhrhub.get_html_error("501 - " + request.args['scan'] + " is not a valid scan command")
return Response(status=200, response=currenthtmlerror, mimetype='text/html') return Response(status=200, response=currenthtmlerror, mimetype='text/html')
else: else:
currenthtmlerror = hdhr.get_html_error("501 - not a valid command") currenthtmlerror = fhdhrhub.get_html_error("501 - not a valid command")
return Response(status=200, response=currenthtmlerror, mimetype='text/html') return Response(status=200, response=currenthtmlerror, mimetype='text/html')
def __init__(self, settings): def __init__(self, settings):
@ -224,8 +156,8 @@ class HDHR_HTTP_Server():
self.http.stop() self.http.stop()
def interface_start(settings, origserv, epghandling): def interface_start(settings, origin):
print("Starting fHDHR Web Interface") print("Starting fHDHR Web Interface")
hdhr.hubprep(settings, origserv, epghandling) fhdhrhub.setup(settings, origin)
fakhdhrserver = HDHR_HTTP_Server(settings) fakhdhrserver = HDHR_HTTP_Server(settings)
fakhdhrserver.run() fakhdhrserver.run()

60
fHDHR/api/hub/__init__.py Normal file
View File

@ -0,0 +1,60 @@
from . import device, pages, files
class fHDHR_Hub():
def __init__(self):
pass
def setup(self, settings, origin):
self.config = settings
self.origin = origin
self.pages = pages.fHDHR_Pages(settings)
self.device = device.fHDHR_Device(settings, origin)
self.files = files.fHDHR_Files(settings, self.device)
def get_xmltv(self, base_url):
return self.files.xmltv.get_xmltv_xml(base_url)
def get_device_xml(self, base_url):
return self.files.devicexml.get_device_xml(base_url)
def get_discover_json(self, base_url):
return self.files.discoverjson.get_discover_json(base_url)
def get_lineup_status_json(self):
return self.files.lineupstatusjson.get_lineup_status_json()
def get_lineup_xml(self, base_url):
return self.files.lineupxml.get_lineup_xml(base_url)
def get_lineup_json(self, base_url):
return self.files.lineupjson.get_lineup_json(base_url)
def get_debug_json(self, base_url):
return self.files.debug.get_debug_json(base_url)
def get_html_error(self, message):
return self.pages.htmlerror.get_html_error(message)
def post_lineup_scan_start(self):
self.device.station_scan.scan()
def get_image(self, request_args):
return self.device.images.get_image(request_args)
def get_channels_m3u(self, base_url):
return self.files.m3u.get_channels_m3u(base_url)
def get_stream_info(self, stream_args):
return self.device.watch.get_stream_info(stream_args)
def get_stream(self, stream_args):
return self.device.watch.get_stream(stream_args)
def get_index_html(self, base_url):
return self.pages.index.get_index_html(base_url)

View File

@ -0,0 +1,23 @@
from . import channels, epg
from .tuners import Tuners
from .watch import WatchStream
from .images import imageHandler
from .station_scan import Station_Scan
class fHDHR_Device():
def __init__(self, settings, origin):
self.config = settings
self.channels = channels.Channels(settings, origin)
self.epg = epg.EPG(settings, self.channels)
self.tuners = Tuners(settings)
self.watch = WatchStream(settings, self.channels, self.tuners)
self.images = imageHandler(settings, self.epg)
self.station_scan = Station_Scan(settings, self.channels)

View File

@ -0,0 +1,159 @@
import os
import datetime
import json
from collections import OrderedDict
from fHDHR.tools import hours_between_datetime
class ChannelNumbers():
def __init__(self, settings):
self.config = settings
self.channel_numbers_file = self.config.dict["main"]["channel_numbers"]
self.cnumbers = {}
self.load_channel_list()
def get_number(self, channel_name):
if channel_name in list(self.cnumbers.keys()):
return self.cnumbers[channel_name]
used_numbers = []
for channel_name in list(self.cnumbers.keys()):
used_numbers.append(self.cnumbers[channel_name])
for i in range(1, 1000):
if str(float(i)) not in used_numbers:
break
return str(float(i))
def set_number(self, channel_name, channel_number):
self.cnumbers[channel_name] = str(float(channel_number))
def load_channel_list(self):
if os.path.isfile(self.channel_numbers_file):
print("Loading Previously Saved Channel Numbers.")
with open(self.channel_numbers_file, 'r') as cnumbersfile:
self.cnumbers = json.load(cnumbersfile)
def save_channel_list(self):
print("Saving Channel Numbers.")
with open(self.channel_numbers_file, 'w') as cnumbersfile:
cnumbersfile.write(json.dumps(self.cnumbers, indent=4))
class Channels():
def __init__(self, settings, origin):
self.config = settings
self.origin = origin
self.channel_numbers = ChannelNumbers(settings)
self.list = {}
self.list_update_time = None
self.get_channels()
def get_channels(self, forceupdate=False):
"""Pull Channels from origin.
Output a list.
Don't pull more often than 12 hours.
"""
updatelist = False
if not self.list_update_time:
updatelist = True
elif hours_between_datetime(self.list_update_time, datetime.datetime.now()) > 12:
updatelist = True
elif forceupdate:
updatelist = True
if updatelist:
channel_dict_list = self.origin.get_channels()
channel_dict_list = self.verify_channel_info(channel_dict_list)
self.append_channel_info(channel_dict_list)
if not self.list_update_time:
print("Found " + str(len(self.list)) + " channels for " + str(self.config.dict["main"]["servicename"]))
self.list_update_time = datetime.datetime.now()
self.channel_numbers.save_channel_list()
channel_list = []
for chandict in list(self.list.keys()):
channel_list.append(self.list[chandict])
return channel_list
def get_station_list(self, base_url):
station_list = []
for c in self.get_channels():
station_list.append({
'GuideNumber': c['number'],
'GuideName': c['name'],
'URL': self.get_fhdhr_stream_url(base_url, c['number']),
})
return station_list
def get_channel_stream(self, channel_number):
if channel_number not in list(self.list.keys()):
self.get_channels()
if channel_number not in list(self.list.keys()):
return None
if "stream_url" not in list(self.list[channel_number].keys()):
chandict = self.get_channel_dict("number", channel_number)
streamlist, caching = self.origin.get_channel_stream(chandict, self.list)
if caching:
self.append_channel_info(streamlist)
return self.list[channel_number]["stream_url"]
else:
chanstreamdict = next(item for item in streamlist if item["number"] == channel_number)
return chanstreamdict["stream_url"]
return self.list[channel_number]["stream_url"]
def get_station_total(self):
return len(list(self.list.keys()))
def get_channel_dict(self, keyfind, valfind):
chanlist = self.get_channels()
return next(item for item in chanlist if item[keyfind] == valfind)
def get_fhdhr_stream_url(self, base_url, channel_number):
return ('%s%s/auto/v%s' %
("http://",
base_url,
channel_number))
def verify_channel_info(self, channel_dict_list):
"""Some Channel Information is Critical"""
cleaned_channel_dict_list = []
for station_item in channel_dict_list:
if "callsign" not in list(station_item.keys()):
station_item["callsign"] = station_item["name"]
if "id" not in list(station_item.keys()):
station_item["id"] = station_item["name"]
if "number" not in list(station_item.keys()):
station_item["number"] = self.channel_numbers.get_number(station_item["name"])
else:
station_item["number"] = str(float(station_item["number"]))
self.channel_numbers.set_number(station_item["name"], station_item["number"])
cleaned_channel_dict_list.append(station_item)
return cleaned_channel_dict_list
def append_channel_info(self, channel_dict_list):
"""Update the list dict
Take the channel dict list given.
"""
for chan in channel_dict_list:
if chan["number"] not in list(self.list.keys()):
self.list[chan["number"]] = {}
for chankey in list(chan.keys()):
self.list[chan["number"]][chankey] = chan[chankey]
self.channel_order()
def channel_order(self):
"""Verify the Channel Order"""
self.list = OrderedDict(sorted(self.list.items()))

View File

@ -1,27 +1,36 @@
import os import os
import json import json
import time
from collections import OrderedDict from collections import OrderedDict
from multiprocessing import Process
from . import blocks, zap2it from fHDHR.origin import origin_epg
from .epgtypes import blocks, zap2it
class EPGTypes(): class EPG():
def __init__(self, settings, origserv): def __init__(self, settings, channels):
self.config = settings self.config = settings
self.origin = origserv self.channels = channels
self.blocks = blocks.BlocksEPG(settings, origserv) self.origin = origin_epg.originEPG(settings, channels)
self.zap2it = zap2it.ZapEPG(settings, origserv)
self.epg_method_selfadd()
self.epg_method = self.config.dict["fhdhr"]["epg_method"] self.epg_method = self.config.dict["fhdhr"]["epg_method"]
if self.epg_method: if self.epg_method:
self.sleeptime = self.config.dict[self.epg_method]["epg_update_frequency"]
self.epg_cache_file = self.config.dict["filedir"]["epg_cache"][self.epg_method]["epg_json"] self.epg_cache_file = self.config.dict["filedir"]["epg_cache"][self.epg_method]["epg_json"]
self.epgtypename = self.epg_method self.epgtypename = self.epg_method
if self.epg_method == self.config.dict["main"]["dictpopname"] or self.epg_method == "origin": if self.epg_method in [self.config.dict["main"]["dictpopname"], "origin"]:
self.epgtypename = self.config.dict["main"]["dictpopname"] self.epgtypename = self.config.dict["main"]["dictpopname"]
self.epgscan = Process(target=self.epgServerProcess)
self.epgscan.start()
def get_epg(self): def get_epg(self):
epgdict = None epgdict = None
if os.path.isfile(self.epg_cache_file): if os.path.isfile(self.epg_cache_file):
@ -52,6 +61,11 @@ class EPGTypes():
event_list.extend(epgdict[channel]["listing"]) event_list.extend(epgdict[channel]["listing"])
return next(item for item in event_list if item["id"] == event_id) return next(item for item in event_list if item["id"] == event_id)
def epg_method_selfadd(self):
for method in self.config.dict["main"]["valid_epg_methods"]:
if method not in [None, "None", "origin", self.config.dict["main"]["dictpopname"]]:
exec("%s = %s" % ("self." + str(method), str(method) + "." + str(method) + "EPG(self.config, self.channels)"))
def update(self): def update(self):
print("Updating " + self.epgtypename + " EPG cache file.") print("Updating " + self.epgtypename + " EPG cache file.")
@ -66,3 +80,13 @@ class EPGTypes():
with open(self.epg_cache_file, 'w') as epgfile: with open(self.epg_cache_file, 'w') as epgfile:
epgfile.write(json.dumps(programguide, indent=4)) epgfile.write(json.dumps(programguide, indent=4))
print("Wrote " + self.epgtypename + " EPG cache file.") print("Wrote " + self.epgtypename + " EPG cache file.")
def epgServerProcess(self):
print("Starting EPG thread...")
try:
while True:
self.update()
time.sleep(self.sleeptime)
except KeyboardInterrupt:
pass

View File

@ -1,11 +1,11 @@
import datetime import datetime
class BlocksEPG(): class blocksEPG():
def __init__(self, settings, origserv): def __init__(self, settings, channels):
self.config = settings self.config = settings
self.origserv = origserv self.channels = channels
def update_epg(self): def update_epg(self):
programguide = {} programguide = {}
@ -28,7 +28,7 @@ class BlocksEPG():
} }
timestamps.append(timestampdict) timestamps.append(timestampdict)
for c in self.origserv.get_channels(): for c in self.channels.get_channels():
if str(c["number"]) not in list(programguide.keys()): if str(c["number"]) not in list(programguide.keys()):
programguide[str(c["number"])] = { programguide[str(c["number"])] = {
"callsign": c["callsign"], "callsign": c["callsign"],
@ -44,7 +44,7 @@ class BlocksEPG():
"time_start": timestamp['time_start'], "time_start": timestamp['time_start'],
"time_end": timestamp['time_end'], "time_end": timestamp['time_end'],
"duration_minutes": 60, "duration_minutes": 60,
"thumbnail": ("/images?source=generate&message=%s" % (str(c["id"]) + "_" + str(timestamp['time_start']))), "thumbnail": ("/images?source=generate&message=%s" % (str(c["id"]) + "_" + str(timestamp['time_start']).split(" ")[0])),
"title": "Unavailable", "title": "Unavailable",
"sub-title": "Unavailable", "sub-title": "Unavailable",
"description": "Unavailable", "description": "Unavailable",
@ -55,7 +55,7 @@ class BlocksEPG():
"seasonnumber": None, "seasonnumber": None,
"episodenumber": None, "episodenumber": None,
"isnew": False, "isnew": False,
"id": str(c["id"]) + "_" + str(timestamp['time_start']), "id": str(c["id"]) + "_" + str(timestamp['time_start']).split(" ")[0],
} }
programguide[str(c["number"])]["listing"].append(clean_prog_dict) programguide[str(c["number"])]["listing"].append(clean_prog_dict)

View File

@ -4,14 +4,14 @@ import datetime
import urllib.parse import urllib.parse
from fHDHR.tools import xmldictmaker, WebReq from fHDHR.tools import xmldictmaker, WebReq
from fHDHR.fHDHRerrors import EPGSetupError from fHDHR.exceptions import EPGSetupError
class ZapEPG(): class zap2itEPG():
def __init__(self, settings, origserv): def __init__(self, settings, channels):
self.config = settings self.config = settings
self.origserv = origserv self.channels = channels
self.web = WebReq() self.web = WebReq()
self.postalcode = self.config.dict["zap2it"]["postalcode"] self.postalcode = self.config.dict["zap2it"]["postalcode"]

View File

@ -7,9 +7,9 @@ import PIL.ImageFont
class imageHandler(): class imageHandler():
def __init__(self, settings, epghandling): def __init__(self, settings, epg):
self.config = settings self.config = settings
self.epghandling = epghandling self.epg = epg
def get_image(self, request_args): def get_image(self, request_args):
@ -28,7 +28,7 @@ class imageHandler():
return image, imagetype return image, imagetype
def get_epg_image(self, request_args): def get_epg_image(self, request_args):
imageUri = self.epghandling.get_thumbnail(request_args["type"], str(request_args["id"])) imageUri = self.epg.get_thumbnail(request_args["type"], str(request_args["id"]))
if not imageUri: if not imageUri:
return self.generate_image(request_args["type"], str(request_args["id"])) return self.generate_image(request_args["type"], str(request_args["id"]))

View File

@ -3,9 +3,9 @@ from multiprocessing import Process
class Station_Scan(): class Station_Scan():
def __init__(self, settings, origserv): def __init__(self, settings, channels):
self.config = settings self.config = settings
self.origserv = origserv self.channels = channels
self.chanscan = Process(target=self.runscan) self.chanscan = Process(target=self.runscan)
def scan(self): def scan(self):
@ -16,7 +16,7 @@ class Station_Scan():
print("Channel Scan Already In Progress!") print("Channel Scan Already In Progress!")
def runscan(self): def runscan(self):
self.origserv.get_channels(forceupdate=True) self.channels.get_channels(forceupdate=True)
print("Requested Channel Scan Complete.") print("Requested Channel Scan Complete.")
def scanning(self): def scanning(self):

View File

@ -1,13 +1,13 @@
import threading import threading
from fHDHR.fHDHRerrors import TunerError from fHDHR.exceptions import TunerError
class Tuner(): class Tuner():
def __init__(self, inum): def __init__(self, inum):
self.number = inum self.number = inum
self.tuner_lock = threading.Lock() self.tuner_lock = threading.Lock()
self.status = {} self.set_off_status()
def grab(self, stream_args): def grab(self, stream_args):
if self.tuner_lock.locked(): if self.tuner_lock.locked():
@ -24,14 +24,20 @@ class Tuner():
def close(self): def close(self):
print("Tuner #" + str(self.number) + " Shutting Down.") print("Tuner #" + str(self.number) + " Shutting Down.")
self.status = {} self.set_off_status()
self.tuner_lock.release() self.tuner_lock.release()
def get_status(self): def get_status(self):
if not self.tuner_lock.locked():
return {"status": "Inactive"}
return self.status return self.status
def set_off_status(self):
self.status = {
"status": "Inactive",
"method": None,
"accessed": None,
"proxied_url": None,
}
class Tuners(): class Tuners():

View File

@ -1,7 +1,7 @@
import subprocess import subprocess
import time import time
from fHDHR.fHDHRerrors import TunerError from fHDHR.exceptions import TunerError
import fHDHR.tools import fHDHR.tools

View File

@ -0,0 +1,28 @@
# pylama:ignore=W0611
from .discover_json import Discover_JSON
from .device_xml import Device_XML
from .lineup_xml import Lineup_XML
from .lineup_json import Lineup_JSON
from .debug_json import Debug_JSON
from .lineup_status_json import Lineup_Status_JSON
from .xmltv_xml import xmlTV_XML
from .channels_m3u import channels_M3U
class fHDHR_Files():
def __init__(self, settings, device):
self.config = settings
self.device = device
self.discoverjson = Discover_JSON(settings)
self.devicexml = Device_XML(settings)
self.lineupxml = Lineup_XML(settings, device)
self.lineupjson = Lineup_JSON(settings, device)
self.lineupstatusjson = Lineup_Status_JSON(settings, device)
self.xmltv = xmlTV_XML(settings, device)
self.m3u = channels_M3U(settings, device)
self.debug = Debug_JSON(settings, device)

View File

@ -3,9 +3,9 @@ from io import StringIO
class channels_M3U(): class channels_M3U():
def __init__(self, settings, origserv): def __init__(self, settings, device):
self.config = settings self.config = settings
self.origserv = origserv self.device = device
def get_channels_m3u(self, base_url): def get_channels_m3u(self, base_url):
@ -25,7 +25,7 @@ class channels_M3U():
"x-tvg-url=\"" + xmltvurl + "\"") "x-tvg-url=\"" + xmltvurl + "\"")
) )
for channel in self.origserv.get_channels(): for channel in self.device.channels.get_channels():
logourl = ('%s%s/images?source=epg&type=channel&id=%s' % logourl = ('%s%s/images?source=epg&type=channel&id=%s' %
("http://", ("http://",

View File

@ -0,0 +1,16 @@
import json
class Debug_JSON():
def __init__(self, settings, device):
self.config = settings
self.device = device
def get_debug_json(self, base_url):
debugjson = {
"base_url": base_url,
"total channels": self.device.channels.get_station_total(),
"tuner status": self.device.tuners.status(),
}
return json.dumps(debugjson, indent=4)

View File

@ -4,13 +4,13 @@ import json
class Lineup_JSON(): class Lineup_JSON():
lineup_json = None lineup_json = None
def __init__(self, settings, origserv): def __init__(self, settings, device):
self.config = settings self.config = settings
self.origserv = origserv self.device = device
def get_lineup_json(self, base_url, force_update=False): def get_lineup_json(self, base_url, force_update=False):
if not self.lineup_json or force_update: if not self.lineup_json or force_update:
jsonlineup = self.origserv.get_station_list(base_url) jsonlineup = self.device.channels.get_station_list(base_url)
self.lineup_json = json.dumps(jsonlineup, indent=4) self.lineup_json = json.dumps(jsonlineup, indent=4)
return self.lineup_json return self.lineup_json

View File

@ -3,21 +3,22 @@ import json
class Lineup_Status_JSON(): class Lineup_Status_JSON():
def __init__(self, settings, origserv): def __init__(self, settings, device):
self.config = settings self.config = settings
self.origserv = origserv self.device = device
def get_lineup_json(self, station_scanning): def get_lineup_status_json(self):
station_scanning = self.device.station_scan.scanning()
if station_scanning: if station_scanning:
jsonlineup = self.scan_in_progress() jsonlineup = self.scan_in_progress()
elif not self.origserv.get_station_total(): elif not self.device.channels.get_station_total():
jsonlineup = self.scan_in_progress() jsonlineup = self.scan_in_progress()
else: else:
jsonlineup = self.not_scanning() jsonlineup = self.not_scanning()
return json.dumps(jsonlineup, indent=4) return json.dumps(jsonlineup, indent=4)
def scan_in_progress(self): def scan_in_progress(self):
channel_count = self.origserv.get_station_total() channel_count = self.device.channels.get_station_total()
jsonlineup = { jsonlineup = {
"ScanInProgress": "true", "ScanInProgress": "true",
"Progress": 99, "Progress": 99,

View File

@ -7,14 +7,14 @@ from fHDHR.tools import sub_el
class Lineup_XML(): class Lineup_XML():
device_xml = None device_xml = None
def __init__(self, settings, origserv): def __init__(self, settings, device):
self.config = settings self.config = settings
self.origserv = origserv self.device = device
def get_lineup_xml(self, base_url, force_update=False): def get_lineup_xml(self, base_url, force_update=False):
if not self.device_xml or force_update: if not self.device_xml or force_update:
out = xml.etree.ElementTree.Element('Lineup') out = xml.etree.ElementTree.Element('Lineup')
station_list = self.origserv.get_station_list(base_url) station_list = self.device.channels.get_station_list(base_url)
for station_item in station_list: for station_item in station_list:
program_out = sub_el(out, 'Program') program_out = sub_el(out, 'Program')
sub_el(program_out, 'GuideNumber', station_item['GuideNumber']) sub_el(program_out, 'GuideNumber', station_item['GuideNumber'])

View File

@ -8,13 +8,13 @@ class xmlTV_XML():
"""Methods to create xmltv.xml""" """Methods to create xmltv.xml"""
xmltv_xml = None xmltv_xml = None
def __init__(self, settings, epghandling): def __init__(self, settings, device):
self.config = settings self.config = settings
self.epghandling = epghandling self.device = device
def get_xmltv_xml(self, base_url, force_update=False): def get_xmltv_xml(self, base_url, force_update=False):
epgdict = self.epghandling.epgtypes.get_epg() epgdict = self.device.epg.get_epg()
return self.create_xmltv(base_url, epgdict) return self.create_xmltv(base_url, epgdict)
def xmltv_headers(self): def xmltv_headers(self):

View File

@ -0,0 +1,11 @@
# pylama:ignore=W0611
from .index_html import Index_HTML
from .htmlerror import HTMLerror
class fHDHR_Pages():
def __init__(self, settings):
self.config = settings
self.index = Index_HTML(settings)
self.htmlerror = HTMLerror(settings)

View File

@ -23,12 +23,17 @@ class Index_HTML():
fakefile.write("</head>\n") fakefile.write("</head>\n")
fakefile.write("<h2 id=\"mcetoc_1cdobsl3g0\" style=\"text-align: center;\"><span style=\"text-decoration: underline;\"><strong><em>%s</em></strong></span></h2>\n" % friendlyname) fakefile.write("<h2 id=\"mcetoc_1cdobsl3g0\" style=\"text-align: center;\"><span style=\"text-decoration: underline;\"><strong><em>%s</em></strong></span></h2>\n" % friendlyname)
fakefile.write("\n")
fakefile.write("<h4 style=\"text-align: center;\">Primary fHDHR Links</h4>")
fakefile.write("\n")
# a list of 2 part lists containing button information # a list of 2 part lists containing button information
button_list = [ button_list = [
["xmltv", "xmltv.xml"], ["xmltv", "xmltv.xml"],
["m3u", "channels.m3u"], ["m3u", "channels.m3u"],
["debug", "debug.json"] ["debug", "debug.json"],
["Force Channel Update", "chanscan"]
] ]
for button_item in button_list: for button_item in button_list:
@ -37,6 +42,25 @@ class Index_HTML():
fakefile.write("<div style=\"text-align: center;\">\n") fakefile.write("<div style=\"text-align: center;\">\n")
fakefile.write(" <p><button onclick=\"OpenLink('%s')\">%s</a></button></p>\n" % (button_path, button_label)) fakefile.write(" <p><button onclick=\"OpenLink('%s')\">%s</a></button></p>\n" % (button_path, button_label))
fakefile.write("</div>\n") fakefile.write("</div>\n")
fakefile.write("\n")
fakefile.write("<h4 style=\"text-align: center;\">Other fHDHR Links</h4>")
# a list of 2 part lists containing button information
button_list = [
["device.xml", "device.xml"],
["discover.json", "discover.json"],
["lineup.json", "lineup.json"],
["lineup_status.json", "lineup_status.json"]
]
for button_item in button_list:
button_label = button_item[0]
button_path = button_item[1]
fakefile.write("<div style=\"text-align: center;\">\n")
fakefile.write(" <p><button onclick=\"OpenLink('%s')\">%s</a></button></p>\n" % (button_path, button_label))
fakefile.write("</div>\n")
fakefile.write("\n")
fakefile.write("</html>\n") fakefile.write("</html>\n")
fakefile.write("\n") fakefile.write("\n")

View File

@ -4,7 +4,13 @@ import time
import argparse import argparse
from multiprocessing import Process from multiprocessing import Process
from fHDHR import fHDHR_VERSION, config, originservice, ssdpserver, epghandler, fHDHRerrors, fHDHRweb from fHDHR import fHDHR_VERSION
import fHDHR.exceptions
import fHDHR.config
import fHDHR.origin
import fHDHR.api
import fHDHR.ssdpserver
ERR_CODE = 1 ERR_CODE = 1
ERR_CODE_NO_RESTART = 2 ERR_CODE_NO_RESTART = 2
@ -25,25 +31,17 @@ def build_args_parser():
def get_configuration(args, script_dir): def get_configuration(args, script_dir):
if not os.path.isfile(args.cfg): if not os.path.isfile(args.cfg):
raise config.ConfigurationNotFound(filename=args.cfg) raise fHDHR.exceptions.ConfigurationNotFound(filename=args.cfg)
return config.Config(args.cfg, script_dir) return fHDHR.config.Config(args.cfg, script_dir)
def get_originservice(settings): def run(settings, origin):
return originservice.OriginService(settings)
def run(settings, origserv, epghandling):
if settings.dict["fhdhr"]["discovery_address"]: if settings.dict["fhdhr"]["discovery_address"]:
ssdpServer = Process(target=ssdpserver.ssdpServerProcess, args=(settings,)) ssdpServer = Process(target=fHDHR.ssdpserver.ssdpServerProcess, args=(settings,))
ssdpServer.start() ssdpServer.start()
if settings.dict["fhdhr"]["epg_method"]: fhdhrweb = Process(target=fHDHR.api.interface_start, args=(settings, origin))
epgServer = Process(target=epghandler.epgServerProcess, args=(settings, epghandling))
epgServer.start()
fhdhrweb = Process(target=fHDHRweb.interface_start, args=(settings, origserv, epghandling))
fhdhrweb.start() fhdhrweb.start()
print(settings.dict["fhdhr"]["friendlyname"] + " is now running!") print(settings.dict["fhdhr"]["friendlyname"] + " is now running!")
@ -60,23 +58,17 @@ def start(args, script_dir):
try: try:
settings = get_configuration(args, script_dir) settings = get_configuration(args, script_dir)
except fHDHRerrors.ConfigurationError as e: except fHDHR.exceptions.ConfigurationError as e:
print(e) print(e)
return ERR_CODE_NO_RESTART return ERR_CODE_NO_RESTART
try: try:
origserv = get_originservice(settings) origin = fHDHR.origin.origin_channels.OriginService(settings)
except fHDHRerrors.LoginError as e: except fHDHR.exceptions.OriginSetupError as e:
print(e) print(e)
return ERR_CODE_NO_RESTART return ERR_CODE_NO_RESTART
try: return run(settings, origin)
epghandling = epghandler.EPGhandler(settings, origserv)
except fHDHRerrors.EPGSetupError as e:
print(e)
return ERR_CODE_NO_RESTART
return run(settings, origserv, epghandling)
def main(script_dir): def main(script_dir):

View File

@ -3,7 +3,7 @@ import random
import configparser import configparser
import pathlib import pathlib
from fHDHR import fHDHRerrors import fHDHR.exceptions
from fHDHR.tools import isint, isfloat, is_arithmetic from fHDHR.tools import isint, isfloat, is_arithmetic
@ -100,7 +100,7 @@ class Config():
if not self.dict[req_section][req_key]: if not self.dict[req_section][req_key]:
required_missing.append(req_item) required_missing.append(req_item)
if len(required_missing): if len(required_missing):
raise fHDHRerrors.ConfigurationError("Required configuration options missing: " + ", ".join(required_missing)) raise fHDHR.exceptions.ConfigurationError("Required configuration options missing: " + ", ".join(required_missing))
self.dict["origin"] = self.dict.pop(self.dict["main"]["dictpopname"]) self.dict["origin"] = self.dict.pop(self.dict["main"]["dictpopname"])
@ -111,7 +111,7 @@ class Config():
if self.dict["fhdhr"]["epg_method"] == self.dict["main"]["dictpopname"]: if self.dict["fhdhr"]["epg_method"] == self.dict["main"]["dictpopname"]:
self.dict["fhdhr"]["epg_method"] = "origin" self.dict["fhdhr"]["epg_method"] = "origin"
elif self.dict["fhdhr"]["epg_method"] not in self.dict["main"]["valid_epg_methods"]: elif self.dict["fhdhr"]["epg_method"] not in self.dict["main"]["valid_epg_methods"]:
raise fHDHRerrors.ConfigurationError("Invalid EPG Method. Exiting...") raise fHDHR.exceptions.ConfigurationError("Invalid EPG Method. Exiting...")
else: else:
print("EPG Method not set, will not create EPG/xmltv") print("EPG Method not set, will not create EPG/xmltv")
@ -129,11 +129,13 @@ class Config():
if self.dict["main"]["cache_dir"]: if self.dict["main"]["cache_dir"]:
print("Verifying cache directory...") print("Verifying cache directory...")
if not pathlib.Path(self.dict["main"]["cache_dir"]).is_dir(): if not pathlib.Path(self.dict["main"]["cache_dir"]).is_dir():
raise fHDHRerrors.ConfigurationError("Invalid Cache Directory. Exiting...") raise fHDHR.exceptions.ConfigurationError("Invalid Cache Directory. Exiting...")
self.dict["filedir"]["cache_dir"] = pathlib.Path(self.dict["main"]["cache_dir"]) self.dict["filedir"]["cache_dir"] = pathlib.Path(self.dict["main"]["cache_dir"])
print("Cache set to " + str(self.dict["filedir"]["cache_dir"])) print("Cache set to " + str(self.dict["filedir"]["cache_dir"]))
cache_dir = self.dict["filedir"]["cache_dir"] cache_dir = self.dict["filedir"]["cache_dir"]
self.dict["main"]["channel_numbers"] = pathlib.Path(cache_dir).joinpath("cnumbers.json")
for epg_method in self.dict["main"]["valid_epg_methods"]: for epg_method in self.dict["main"]["valid_epg_methods"]:
if epg_method and epg_method != "None": if epg_method and epg_method != "None":
epg_cache_dir = pathlib.Path(cache_dir).joinpath(epg_method) epg_cache_dir = pathlib.Path(cache_dir).joinpath(epg_method)
@ -149,7 +151,7 @@ class Config():
self.dict["filedir"]["epg_cache"][epg_method]["epg_json"] = pathlib.Path(epg_cache_dir).joinpath('epg.json') self.dict["filedir"]["epg_cache"][epg_method]["epg_json"] = pathlib.Path(epg_cache_dir).joinpath('epg.json')
if self.dict["fhdhr"]["stream_type"] not in ["direct", "ffmpeg"]: if self.dict["fhdhr"]["stream_type"] not in ["direct", "ffmpeg"]:
raise fHDHRerrors.ConfigurationError("Invalid stream type. Exiting...") raise fHDHR.exceptions.ConfigurationError("Invalid stream type. Exiting...")
if not self.dict["fhdhr"]["discovery_address"] and self.dict["fhdhr"]["address"] != "0.0.0.0": if not self.dict["fhdhr"]["discovery_address"] and self.dict["fhdhr"]["address"] != "0.0.0.0":
self.dict["fhdhr"]["discovery_address"] = self.dict["fhdhr"]["address"] self.dict["fhdhr"]["discovery_address"] = self.dict["fhdhr"]["address"]

View File

@ -1,30 +0,0 @@
import time
from . import epgtypes
class EPGhandler():
def __init__(self, settings, origserv):
self.config = settings
self.epg_method = self.config.dict["fhdhr"]["epg_method"]
if self.epg_method:
self.sleeptime = self.config.dict[self.epg_method]["epg_update_frequency"]
self.epgtypes = epgtypes.EPGTypes(settings, origserv)
def get_thumbnail(self, itemtype, itemid):
return self.epgtypes.get_thumbnail(itemtype, itemid)
def epgServerProcess(settings, epghandling):
print("Starting EPG thread...")
try:
while True:
epghandling.epgtypes.update()
time.sleep(epghandling.sleeptime)
except KeyboardInterrupt:
pass

View File

@ -7,12 +7,12 @@ class TunerError(Exception):
return 'TunerError: %s' % self.value return 'TunerError: %s' % self.value
class LoginError(Exception): class OriginSetupError(Exception):
def __init__(self, value): def __init__(self, value):
self.value = value self.value = value
def __str__(self): def __str__(self):
return 'LoginError: %s' % self.value return 'OriginSetupError: %s' % self.value
class EPGSetupError(Exception): class EPGSetupError(Exception):

View File

@ -1,16 +0,0 @@
# pylama:ignore=W0611
from .tuners import Tuners
from .images import imageHandler
from .watch import WatchStream
from .station_scan import Station_Scan
from .discover_json import Discover_JSON
from .device_xml import Device_XML
from .lineup_xml import Lineup_XML
from .lineup_json import Lineup_JSON
from .debug_json import Debug_JSON
from .lineup_status_json import Lineup_Status_JSON
from .xmltv_xml import xmlTV_XML
from .channels_m3u import channels_M3U
from .htmlerror import HTMLerror

View File

@ -1,16 +0,0 @@
import json
class Debug_JSON():
def __init__(self, settings, origserv, epghandling):
self.config = settings
self.origserv = origserv
def get_debug_json(self, base_url, tuners):
debugjson = {
"base_url": base_url,
"total channels": self.origserv.get_station_total(),
"tuner status": tuners.status(),
}
return json.dumps(debugjson, indent=4)

View File

@ -1,2 +0,0 @@
# pylama:ignore=W0611
from .index_html import Index_HTML

3
fHDHR/origin/__init__.py Normal file
View File

@ -0,0 +1,3 @@
# pylama:ignore=W0611
from . import origin_channels
from . import origin_epg

View File

@ -0,0 +1,107 @@
import xmltodict
import json
import hashlib
import fHDHR.tools
import fHDHR.exceptions
class OriginService():
def __init__(self, settings):
self.config = settings
self.web = fHDHR.tools.WebReq()
self.login()
def login(self):
print("Logging into NextPVR")
self.sid = self.get_sid()
if not self.sid:
raise fHDHR.exceptions.OriginSetupError("NextPVR Login Failed")
else:
print("NextPVR Login Success")
self.config.write(self.config.dict["main"]["dictpopname"], 'sid', self.sid)
def get_sid(self):
if self.config.dict["origin"]["sid"]:
return self.config.dict["origin"]["sid"]
initiate_url = ('%s%s:%s/service?method=session.initiate&ver=1.0&device=fhdhr' %
("https://" if self.config.dict["origin"]["ssl"] else "http://",
self.config.dict["origin"]["address"],
str(self.config.dict["origin"]["port"]),
))
initiate_req = self.web.session.get(initiate_url)
initiate_dict = xmltodict.parse(initiate_req.content)
sid = initiate_dict['rsp']['sid']
salt = initiate_dict['rsp']['salt']
md5PIN = hashlib.md5(str(self.config.dict["origin"]['pin']).encode('utf-8')).hexdigest()
string = ':%s:%s' % (md5PIN, salt)
clientKey = hashlib.md5(string.encode('utf-8')).hexdigest()
login_url = ('%s%s:%s/service?method=session.login&sid=%s&md5=%s' %
("https://" if self.config.dict["origin"]["ssl"] else "http://",
self.config.dict["origin"]["address"],
str(self.config.dict["origin"]["port"]),
sid,
clientKey
))
login_req = self.web.session.get(login_url)
login_dict = xmltodict.parse(login_req.content)
loginsuccess = None
if login_dict['rsp']['@stat'] == "ok":
if login_dict['rsp']['allow_watch'] == "true":
loginsuccess = sid
return loginsuccess
def get_channels(self):
data_url = ('%s%s:%s/service?method=channel.list&sid=%s' %
("https://" if self.config.dict["origin"]["ssl"] else "http://",
self.config.dict["origin"]["address"],
str(self.config.dict["origin"]["port"]),
self.sid
))
data_req = self.web.session.get(data_url)
data_dict = xmltodict.parse(data_req.content)
if 'channels' not in list(data_dict['rsp'].keys()):
print("Could not retrieve channel list")
return []
channel_o_list = data_dict['rsp']['channels']['channel']
channel_list = []
for c in channel_o_list:
dString = json.dumps(c)
channel_dict = eval(dString)
clean_station_item = {
"name": channel_dict["name"],
"callsign": channel_dict["name"],
"number": channel_dict["formatted-number"],
"id": channel_dict["id"],
}
channel_list.append(clean_station_item)
return channel_list
def get_channel_stream(self, chandict, allchandict):
caching = True
streamlist = []
streamdict = {}
streamurl = ('%s%s:%s/live?channel=%s&client=%s' %
("https://" if self.config.dict["origin"]["ssl"] else "http://",
self.config.dict["origin"]["address"],
str(self.config.dict["origin"]["port"]),
str(chandict["number"]),
str(chandict["number"]),
))
streamdict = {"number": chandict["number"], "stream_url": streamurl}
streamlist.append(streamdict)
return streamlist, caching

View File

@ -1,110 +1,17 @@
import xmltodict
import json
import hashlib
import datetime import datetime
import xmltodict
import fHDHR.tools import fHDHR.tools
class fHDHRservice(): class originEPG():
def __init__(self, settings):
def __init__(self, settings, channels):
self.config = settings self.config = settings
self.channels = channels
self.web = fHDHR.tools.WebReq() self.web = fHDHR.tools.WebReq()
def login(self):
print("Logging into NextPVR")
self.sid = self.get_sid()
if not self.sid:
return False
else:
print("NextPVR Login Success")
self.config.write(self.config.dict["main"]["dictpopname"], 'sid', self.sid)
return True
def get_sid(self):
if self.config.dict["origin"]["sid"]:
return self.config.dict["origin"]["sid"]
initiate_url = ('%s%s:%s/service?method=session.initiate&ver=1.0&device=fhdhr' %
("https://" if self.config.dict["origin"]["ssl"] else "http://",
self.config.dict["origin"]["address"],
str(self.config.dict["origin"]["port"]),
))
initiate_req = self.web.session.get(initiate_url)
initiate_dict = xmltodict.parse(initiate_req.content)
sid = initiate_dict['rsp']['sid']
salt = initiate_dict['rsp']['salt']
md5PIN = hashlib.md5(str(self.config.dict["origin"]['pin']).encode('utf-8')).hexdigest()
string = ':%s:%s' % (md5PIN, salt)
clientKey = hashlib.md5(string.encode('utf-8')).hexdigest()
login_url = ('%s%s:%s/service?method=session.login&sid=%s&md5=%s' %
("https://" if self.config.dict["origin"]["ssl"] else "http://",
self.config.dict["origin"]["address"],
str(self.config.dict["origin"]["port"]),
sid,
clientKey
))
login_req = self.web.session.get(login_url)
login_dict = xmltodict.parse(login_req.content)
loginsuccess = None
if login_dict['rsp']['@stat'] == "ok":
if login_dict['rsp']['allow_watch'] == "true":
loginsuccess = sid
return loginsuccess
def get_channels(self):
data_url = ('%s%s:%s/service?method=channel.list&sid=%s' %
("https://" if self.config.dict["origin"]["ssl"] else "http://",
self.config.dict["origin"]["address"],
str(self.config.dict["origin"]["port"]),
self.sid
))
data_req = self.web.session.get(data_url)
data_dict = xmltodict.parse(data_req.content)
if 'channels' not in list(data_dict['rsp'].keys()):
print("Could not retrieve channel list")
return []
channel_o_list = data_dict['rsp']['channels']['channel']
channel_list = []
for c in channel_o_list:
dString = json.dumps(c)
channel_dict = eval(dString)
clean_station_item = {
"name": channel_dict["name"],
"callsign": channel_dict["name"],
"number": channel_dict["formatted-number"],
"id": channel_dict["id"],
}
channel_list.append(clean_station_item)
return channel_list
def get_channel_stream(self, chandict, allchandict):
caching = True
streamlist = []
streamdict = {}
streamurl = ('%s%s:%s/live?channel=%s&client=%s' %
("https://" if self.config.dict["origin"]["ssl"] else "http://",
self.config.dict["origin"]["address"],
str(self.config.dict["origin"]["port"]),
str(chandict["number"]),
str(chandict["number"]),
))
streamdict = {"number": chandict["number"], "stream_url": streamurl}
streamlist.append(streamdict)
return streamlist, caching
def get_channel_thumbnail(self, channel_id): def get_channel_thumbnail(self, channel_id):
channel_thumb_url = ("%s%s:%s/service?method=channel.icon&channel_id=%s" % channel_thumb_url = ("%s%s:%s/service?method=channel.icon&channel_id=%s" %
("https://" if self.config.dict["origin"]["ssl"] else "http://", ("https://" if self.config.dict["origin"]["ssl"] else "http://",
@ -124,10 +31,18 @@ class fHDHRservice():
)) ))
return item_thumb_url return item_thumb_url
def xmltimestamp_nextpvr(self, epochtime):
xmltime = datetime.datetime.fromtimestamp(int(epochtime)/1000)
xmltime = str(xmltime.strftime('%Y%m%d%H%M%S')) + " +0000"
return xmltime
def duration_nextpvr_minutes(self, starttime, endtime):
return ((int(endtime) - int(starttime))/1000/60)
def update_epg(self): def update_epg(self):
programguide = {} programguide = {}
for c in self.get_channels(): for c in self.channels.get_channels():
cdict = fHDHR.tools.xmldictmaker(c, ["callsign", "name", "number", "id"]) cdict = fHDHR.tools.xmldictmaker(c, ["callsign", "name", "number", "id"])
@ -188,11 +103,3 @@ class fHDHRservice():
programguide[str(cdict["number"])]["listing"].append(clean_prog_dict) programguide[str(cdict["number"])]["listing"].append(clean_prog_dict)
return programguide return programguide
def xmltimestamp_nextpvr(self, epochtime):
xmltime = datetime.datetime.fromtimestamp(int(epochtime)/1000)
xmltime = str(xmltime.strftime('%Y%m%d%H%M%S')) + " +0000"
return xmltime
def duration_nextpvr_minutes(self, starttime, endtime):
return ((int(endtime) - int(starttime))/1000/60)

View File

@ -1,95 +0,0 @@
import datetime
from collections import OrderedDict
from . import nextpvr as serviceorigin
from fHDHR.tools import hours_between_datetime
from fHDHR.fHDHRerrors import LoginError
class OriginService():
def __init__(self, settings):
self.config = settings
self.serviceorigin = serviceorigin.fHDHRservice(settings)
if not self.serviceorigin.login():
raise LoginError(self.config.dict["main"]["servicename"] + " Login Failed.")
self.channels = {
"list": {},
"list_updated": None,
}
def append_channel_info(self, chanlist):
for chan in chanlist:
if chan["number"] not in list(self.channels["list"].keys()):
self.channels["list"][chan["number"]] = {}
for chankey in list(chan.keys()):
self.channels["list"][chan["number"]][chankey] = chan[chankey]
self.channel_order()
def channel_order(self):
self.channels["list"] = OrderedDict(sorted(self.channels["list"].items()))
def get_channels(self, forceupdate=False):
updatelist = False
if not self.channels["list_updated"]:
updatelist = True
elif hours_between_datetime(self.channels["list_updated"], datetime.datetime.now()) > 12:
updatelist = True
elif forceupdate:
updatelist = True
if updatelist:
chanlist = self.serviceorigin.get_channels()
self.append_channel_info(chanlist)
self.channels["list_updated"] = datetime.datetime.now()
channel_list = []
for chandict in list(self.channels["list"].keys()):
channel_list.append(self.channels["list"][chandict])
return channel_list
def get_fhdhr_stream_url(self, base_url, channel):
return ('%s%s/auto/v%s' %
("http://",
base_url,
channel['number']))
def get_station_list(self, base_url):
station_list = []
for c in self.get_channels():
station_list.append({
'GuideNumber': c['number'],
'GuideName': c['name'],
'URL': self.get_fhdhr_stream_url(base_url, c),
})
return station_list
def get_channel_stream(self, channel_number):
if channel_number not in list(self.channels["list"].keys()):
self.get_channels()
if channel_number not in list(self.channels["list"].keys()):
return None
if "stream_url" not in list(self.channels["list"][channel_number].keys()):
chandict = self.get_channel_dict("number", channel_number)
streamlist, caching = self.serviceorigin.get_channel_stream(chandict, self.channels["list"])
if caching:
self.append_channel_info(streamlist)
return self.channels["list"][channel_number]["stream_url"]
else:
chanstreamdict = next(item for item in streamlist if item["number"] == channel_number)
return chanstreamdict["stream_url"]
return self.channels["list"][channel_number]["stream_url"]
def get_station_total(self):
chanlist = self.get_channels()
return len(chanlist)
def get_channel_dict(self, keyfind, valfind):
chanlist = self.get_channels()
return next(item for item in chanlist if item[keyfind] == valfind)
def update_epg(self):
return self.serviceorigin.update_epg()

View File

@ -1,5 +1,3 @@
import os
import sys
import ast import ast
import requests import requests
import xml.etree.ElementTree import xml.etree.ElementTree
@ -8,12 +6,6 @@ UNARY_OPS = (ast.UAdd, ast.USub)
BINARY_OPS = (ast.Add, ast.Sub, ast.Mult, ast.Div, ast.Mod) BINARY_OPS = (ast.Add, ast.Sub, ast.Mult, ast.Div, ast.Mod)
def clean_exit():
sys.stderr.flush()
sys.stdout.flush()
os._exit(0)
def sub_el(parent, name, text=None, **kwargs): def sub_el(parent, name, text=None, **kwargs):
el = xml.etree.ElementTree.SubElement(parent, name, **kwargs) el = xml.etree.ElementTree.SubElement(parent, name, **kwargs)
if text: if text: