mirror of
https://github.com/fHDHR/fHDHR_NextPVR.git
synced 2025-12-06 10:26:57 -05:00
Core Rewrite
This commit is contained in:
parent
a5b3c7080d
commit
0a41d8897d
@ -1,2 +1,2 @@
|
|||||||
# coding=utf-8
|
# coding=utf-8
|
||||||
fHDHR_VERSION = "v0.2.0-beta"
|
fHDHR_VERSION = "v0.2.2-beta"
|
||||||
|
|||||||
@ -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
60
fHDHR/api/hub/__init__.py
Normal 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)
|
||||||
23
fHDHR/api/hub/device/__init__.py
Normal file
23
fHDHR/api/hub/device/__init__.py
Normal 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)
|
||||||
159
fHDHR/api/hub/device/channels.py
Normal file
159
fHDHR/api/hub/device/channels.py
Normal 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()))
|
||||||
@ -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
|
||||||
0
fHDHR/api/hub/device/epgtypes/__init__.py
Normal file
0
fHDHR/api/hub/device/epgtypes/__init__.py
Normal 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)
|
||||||
@ -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"]
|
||||||
@ -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"]))
|
||||||
|
|
||||||
@ -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):
|
||||||
@ -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():
|
||||||
|
|
||||||
@ -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
|
||||||
|
|
||||||
|
|
||||||
28
fHDHR/api/hub/files/__init__.py
Normal file
28
fHDHR/api/hub/files/__init__.py
Normal 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)
|
||||||
@ -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://",
|
||||||
16
fHDHR/api/hub/files/debug_json.py
Normal file
16
fHDHR/api/hub/files/debug_json.py
Normal 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)
|
||||||
@ -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
|
||||||
@ -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,
|
||||||
@ -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'])
|
||||||
@ -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):
|
||||||
11
fHDHR/api/hub/pages/__init__.py
Normal file
11
fHDHR/api/hub/pages/__init__.py
Normal 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)
|
||||||
@ -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")
|
||||||
@ -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):
|
||||||
|
|||||||
@ -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"]
|
||||||
|
|||||||
@ -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
|
|
||||||
@ -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):
|
||||||
@ -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
|
|
||||||
@ -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)
|
|
||||||
@ -1,2 +0,0 @@
|
|||||||
# pylama:ignore=W0611
|
|
||||||
from .index_html import Index_HTML
|
|
||||||
3
fHDHR/origin/__init__.py
Normal file
3
fHDHR/origin/__init__.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# pylama:ignore=W0611
|
||||||
|
from . import origin_channels
|
||||||
|
from . import origin_epg
|
||||||
107
fHDHR/origin/origin_channels.py
Normal file
107
fHDHR/origin/origin_channels.py
Normal 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
|
||||||
@ -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)
|
|
||||||
@ -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()
|
|
||||||
@ -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:
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user