mirror of
https://github.com/fHDHR/fHDHR_NextPVR.git
synced 2025-12-06 09:16:58 -05:00
commit
eadb5e5385
8
data/www/style.css
Normal file
8
data/www/style.css
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
.pull-right { float: right; }
|
||||||
|
|
||||||
|
.pull-lef { float: left; }
|
||||||
|
|
||||||
|
.center {
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
@ -1,2 +1,2 @@
|
|||||||
# coding=utf-8
|
# coding=utf-8
|
||||||
fHDHR_VERSION = "v0.2.2-beta"
|
fHDHR_VERSION = "v0.2.9-beta"
|
||||||
|
|||||||
@ -15,6 +15,19 @@ class HDHR_HTTP_Server():
|
|||||||
base_url = request.headers["host"]
|
base_url = request.headers["host"]
|
||||||
return fhdhrhub.get_index_html(base_url)
|
return fhdhrhub.get_index_html(base_url)
|
||||||
|
|
||||||
|
@app.route('/guide')
|
||||||
|
def channel_guide_html():
|
||||||
|
return fhdhrhub.get_channel_guide_html()
|
||||||
|
|
||||||
|
@app.route('/origin')
|
||||||
|
def origin_html():
|
||||||
|
base_url = request.headers["host"]
|
||||||
|
return fhdhrhub.get_origin_html(base_url)
|
||||||
|
|
||||||
|
@app.route('/style.css', methods=['GET'])
|
||||||
|
def style_css():
|
||||||
|
return send_from_directory(fhdhrhub.config.dict["filedir"]["www_dir"], 'style.css')
|
||||||
|
|
||||||
@app.route('/favicon.ico', methods=['GET'])
|
@app.route('/favicon.ico', methods=['GET'])
|
||||||
def favicon():
|
def favicon():
|
||||||
return send_from_directory(fhdhrhub.config.dict["filedir"]["www_dir"],
|
return send_from_directory(fhdhrhub.config.dict["filedir"]["www_dir"],
|
||||||
@ -79,6 +92,16 @@ class HDHR_HTTP_Server():
|
|||||||
mimetype='application/xml')
|
mimetype='application/xml')
|
||||||
return "not subscribed"
|
return "not subscribed"
|
||||||
|
|
||||||
|
@app.route('/diagnostics', methods=['GET'])
|
||||||
|
def debug_html():
|
||||||
|
base_url = request.headers["host"]
|
||||||
|
return fhdhrhub.get_diagnostics_html(base_url)
|
||||||
|
|
||||||
|
@app.route('/version', methods=['GET'])
|
||||||
|
def version_html():
|
||||||
|
base_url = request.headers["host"]
|
||||||
|
return fhdhrhub.get_version_html(base_url)
|
||||||
|
|
||||||
@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"]
|
||||||
@ -96,6 +119,14 @@ class HDHR_HTTP_Server():
|
|||||||
response=channels_m3u,
|
response=channels_m3u,
|
||||||
mimetype='text/plain')
|
mimetype='text/plain')
|
||||||
|
|
||||||
|
@app.route('/<channel>.m3u', methods=['GET'])
|
||||||
|
def channel_m3u(channel):
|
||||||
|
base_url = request.headers["host"]
|
||||||
|
channel_m3u = fhdhrhub.get_channel_m3u(base_url, channel)
|
||||||
|
return Response(status=200,
|
||||||
|
response=channel_m3u,
|
||||||
|
mimetype='text/plain')
|
||||||
|
|
||||||
@app.route('/images', methods=['GET'])
|
@app.route('/images', methods=['GET'])
|
||||||
def images():
|
def images():
|
||||||
image, imagetype = fhdhrhub.get_image(request.args)
|
image, imagetype = fhdhrhub.get_image(request.args)
|
||||||
|
|||||||
@ -11,10 +11,10 @@ class fHDHR_Hub():
|
|||||||
|
|
||||||
self.origin = origin
|
self.origin = origin
|
||||||
|
|
||||||
self.pages = pages.fHDHR_Pages(settings)
|
|
||||||
|
|
||||||
self.device = device.fHDHR_Device(settings, origin)
|
self.device = device.fHDHR_Device(settings, origin)
|
||||||
|
|
||||||
|
self.pages = pages.fHDHR_Pages(settings, self.device)
|
||||||
|
|
||||||
self.files = files.fHDHR_Files(settings, self.device)
|
self.files = files.fHDHR_Files(settings, self.device)
|
||||||
|
|
||||||
def get_xmltv(self, base_url):
|
def get_xmltv(self, base_url):
|
||||||
@ -50,6 +50,9 @@ class fHDHR_Hub():
|
|||||||
def get_channels_m3u(self, base_url):
|
def get_channels_m3u(self, base_url):
|
||||||
return self.files.m3u.get_channels_m3u(base_url)
|
return self.files.m3u.get_channels_m3u(base_url)
|
||||||
|
|
||||||
|
def get_channel_m3u(self, base_url, channel_number):
|
||||||
|
return self.files.m3u.get_channel_m3u(base_url, channel_number)
|
||||||
|
|
||||||
def get_stream_info(self, stream_args):
|
def get_stream_info(self, stream_args):
|
||||||
return self.device.watch.get_stream_info(stream_args)
|
return self.device.watch.get_stream_info(stream_args)
|
||||||
|
|
||||||
@ -58,3 +61,15 @@ class fHDHR_Hub():
|
|||||||
|
|
||||||
def get_index_html(self, base_url):
|
def get_index_html(self, base_url):
|
||||||
return self.pages.index.get_index_html(base_url)
|
return self.pages.index.get_index_html(base_url)
|
||||||
|
|
||||||
|
def get_channel_guide_html(self):
|
||||||
|
return self.pages.channel_guide.get_channel_guide_html()
|
||||||
|
|
||||||
|
def get_diagnostics_html(self, base_url):
|
||||||
|
return self.pages.diagnostics.get_diagnostics_html(base_url)
|
||||||
|
|
||||||
|
def get_version_html(self, base_url):
|
||||||
|
return self.pages.version.get_version_html(base_url)
|
||||||
|
|
||||||
|
def get_origin_html(self, base_url):
|
||||||
|
return self.pages.origin.get_origin_html(base_url)
|
||||||
|
|||||||
@ -14,7 +14,7 @@ class fHDHR_Device():
|
|||||||
|
|
||||||
self.epg = epg.EPG(settings, self.channels)
|
self.epg = epg.EPG(settings, self.channels)
|
||||||
|
|
||||||
self.tuners = Tuners(settings)
|
self.tuners = Tuners(settings, self.epg)
|
||||||
|
|
||||||
self.watch = WatchStream(settings, self.channels, self.tuners)
|
self.watch = WatchStream(settings, self.channels, self.tuners)
|
||||||
|
|
||||||
|
|||||||
@ -56,6 +56,12 @@ class Channels():
|
|||||||
self.list_update_time = None
|
self.list_update_time = None
|
||||||
self.get_channels()
|
self.get_channels()
|
||||||
|
|
||||||
|
def get_origin_status(self):
|
||||||
|
try:
|
||||||
|
return self.origin.get_status_dict()
|
||||||
|
except AttributeError:
|
||||||
|
return {}
|
||||||
|
|
||||||
def get_channels(self, forceupdate=False):
|
def get_channels(self, forceupdate=False):
|
||||||
"""Pull Channels from origin.
|
"""Pull Channels from origin.
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
import time
|
import time
|
||||||
|
import datetime
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from multiprocessing import Process
|
from multiprocessing import Process
|
||||||
|
|
||||||
@ -16,6 +17,8 @@ class EPG():
|
|||||||
|
|
||||||
self.origin = origin_epg.originEPG(settings, channels)
|
self.origin = origin_epg.originEPG(settings, channels)
|
||||||
|
|
||||||
|
self.epgdict = None
|
||||||
|
|
||||||
self.epg_method_selfadd()
|
self.epg_method_selfadd()
|
||||||
|
|
||||||
self.epg_method = self.config.dict["fhdhr"]["epg_method"]
|
self.epg_method = self.config.dict["fhdhr"]["epg_method"]
|
||||||
@ -31,12 +34,31 @@ class EPG():
|
|||||||
self.epgscan = Process(target=self.epgServerProcess)
|
self.epgscan = Process(target=self.epgServerProcess)
|
||||||
self.epgscan.start()
|
self.epgscan.start()
|
||||||
|
|
||||||
|
def whats_on_now(self, channel):
|
||||||
|
epgdict = self.get_epg()
|
||||||
|
listings = epgdict[channel]["listing"]
|
||||||
|
for listing in listings:
|
||||||
|
nowtime = datetime.datetime.utcnow()
|
||||||
|
start_time = datetime.datetime.strptime(listing["time_start"], '%Y%m%d%H%M%S +0000')
|
||||||
|
end_time = datetime.datetime.strptime(listing["time_end"], '%Y%m%d%H%M%S +0000')
|
||||||
|
if start_time <= nowtime <= end_time:
|
||||||
|
epgitem = epgdict[channel].copy()
|
||||||
|
epgitem["listing"] = [listing]
|
||||||
|
return epgitem
|
||||||
|
return None
|
||||||
|
|
||||||
|
def whats_on_allchans(self):
|
||||||
|
channel_guide_list = []
|
||||||
|
for channel in self.channels.get_channels():
|
||||||
|
channel_guide_list.append(self.whats_on_now(channel["number"]))
|
||||||
|
return channel_guide_list
|
||||||
|
|
||||||
def get_epg(self):
|
def get_epg(self):
|
||||||
epgdict = None
|
if not self.epgdict:
|
||||||
if os.path.isfile(self.epg_cache_file):
|
if os.path.isfile(self.epg_cache_file):
|
||||||
with open(self.epg_cache_file, 'r') as epgfile:
|
with open(self.epg_cache_file, 'r') as epgfile:
|
||||||
epgdict = json.load(epgfile)
|
self.epgdict = json.load(epgfile)
|
||||||
return epgdict
|
return self.epgdict
|
||||||
|
|
||||||
def get_thumbnail(self, itemtype, itemid):
|
def get_thumbnail(self, itemtype, itemid):
|
||||||
if itemtype == "channel":
|
if itemtype == "channel":
|
||||||
@ -86,6 +108,7 @@ class EPG():
|
|||||||
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.")
|
||||||
|
self.epgdict = programguide
|
||||||
|
|
||||||
def epgServerProcess(self):
|
def epgServerProcess(self):
|
||||||
print("Starting EPG thread...")
|
print("Starting EPG thread...")
|
||||||
|
|||||||
@ -1,11 +1,14 @@
|
|||||||
import threading
|
import threading
|
||||||
|
import datetime
|
||||||
|
|
||||||
from fHDHR.exceptions import TunerError
|
from fHDHR.exceptions import TunerError
|
||||||
|
from fHDHR.tools import humanized_time
|
||||||
|
|
||||||
|
|
||||||
class Tuner():
|
class Tuner():
|
||||||
def __init__(self, inum):
|
def __init__(self, inum, epg):
|
||||||
self.number = inum
|
self.number = inum
|
||||||
|
self.epg = epg
|
||||||
self.tuner_lock = threading.Lock()
|
self.tuner_lock = threading.Lock()
|
||||||
self.set_off_status()
|
self.set_off_status()
|
||||||
|
|
||||||
@ -20,6 +23,7 @@ class Tuner():
|
|||||||
"method": stream_args["method"],
|
"method": stream_args["method"],
|
||||||
"accessed": stream_args["accessed"],
|
"accessed": stream_args["accessed"],
|
||||||
"proxied_url": stream_args["channelUri"],
|
"proxied_url": stream_args["channelUri"],
|
||||||
|
"time_start": datetime.datetime.utcnow(),
|
||||||
}
|
}
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
@ -28,25 +32,28 @@ class Tuner():
|
|||||||
self.tuner_lock.release()
|
self.tuner_lock.release()
|
||||||
|
|
||||||
def get_status(self):
|
def get_status(self):
|
||||||
return self.status
|
current_status = self.status.copy()
|
||||||
|
if current_status["status"] == "Active":
|
||||||
|
current_status["Play Time"] = str(
|
||||||
|
humanized_time(
|
||||||
|
int((datetime.datetime.utcnow() - current_status["time_start"]).total_seconds())))
|
||||||
|
current_status["time_start"] = str(current_status["time_start"])
|
||||||
|
current_status["epg"] = self.epg.whats_on_now(current_status["accessed"].split("v")[-1])
|
||||||
|
return current_status
|
||||||
|
|
||||||
def set_off_status(self):
|
def set_off_status(self):
|
||||||
self.status = {
|
self.status = {"status": "Inactive"}
|
||||||
"status": "Inactive",
|
|
||||||
"method": None,
|
|
||||||
"accessed": None,
|
|
||||||
"proxied_url": None,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class Tuners():
|
class Tuners():
|
||||||
|
|
||||||
def __init__(self, settings):
|
def __init__(self, settings, epg):
|
||||||
self.config = settings
|
self.config = settings
|
||||||
|
self.epg = epg
|
||||||
self.max_tuners = int(self.config.dict["fhdhr"]["tuner_count"])
|
self.max_tuners = int(self.config.dict["fhdhr"]["tuner_count"])
|
||||||
|
|
||||||
for i in range(1, self.max_tuners + 1):
|
for i in range(1, self.max_tuners + 1):
|
||||||
exec("%s = %s" % ("self.tuner_" + str(i), "Tuner(i)"))
|
exec("%s = %s" % ("self.tuner_" + str(i), "Tuner(i, epg)"))
|
||||||
|
|
||||||
def tuner_grab(self, stream_args, tunernum=None):
|
def tuner_grab(self, stream_args, tunernum=None):
|
||||||
tunerselected = None
|
tunerselected = None
|
||||||
@ -81,3 +88,19 @@ class Tuners():
|
|||||||
for tunernum in range(1, self.max_tuners + 1):
|
for tunernum in range(1, self.max_tuners + 1):
|
||||||
all_status[tunernum] = eval("self.tuner_" + str(tunernum) + ".get_status()")
|
all_status[tunernum] = eval("self.tuner_" + str(tunernum) + ".get_status()")
|
||||||
return all_status
|
return all_status
|
||||||
|
|
||||||
|
def available_tuner_count(self):
|
||||||
|
available_tuners = 0
|
||||||
|
for tunernum in range(1, self.max_tuners + 1):
|
||||||
|
tuner_status = eval("self.tuner_" + str(tunernum) + ".get_status()")
|
||||||
|
if tuner_status["status"] == "Inactive":
|
||||||
|
available_tuners += 1
|
||||||
|
return available_tuners
|
||||||
|
|
||||||
|
def inuse_tuner_count(self):
|
||||||
|
inuse_tuners = 0
|
||||||
|
for tunernum in range(1, self.max_tuners + 1):
|
||||||
|
tuner_status = eval("self.tuner_" + str(tunernum) + ".get_status()")
|
||||||
|
if tuner_status["status"] == "Active":
|
||||||
|
inuse_tuners += 1
|
||||||
|
return inuse_tuners
|
||||||
|
|||||||
@ -6,7 +6,7 @@ from .lineup_json import Lineup_JSON
|
|||||||
from .debug_json import Debug_JSON
|
from .debug_json import Debug_JSON
|
||||||
from .lineup_status_json import Lineup_Status_JSON
|
from .lineup_status_json import Lineup_Status_JSON
|
||||||
from .xmltv_xml import xmlTV_XML
|
from .xmltv_xml import xmlTV_XML
|
||||||
from .channels_m3u import channels_M3U
|
from .m3u import channels_M3U
|
||||||
|
|
||||||
|
|
||||||
class fHDHR_Files():
|
class fHDHR_Files():
|
||||||
|
|||||||
@ -53,3 +53,50 @@ class channels_M3U():
|
|||||||
)
|
)
|
||||||
|
|
||||||
return fakefile.getvalue()
|
return fakefile.getvalue()
|
||||||
|
|
||||||
|
def get_channel_m3u(self, base_url, channel_number):
|
||||||
|
|
||||||
|
FORMAT_DESCRIPTOR = "#EXTM3U"
|
||||||
|
RECORD_MARKER = "#EXTINF"
|
||||||
|
|
||||||
|
fakefile = StringIO()
|
||||||
|
|
||||||
|
xmltvurl = ('%s%s/xmltv.xml' %
|
||||||
|
("http://",
|
||||||
|
base_url))
|
||||||
|
|
||||||
|
fakefile.write(
|
||||||
|
"%s\n" % (
|
||||||
|
FORMAT_DESCRIPTOR + " " +
|
||||||
|
"url-tvg=\"" + xmltvurl + "\"" + " " +
|
||||||
|
"x-tvg-url=\"" + xmltvurl + "\"")
|
||||||
|
)
|
||||||
|
|
||||||
|
channel = self.device.channels.get_channel_dict("number", channel_number)
|
||||||
|
|
||||||
|
logourl = ('%s%s/images?source=epg&type=channel&id=%s' %
|
||||||
|
("http://",
|
||||||
|
base_url,
|
||||||
|
str(channel['id'])))
|
||||||
|
|
||||||
|
fakefile.write(
|
||||||
|
"%s\n" % (
|
||||||
|
RECORD_MARKER + ":0" + " " +
|
||||||
|
"channelID=\"" + str(channel['id']) + "\" " +
|
||||||
|
"tvg-chno=\"" + str(channel['number']) + "\" " +
|
||||||
|
"tvg-name=\"" + str(channel['name']) + "\" " +
|
||||||
|
"tvg-id=\"" + str(channel['number']) + "\" " +
|
||||||
|
"tvg-logo=\"" + logourl + "\" " +
|
||||||
|
"group-title=\"" + self.config.dict["fhdhr"]["friendlyname"] + "," + str(channel['name']))
|
||||||
|
)
|
||||||
|
|
||||||
|
fakefile.write(
|
||||||
|
"%s\n" % (
|
||||||
|
('%s%s/auto/v%s' %
|
||||||
|
("http://",
|
||||||
|
base_url,
|
||||||
|
str(channel['number'])))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return fakefile.getvalue()
|
||||||
@ -1,11 +1,78 @@
|
|||||||
# pylama:ignore=W0611
|
# pylama:ignore=W0611
|
||||||
from .index_html import Index_HTML
|
from io import StringIO
|
||||||
|
|
||||||
from .htmlerror import HTMLerror
|
from .htmlerror import HTMLerror
|
||||||
|
from .index_html import Index_HTML
|
||||||
|
from .origin_html import Origin_HTML
|
||||||
|
from .diagnostics_html import Diagnostics_HTML
|
||||||
|
from .version_html import Version_HTML
|
||||||
|
from .channel_guide_html import Channel_Guide_HTML
|
||||||
|
|
||||||
|
|
||||||
class fHDHR_Pages():
|
class fHDHR_Pages():
|
||||||
|
|
||||||
def __init__(self, settings):
|
def __init__(self, settings, device):
|
||||||
self.config = settings
|
self.config = settings
|
||||||
self.index = Index_HTML(settings)
|
self.device = device
|
||||||
|
|
||||||
|
self.page_elements = {
|
||||||
|
"top": self.pagetop(),
|
||||||
|
"end": self.pageend()
|
||||||
|
}
|
||||||
|
|
||||||
self.htmlerror = HTMLerror(settings)
|
self.htmlerror = HTMLerror(settings)
|
||||||
|
|
||||||
|
self.index = Index_HTML(settings, self.device, self.page_elements)
|
||||||
|
self.origin = Origin_HTML(settings, self.device, self.page_elements)
|
||||||
|
self.diagnostics = Diagnostics_HTML(settings, self.device, self.page_elements)
|
||||||
|
self.version = Version_HTML(settings, self.device, self.page_elements)
|
||||||
|
self.channel_guide = Channel_Guide_HTML(settings, self.device, self.page_elements)
|
||||||
|
|
||||||
|
def pagetop(self):
|
||||||
|
friendlyname = self.config.dict["fhdhr"]["friendlyname"]
|
||||||
|
servicename = str(self.config.dict["main"]["servicename"])
|
||||||
|
|
||||||
|
return [
|
||||||
|
"<!DOCTYPE html>",
|
||||||
|
"<html>",
|
||||||
|
|
||||||
|
"<head>",
|
||||||
|
"<title>%s</title>" % friendlyname,
|
||||||
|
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">",
|
||||||
|
"<style>",
|
||||||
|
"table, th, td {",
|
||||||
|
"border: 1px solid black;",
|
||||||
|
"}",
|
||||||
|
"</style>",
|
||||||
|
"<link href=\"style.css\" rel=\"stylesheet\">",
|
||||||
|
"</head>",
|
||||||
|
"<h1 id=\"mcetoc_1cdobsl3g0\" style=\"text-align: center;\"><span style=\"text-decoration: underline;\"><strong><em>%s</em></strong></span></h1><br>" % friendlyname,
|
||||||
|
"",
|
||||||
|
"<h2>"
|
||||||
|
"<div>",
|
||||||
|
|
||||||
|
"<button class=\"pull-left\" onclick=\"OpenLink('%s')\">%s</a></button>" % ("/", "fHDHR"),
|
||||||
|
"<button class=\"pull-left\" onclick=\"OpenLink('%s')\">%s</a></button>" % ("/origin", servicename),
|
||||||
|
"<button class=\"pull-left\" onclick=\"OpenLink('%s')\">%s</a></button>" % ("/guide", "Guide"),
|
||||||
|
"<button class=\"pull-left\" onclick=\"OpenLink('%s')\">%s</a></button>" % ("/version", "Version"),
|
||||||
|
"<button class=\"pull-left\" onclick=\"OpenLink('%s')\">%s</a></button>" % ("/diagnostics", "Diagnostics"),
|
||||||
|
|
||||||
|
"<a class=\"pull-right\" style=\"padding: 5px;\" href=\"%s\">%s</a>" % ("xmltv.xml", "xmltv"),
|
||||||
|
"<a class=\"pull-right\" style=\"padding: 5px;\" href=\"%s\">%s</a>" % ("channels.m3u", "m3u"),
|
||||||
|
|
||||||
|
"</p>",
|
||||||
|
"</div>"
|
||||||
|
"<hr align=\"center\" width=\"100%\">"
|
||||||
|
]
|
||||||
|
|
||||||
|
def pageend(self):
|
||||||
|
return [
|
||||||
|
"</html>",
|
||||||
|
"",
|
||||||
|
|
||||||
|
"<script>",
|
||||||
|
"function OpenLink(NewURL) {",
|
||||||
|
" window.open(NewURL, \"_self\");",
|
||||||
|
"}",
|
||||||
|
"</script>"
|
||||||
|
]
|
||||||
|
|||||||
61
fHDHR/api/hub/pages/channel_guide_html.py
Normal file
61
fHDHR/api/hub/pages/channel_guide_html.py
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
from io import StringIO
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
from fHDHR.tools import humanized_time
|
||||||
|
|
||||||
|
|
||||||
|
class Channel_Guide_HTML():
|
||||||
|
|
||||||
|
def __init__(self, settings, device, page_elements):
|
||||||
|
self.config = settings
|
||||||
|
self.device = device
|
||||||
|
self.page_elements = page_elements
|
||||||
|
|
||||||
|
def get_channel_guide_html(self, force_update=False):
|
||||||
|
|
||||||
|
friendlyname = self.config.dict["fhdhr"]["friendlyname"]
|
||||||
|
|
||||||
|
nowtime = datetime.datetime.utcnow()
|
||||||
|
|
||||||
|
fakefile = StringIO()
|
||||||
|
|
||||||
|
for line in self.page_elements["top"]:
|
||||||
|
fakefile.write(line + "\n")
|
||||||
|
|
||||||
|
fakefile.write("<h4 id=\"mcetoc_1cdobsl3g0\" style=\"text-align: center;\"><span style=\"text-decoration: underline;\"><strong><em>What's On %s</em></strong></span></h4>\n" % friendlyname)
|
||||||
|
fakefile.write("\n")
|
||||||
|
|
||||||
|
fakefile.write("<table style=\"width:100%\">\n")
|
||||||
|
fakefile.write(" <tr>\n")
|
||||||
|
fakefile.write(" <th>Play</th>\n")
|
||||||
|
fakefile.write(" <th>Channel Name</th>\n")
|
||||||
|
fakefile.write(" <th>Channel Number</th>\n")
|
||||||
|
fakefile.write(" <th>Channel Thumbnail</th>\n")
|
||||||
|
fakefile.write(" <th>Content Title</th>\n")
|
||||||
|
fakefile.write(" <th>Content Thumbnail</th>\n")
|
||||||
|
fakefile.write(" <th>Content Description</th>\n")
|
||||||
|
fakefile.write(" <th>Content Remaining Time</th>\n")
|
||||||
|
fakefile.write(" </tr>\n")
|
||||||
|
|
||||||
|
for channel in self.device.epg.whats_on_allchans():
|
||||||
|
end_time = datetime.datetime.strptime(channel["listing"][0]["time_end"], '%Y%m%d%H%M%S +0000')
|
||||||
|
remaining_time = humanized_time(int((end_time - nowtime).total_seconds()))
|
||||||
|
play_url = ("/%s.m3u\n" % (channel["number"]))
|
||||||
|
|
||||||
|
fakefile.write(" <tr>\n")
|
||||||
|
fakefile.write(" <td><a href=\"%s\">%s</a>\n" % (play_url, "Play"))
|
||||||
|
fakefile.write(" <td>%s</td>\n" % (channel["name"]))
|
||||||
|
fakefile.write(" <td>%s</td>\n" % (channel["number"]))
|
||||||
|
fakefile.write(" <td><img src=\"%s\" alt=\"%s\" width=\"100\" height=\"100\">\n" % (channel["thumbnail"], channel["name"]))
|
||||||
|
fakefile.write(" <td>%s</td>\n" % (channel["listing"][0]["title"]))
|
||||||
|
fakefile.write(" <td><img src=\"%s\" alt=\"%s\" width=\"100\" height=\"100\">\n" % (channel["listing"][0]["thumbnail"], channel["listing"][0]["title"]))
|
||||||
|
fakefile.write(" <td>%s</td>\n" % (channel["listing"][0]["description"]))
|
||||||
|
fakefile.write(" <td>%s</td>\n" % (str(remaining_time)))
|
||||||
|
fakefile.write(" </tr>\n")
|
||||||
|
|
||||||
|
for line in self.page_elements["end"]:
|
||||||
|
fakefile.write(line + "\n")
|
||||||
|
|
||||||
|
channel_guide_html = fakefile.getvalue()
|
||||||
|
|
||||||
|
return channel_guide_html
|
||||||
43
fHDHR/api/hub/pages/diagnostics_html.py
Normal file
43
fHDHR/api/hub/pages/diagnostics_html.py
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
from io import StringIO
|
||||||
|
|
||||||
|
|
||||||
|
class Diagnostics_HTML():
|
||||||
|
|
||||||
|
def __init__(self, settings, device, page_elements):
|
||||||
|
self.config = settings
|
||||||
|
self.device = device
|
||||||
|
self.diagnostics_html = None
|
||||||
|
self.page_elements = page_elements
|
||||||
|
|
||||||
|
def get_diagnostics_html(self, base_url, force_update=False):
|
||||||
|
if not self.diagnostics_html or force_update:
|
||||||
|
|
||||||
|
fakefile = StringIO()
|
||||||
|
|
||||||
|
for line in self.page_elements["top"]:
|
||||||
|
fakefile.write(line + "\n")
|
||||||
|
|
||||||
|
# a list of 2 part lists containing button information
|
||||||
|
button_list = [
|
||||||
|
["Force Channel Update", "chanscan"],
|
||||||
|
["debug", "debug.json"],
|
||||||
|
["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")
|
||||||
|
|
||||||
|
for line in self.page_elements["end"]:
|
||||||
|
fakefile.write(line + "\n")
|
||||||
|
|
||||||
|
self.diagnostics_html = fakefile.getvalue()
|
||||||
|
|
||||||
|
return self.diagnostics_html
|
||||||
@ -3,74 +3,47 @@ from io import StringIO
|
|||||||
|
|
||||||
class Index_HTML():
|
class Index_HTML():
|
||||||
|
|
||||||
def __init__(self, settings):
|
def __init__(self, settings, device, page_elements):
|
||||||
self.config = settings
|
self.config = settings
|
||||||
self.index_html = None
|
self.device = device
|
||||||
|
self.page_elements = page_elements
|
||||||
|
|
||||||
def get_index_html(self, base_url, force_update=False):
|
def get_index_html(self, base_url, force_update=False):
|
||||||
if not self.index_html or force_update:
|
|
||||||
|
|
||||||
friendlyname = self.config.dict["fhdhr"]["friendlyname"]
|
|
||||||
|
|
||||||
fakefile = StringIO()
|
fakefile = StringIO()
|
||||||
|
|
||||||
fakefile.write("<!DOCTYPE html>\n")
|
for line in self.page_elements["top"]:
|
||||||
fakefile.write("<html>\n")
|
fakefile.write(line + "\n")
|
||||||
|
|
||||||
fakefile.write("<head>\n")
|
fakefile.write("<h4 style=\"text-align: center;\">fHDHR Status</h4>")
|
||||||
fakefile.write("<title>%s</title>\n" % friendlyname)
|
|
||||||
fakefile.write("<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\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("\n")
|
fakefile.write("\n")
|
||||||
|
|
||||||
fakefile.write("<h4 style=\"text-align: center;\">Primary fHDHR Links</h4>")
|
fakefile.write("<table class=\"center\" style=\"width:50%\">\n")
|
||||||
fakefile.write("\n")
|
fakefile.write(" <tr>\n")
|
||||||
|
fakefile.write(" <th></th>\n")
|
||||||
|
fakefile.write(" <th></th>\n")
|
||||||
|
fakefile.write(" </tr>\n")
|
||||||
|
|
||||||
# a list of 2 part lists containing button information
|
total_channels = self.device.channels.get_station_total()
|
||||||
button_list = [
|
|
||||||
["xmltv", "xmltv.xml"],
|
tuners_in_use = self.device.tuners.inuse_tuner_count()
|
||||||
["m3u", "channels.m3u"],
|
max_tuners = self.device.tuners.max_tuners
|
||||||
["debug", "debug.json"],
|
|
||||||
["Force Channel Update", "chanscan"]
|
tableguts = [
|
||||||
|
["Script Directory", str(self.config.dict["filedir"]["script_dir"])],
|
||||||
|
["Config File", str(self.config.config_file)],
|
||||||
|
["Cache Path", str(self.config.dict["filedir"]["cache_dir"])],
|
||||||
|
["Total Channels", str(total_channels)],
|
||||||
|
["Tuner Usage", "%s/%s" % (str(tuners_in_use), str(max_tuners))]
|
||||||
]
|
]
|
||||||
|
|
||||||
for button_item in button_list:
|
for guts in tableguts:
|
||||||
button_label = button_item[0]
|
fakefile.write(" <tr>\n")
|
||||||
button_path = button_item[1]
|
fakefile.write(" <td>%s</td>\n" % (guts[0]))
|
||||||
fakefile.write("<div style=\"text-align: center;\">\n")
|
fakefile.write(" <td>%s</td>\n" % (guts[1]))
|
||||||
fakefile.write(" <p><button onclick=\"OpenLink('%s')\">%s</a></button></p>\n" % (button_path, button_label))
|
fakefile.write(" </tr>\n")
|
||||||
fakefile.write("</div>\n")
|
|
||||||
fakefile.write("\n")
|
|
||||||
|
|
||||||
fakefile.write("<h4 style=\"text-align: center;\">Other fHDHR Links</h4>")
|
for line in self.page_elements["end"]:
|
||||||
|
fakefile.write(line + "\n")
|
||||||
|
|
||||||
# a list of 2 part lists containing button information
|
return fakefile.getvalue()
|
||||||
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("\n")
|
|
||||||
|
|
||||||
fakefile.write("<script>\n")
|
|
||||||
fakefile.write("function OpenLink(NewURL) {\n")
|
|
||||||
fakefile.write(" window.open(NewURL, \"_self\");\n")
|
|
||||||
fakefile.write("}\n")
|
|
||||||
fakefile.write("</script>")
|
|
||||||
|
|
||||||
self.index_html = fakefile.getvalue()
|
|
||||||
|
|
||||||
return self.index_html
|
|
||||||
|
|||||||
39
fHDHR/api/hub/pages/origin_html.py
Normal file
39
fHDHR/api/hub/pages/origin_html.py
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
from io import StringIO
|
||||||
|
|
||||||
|
|
||||||
|
class Origin_HTML():
|
||||||
|
|
||||||
|
def __init__(self, settings, device, page_elements):
|
||||||
|
self.config = settings
|
||||||
|
self.device = device
|
||||||
|
self.page_elements = page_elements
|
||||||
|
|
||||||
|
def get_origin_html(self, base_url, force_update=False):
|
||||||
|
|
||||||
|
servicename = str(self.config.dict["main"]["servicename"])
|
||||||
|
|
||||||
|
fakefile = StringIO()
|
||||||
|
|
||||||
|
for line in self.page_elements["top"]:
|
||||||
|
fakefile.write(line + "\n")
|
||||||
|
|
||||||
|
fakefile.write("<h4 style=\"text-align: center;\">%s Status</h4>" % (servicename))
|
||||||
|
fakefile.write("\n")
|
||||||
|
|
||||||
|
fakefile.write("<table class=\"center\" style=\"width:50%\">\n")
|
||||||
|
fakefile.write(" <tr>\n")
|
||||||
|
fakefile.write(" <th></th>\n")
|
||||||
|
fakefile.write(" <th></th>\n")
|
||||||
|
fakefile.write(" </tr>\n")
|
||||||
|
|
||||||
|
origin_status_dict = self.device.channels.get_origin_status()
|
||||||
|
for key in list(origin_status_dict.keys()):
|
||||||
|
fakefile.write(" <tr>\n")
|
||||||
|
fakefile.write(" <td>%s</td>\n" % (str(key)))
|
||||||
|
fakefile.write(" <td>%s</td>\n" % (str(origin_status_dict[key])))
|
||||||
|
fakefile.write(" </tr>\n")
|
||||||
|
|
||||||
|
for line in self.page_elements["end"]:
|
||||||
|
fakefile.write(line + "\n")
|
||||||
|
|
||||||
|
return fakefile.getvalue()
|
||||||
34
fHDHR/api/hub/pages/version_html.py
Normal file
34
fHDHR/api/hub/pages/version_html.py
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
from io import StringIO
|
||||||
|
|
||||||
|
from fHDHR import fHDHR_VERSION
|
||||||
|
|
||||||
|
|
||||||
|
class Version_HTML():
|
||||||
|
|
||||||
|
def __init__(self, settings, device, page_elements):
|
||||||
|
self.config = settings
|
||||||
|
self.device = device
|
||||||
|
self.page_elements = page_elements
|
||||||
|
|
||||||
|
def get_version_html(self, base_url, force_update=False):
|
||||||
|
|
||||||
|
fakefile = StringIO()
|
||||||
|
|
||||||
|
for line in self.page_elements["top"]:
|
||||||
|
fakefile.write(line + "\n")
|
||||||
|
|
||||||
|
fakefile.write("<table class=\"center\" style=\"width:50%\">\n")
|
||||||
|
fakefile.write(" <tr>\n")
|
||||||
|
fakefile.write(" <th></th>\n")
|
||||||
|
fakefile.write(" <th></th>\n")
|
||||||
|
fakefile.write(" </tr>\n")
|
||||||
|
|
||||||
|
fakefile.write(" <tr>\n")
|
||||||
|
fakefile.write(" <td>%s</td>\n" % ("fHDHR"))
|
||||||
|
fakefile.write(" <td>%s</td>\n" % (str(fHDHR_VERSION)))
|
||||||
|
fakefile.write(" </tr>\n")
|
||||||
|
|
||||||
|
for line in self.page_elements["end"]:
|
||||||
|
fakefile.write(line + "\n")
|
||||||
|
|
||||||
|
return fakefile.getvalue()
|
||||||
@ -30,6 +30,7 @@ class Config():
|
|||||||
|
|
||||||
data_dir = pathlib.Path(script_dir).joinpath('data')
|
data_dir = pathlib.Path(script_dir).joinpath('data')
|
||||||
www_dir = pathlib.Path(data_dir).joinpath('www')
|
www_dir = pathlib.Path(data_dir).joinpath('www')
|
||||||
|
www_images_dir = pathlib.Path(www_dir).joinpath('images')
|
||||||
|
|
||||||
self.dict["filedir"] = {
|
self.dict["filedir"] = {
|
||||||
"script_dir": script_dir,
|
"script_dir": script_dir,
|
||||||
@ -38,6 +39,7 @@ class Config():
|
|||||||
"cache_dir": pathlib.Path(data_dir).joinpath('cache'),
|
"cache_dir": pathlib.Path(data_dir).joinpath('cache'),
|
||||||
"internal_config": pathlib.Path(data_dir).joinpath('internal_config'),
|
"internal_config": pathlib.Path(data_dir).joinpath('internal_config'),
|
||||||
"www_dir": www_dir,
|
"www_dir": www_dir,
|
||||||
|
"www_images_dir": www_images_dir,
|
||||||
"font": pathlib.Path(data_dir).joinpath('garamond.ttf'),
|
"font": pathlib.Path(data_dir).joinpath('garamond.ttf'),
|
||||||
"favicon": pathlib.Path(data_dir).joinpath('favicon.ico'),
|
"favicon": pathlib.Path(data_dir).joinpath('favicon.ico'),
|
||||||
"epg_cache": {},
|
"epg_cache": {},
|
||||||
|
|||||||
@ -59,6 +59,18 @@ class OriginService():
|
|||||||
|
|
||||||
return loginsuccess
|
return loginsuccess
|
||||||
|
|
||||||
|
def get_status_dict(self):
|
||||||
|
nextpvr_address = ('%s%s:%s' %
|
||||||
|
("https://" if self.config.dict["origin"]["ssl"] else "http://",
|
||||||
|
self.config.dict["origin"]["address"],
|
||||||
|
str(self.config.dict["origin"]["port"]),
|
||||||
|
))
|
||||||
|
ret_status_dict = {
|
||||||
|
"Login": "Success",
|
||||||
|
"Address": nextpvr_address,
|
||||||
|
}
|
||||||
|
return ret_status_dict
|
||||||
|
|
||||||
def get_channels(self):
|
def get_channels(self):
|
||||||
|
|
||||||
data_url = ('%s%s:%s/service?method=channel.list&sid=%s' %
|
data_url = ('%s%s:%s/service?method=channel.list&sid=%s' %
|
||||||
|
|||||||
@ -76,6 +76,39 @@ def hours_between_datetime(first_time, later_time):
|
|||||||
return (timebetween.total_seconds() / 60 / 60)
|
return (timebetween.total_seconds() / 60 / 60)
|
||||||
|
|
||||||
|
|
||||||
|
def humanized_time(countdownseconds):
|
||||||
|
time = float(countdownseconds)
|
||||||
|
if time == 0:
|
||||||
|
return "just now"
|
||||||
|
year = time // (365 * 24 * 3600)
|
||||||
|
time = time % (365 * 24 * 3600)
|
||||||
|
day = time // (24 * 3600)
|
||||||
|
time = time % (24 * 3600)
|
||||||
|
time = time % (24 * 3600)
|
||||||
|
hour = time // 3600
|
||||||
|
time %= 3600
|
||||||
|
minute = time // 60
|
||||||
|
time %= 60
|
||||||
|
second = time
|
||||||
|
displaymsg = None
|
||||||
|
timearray = ['year', 'day', 'hour', 'minute', 'second']
|
||||||
|
for x in timearray:
|
||||||
|
currenttimevar = eval(x)
|
||||||
|
if currenttimevar >= 1:
|
||||||
|
timetype = x
|
||||||
|
if currenttimevar > 1:
|
||||||
|
timetype = str(x+"s")
|
||||||
|
if displaymsg:
|
||||||
|
displaymsg = str(displaymsg + " " + str(int(currenttimevar)) + " " + timetype)
|
||||||
|
else:
|
||||||
|
displaymsg = str(str(int(currenttimevar)) + " " + timetype)
|
||||||
|
if not displaymsg:
|
||||||
|
return "just now"
|
||||||
|
return displaymsg
|
||||||
|
# just for ignoring a pep error
|
||||||
|
year, day, hour, minute, second
|
||||||
|
|
||||||
|
|
||||||
class WebReq():
|
class WebReq():
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.session = requests.Session()
|
self.session = requests.Session()
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user