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

Upgrade Channels System

This commit is contained in:
deathbybandaid 2020-11-20 15:40:12 -05:00
parent 9afc544711
commit 84735271a8
33 changed files with 456 additions and 364 deletions

View File

@ -18,6 +18,7 @@
<button class="pull-left" onclick="OpenLink('/')">fHDHR</a></button>
<button class="pull-left" onclick="OpenLink('/origin')">{{ fhdhr.config.dict["main"]["servicename"] }}</a></button>
<button class="pull-left" onclick="OpenLink('/channels')">Channels</a></button>
<button class="pull-left" onclick="OpenLink('/guide')">Guide</a></button>
<button class="pull-left" onclick="OpenLink('/cluster')">Cluster</a></button>
<button class="pull-left" onclick="OpenLink('/streams')">Streams</a></button>

View File

@ -0,0 +1,46 @@
{% extends "base.html" %}
{% block content %}
<h4 style="text-align: center;">What's On {{ fhdhr.config.dict["fhdhr"]["friendlyname"] }}</h4>
<table class="center" style="width:100%">
<tr>
<th>Play</th>
<th>Channel Name</th>
<th>Channel CallSign</th>
<th>Channel Number</th>
<th>Status</th>
<th>Options</th>
</tr>
{% for chan_dict in channelslist %}
<tr>
<td>
{% if chan_dict["enabled"] %}
<a href="{{ chan_dict["play_url"] }}">Play</a>
{% endif %}
</td>
<td>{{ chan_dict["name"] }}</td>
<td>{{ chan_dict["callsign"] }}</td>
<td>{{ chan_dict["number"] }}</td>
<td>
{% if chan_dict["enabled"] %}
Enabled
{% elif not chan_dict["enabled"] %}
Disabled
{% endif %}
</td>
<td>
<div>
{% if chan_dict["enabled"] %}
<button onclick="OpenLink('/api/channels?method=disable&channel={{ chan_dict["number"] }}&redirect=%2Fchannels')">Disable</a></button>
{% elif not chan_dict["enabled"] %}
<button onclick="OpenLink('/api/channels?method=enable&channel={{ chan_dict["number"] }}&redirect=%2Fchannels')">Enable</a></button>
{% endif %}
</div>
</td>
</tr>
{% endfor %}
{% endblock %}

View File

@ -4,9 +4,14 @@
<h4 style="text-align: center;">What's On {{ fhdhr.config.dict["fhdhr"]["friendlyname"] }}</h4>
<p>
{% for epg_method in epg_methods %}
<button onclick="OpenLink('/guide?source={{ epg_method }}')">{{ epg_method }}</a></button>
{% endfor %}
</p>
<table class="center" style="width:100%">
<tr>
<th>Play</th>
<th>Channel Name</th>
<th>Channel Number</th>
<th>Channel Thumbnail</th>
@ -18,7 +23,6 @@
{% for chan_dict in chan_guide_list %}
<tr>
<td><a href="{{ chan_dict["play_url"] }}">Play</a>
<td>{{ chan_dict["name"] }}</td>
<td>{{ chan_dict["number"] }}</td>
<td><img src="{{ chan_dict["chan_thumbnail"] }}" alt="{{ chan_dict["name"] }}" width="100" height="100">

View File

@ -21,8 +21,8 @@
<tr>
<td>{{ epg_method_name }}</td>
<td><a href="/api/xmltv?method=get&source="{{ epg_method }}>{{ epg_method_name }}</a></td>
<td><a href="/api/epg?method=get&source="{{ epg_method }}>{{ epg_method_name }}</a></td>
<td><a href="/api/xmltv?method=get&source={{ epg_method }}">{{ epg_method_name }}</a></td>
<td><a href="/api/epg?method=get&source={{ epg_method }}">{{ epg_method_name }}</a></td>
<td>
<div>
<button onclick="OpenLink('/api/xmltv?method=update&source={{ epg_method }}&redirect=%2Fxmltv')">Update</a></button>

View File

@ -1,157 +0,0 @@
import datetime
from collections import OrderedDict
from fHDHR.tools import hours_between_datetime
class ChannelNumbers():
def __init__(self, fhdhr):
self.fhdhr = fhdhr
def get_number(self, channel_id):
cnumbers = self.fhdhr.db.get_fhdhr_value("channel_numbers", "list") or {}
if channel_id in list(cnumbers.keys()):
return cnumbers[channel_id]
used_numbers = []
for channel_id in list(cnumbers.keys()):
used_numbers.append(cnumbers[channel_id])
for i in range(1, 1000):
if str(float(i)) not in used_numbers:
break
return str(float(i))
def set_number(self, channel_id, channel_number):
cnumbers = self.fhdhr.db.get_fhdhr_value("channel_numbers", "list") or {}
cnumbers[channel_id] = str(float(channel_number))
self.fhdhr.db.set_fhdhr_value("channel_numbers", "list", cnumbers)
class Channels():
def __init__(self, fhdhr, origin):
self.fhdhr = fhdhr
self.origin = origin
self.channel_numbers = ChannelNumbers(fhdhr)
self.list = {}
self.list_update_time = None
self.get_channels()
def get_origin_status(self):
try:
return self.origin.get_status_dict()
except AttributeError:
return {}
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:
self.fhdhr.logger.info("Found " + str(len(self.list)) + " channels for " + str(self.fhdhr.config.dict["main"]["servicename"]))
self.list_update_time = datetime.datetime.now()
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'],
'Tags': ",".join(c['tags']),
'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/auto/v%s' %
(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 "tags" not in list(station_item.keys()):
station_item["tags"] = []
if "number" not in list(station_item.keys()):
station_item["number"] = self.channel_numbers.get_number(station_item["id"])
else:
station_item["number"] = str(float(station_item["number"]))
self.channel_numbers.set_number(station_item["id"], station_item["number"])
cleaned_channel_dict_list.append(station_item)
return cleaned_channel_dict_list
def append_channel_info(self, channel_dict_list):
"""Update the list dict
Take the channel dict list given.
"""
for chan in channel_dict_list:
if chan["number"] not in list(self.list.keys()):
self.list[chan["number"]] = {}
for chankey in list(chan.keys()):
self.list[chan["number"]][chankey] = chan[chankey]
self.channel_order()
def channel_order(self):
"""Verify the Channel Order"""
self.list = OrderedDict(sorted(self.list.items()))

View File

@ -0,0 +1,76 @@
import datetime
from fHDHR.tools import hours_between_datetime
from .channel import Channel
from .chan_ident import Channel_IDs
class Channels():
def __init__(self, fhdhr, origin):
self.fhdhr = fhdhr
self.origin = origin
self.id_system = Channel_IDs(fhdhr)
self.list = {}
self.list_update_time = None
self.get_db_channels()
self.get_channels()
def get_channel_obj(self, keyfind, valfind):
return next(self.list[fhdhr_id] for fhdhr_id in list(self.list.keys()) if self.list[fhdhr_id].dict[keyfind] == valfind)
def get_channel_list(self, keyfind):
return [self.list[x].dict[keyfind] for x in list(self.list.keys())]
def set_channel_status(self, keyfind, valfind, enablement):
self.get_channel_obj(keyfind, valfind).set_status(enablement)
def get_db_channels(self):
channel_ids = self.fhdhr.db.get_fhdhr_value("channels", "IDs") or []
for channel_id in channel_ids:
channel_obj = Channel(self.fhdhr, self.id_system, channel_id=channel_id)
channel_id = channel_obj.dict["fhdhr_id"]
self.list[channel_id] = channel_obj
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()
for channel_info in channel_dict_list:
channel_obj = Channel(self.fhdhr, self.id_system, origin_id=channel_info["id"])
channel_id = channel_obj.dict["fhdhr_id"]
channel_obj.basics(channel_info)
self.list[channel_id] = channel_obj
if not self.list_update_time:
self.fhdhr.logger.info("Found " + str(len(self.list)) + " channels for " + str(self.fhdhr.config.dict["main"]["servicename"]))
self.list_update_time = datetime.datetime.now()
channel_list = []
for chan_obj in list(self.list.keys()):
channel_list.append(self.list[chan_obj].dict)
return channel_list
def get_channel_stream(self, channel_number):
return self.origin.get_channel_stream(self.get_channel_dict("number", channel_number))
def get_channel_dict(self, keyfind, valfind):
return self.get_channel_obj(keyfind, valfind).dict

View File

@ -0,0 +1,38 @@
import uuid
class Channel_IDs():
def __init__(self, fhdhr):
self.fhdhr = fhdhr
def get(self, origin_id):
existing_ids = self.fhdhr.db.get_fhdhr_value("channels", "IDs") or []
existing_channel_info = [self.fhdhr.db.get_channel_value(channel_id, "info") or {} for channel_id in existing_ids]
for existing_channel in existing_channel_info:
if existing_channel["origin_id"] == origin_id:
return existing_channel["fhdhr_id"]
return self.assign()
def assign(self):
existing_ids = self.fhdhr.db.get_fhdhr_value("channels", "IDs") or []
channel_id = None
while not channel_id:
unique_id = str(uuid.uuid4())
if str(unique_id) not in existing_ids:
channel_id = str(unique_id)
existing_ids.append(channel_id)
self.fhdhr.db.set_fhdhr_value("channels", "IDs", existing_ids)
return channel_id
def get_number(self, channel_id):
existing_ids = self.fhdhr.db.get_fhdhr_value("channels", "IDs") or []
existing_channel_info = [self.fhdhr.db.get_channel_value(channel_id, "info") or {} for channel_id in existing_ids]
cnumber = [existing_channel["number"] for existing_channel in existing_channel_info if existing_channel["fhdhr_id"] == channel_id] or None
if cnumber:
return cnumber
used_numbers = [existing_channel["number"] for existing_channel in existing_channel_info]
for i in range(1000, 2000):
if str(float(i)) not in used_numbers:
break
return str(float(i))

View File

@ -0,0 +1,92 @@
class Channel():
def __init__(self, fhdhr, id_system, origin_id=None, channel_id=None):
self.fhdhr = fhdhr
self.id_system = id_system
if not channel_id:
if origin_id:
channel_id = id_system.get(origin_id)
else:
channel_id = id_system.assign()
self.dict = self.fhdhr.db.get_channel_value(str(channel_id), "info") or self.create_empty_channel(channel_id)
self.fhdhr.db.set_channel_value(self.dict["fhdhr_id"], "info", self.dict)
def basics(self, channel_info):
"""Some Channel Information is Critical"""
if "id" in list(channel_info.keys()):
channel_info["origin_id"] = channel_info["id"]
del channel_info["id"]
if "name" not in list(channel_info.keys()):
channel_info["name"] = self.dict["fhdhr_id"]
if "callsign" not in list(channel_info.keys()):
channel_info["callsign"] = channel_info["name"]
if "origin_id" not in list(channel_info.keys()):
channel_info["origin_id"] = channel_info["name"]
if "tags" not in list(channel_info.keys()):
channel_info["tags"] = []
if "number" not in list(channel_info.keys()):
channel_info["number"] = self.id_system.get_number(channel_info["origin_id"])
channel_info["number"] = str(float(channel_info["number"]))
self.append_channel_info(channel_info)
def create_empty_channel(self, channel_id):
return {
"fhdhr_id": str(channel_id),
"origin_id": None,
"name": None,
"callsign": None,
"number": None,
"tags": [],
"enabled": True
}
def destroy(self):
self.fhdhr.db.delete_channel_value(self.dict["fhdhr_id"], "info")
channel_ids = self.fhdhr.db.get_fhdhr_value("channels", "IDs") or []
if self.dict["fhdhr_id"] in channel_ids:
channel_ids.remove(self.dict["fhdhr_id"])
self.fhdhr.db.set_fhdhr_value("channels", "IDs", channel_ids)
def append_channel_info(self, channel_info):
for chankey in list(channel_info.keys()):
self.dict[chankey] = channel_info[chankey]
self.fhdhr.db.set_channel_value(self.dict["fhdhr_id"], "info", self.dict)
def set_status(self, enablement):
if enablement == "disable":
self.dict["enabled"] = False
elif enablement == "enable":
self.dict["enabled"] = True
self.fhdhr.db.set_channel_value(self.dict["fhdhr_id"], "info", self.dict)
def lineup_dict(self):
return {
'GuideNumber': self.dict['number'],
'GuideName': self.dict['name'],
'Tags': ",".join(self.dict['tags']),
'URL': self.stream_url(),
}
def stream_url(self):
return ('/auto/v%s' % self.dict['number'])
def play_url(self):
return ('/api/m3u?method=get&channel=%s' % self.dict['number'])
def __getattr__(self, name):
''' will only get called for undefined attributes '''
if name in list(self.dict.keys()):
return self.dict[name]
else:
return None

View File

@ -26,6 +26,7 @@ class EPG():
self.epg_method_selfadd()
self.epg_methods = self.fhdhr.config.dict["epg"]["method"]
self.valid_epg_methods = [x for x in self.fhdhr.config.dict["main"]["valid_epg_methods"] if x and x not in [None, "None"]]
self.def_method = self.fhdhr.config.dict["epg"]["def_method"]
self.sleeptime = {}
for epg_method in self.epg_methods:
@ -59,8 +60,8 @@ class EPG():
self.fhdhr.db.delete_fhdhr_value("epg_dict", method)
def whats_on_now(self, channel):
epgdict = self.get_epg()
def whats_on_now(self, channel, method=None):
epgdict = self.get_epg(method)
listings = epgdict[channel]["listing"]
for listing in listings:
nowtime = datetime.datetime.utcnow()
@ -72,10 +73,19 @@ class EPG():
return epgitem
return None
def whats_on_allchans(self):
def whats_on_allchans(self, method=None):
if not method:
method = self.def_method
if (method == self.fhdhr.config.dict["main"]["dictpopname"] or
method not in self.fhdhr.config.dict["main"]["valid_epg_methods"]):
method = "origin"
channel_guide_list = []
for channel in self.channels.get_channels():
whatson = self.whats_on_now(channel["number"])
epgdict = self.get_epg(method)
channels = list(epgdict.keys())
for channel in channels:
whatson = self.whats_on_now(epgdict[channel]["number"], method)
if whatson:
channel_guide_list.append(whatson)
return channel_guide_list

View File

@ -35,7 +35,7 @@ class blocksEPG():
"callsign": c["callsign"],
"name": c["name"],
"number": c["number"],
"id": c["id"],
"id": c["origin_id"],
"thumbnail": ("/api/images?method=generate&type=channel&message=%s" % (str(c['number']))),
"listing": [],
}
@ -45,7 +45,7 @@ class blocksEPG():
"time_start": timestamp['time_start'],
"time_end": timestamp['time_end'],
"duration_minutes": 60,
"thumbnail": ("/api/images?method=generate&type=content&message=%s" % (str(c["id"]) + "_" + str(timestamp['time_start']).split(" ")[0])),
"thumbnail": ("/api/images?method=generate&type=content&message=%s" % (str(c["origin_id"]) + "_" + str(timestamp['time_start']).split(" ")[0])),
"title": "Unavailable",
"sub-title": "Unavailable",
"description": "Unavailable",
@ -56,7 +56,7 @@ class blocksEPG():
"seasonnumber": None,
"episodenumber": None,
"isnew": False,
"id": str(c["id"]) + "_" + str(timestamp['time_start']).split(" ")[0],
"id": str(c["origin_id"]) + "_" + str(timestamp['time_start']).split(" ")[0],
}
programguide[str(c["number"])]["listing"].append(clean_prog_dict)

View File

@ -1,5 +1,6 @@
from flask import request, redirect
from flask import request, redirect, Response
import urllib.parse
import json
class Channels():
@ -18,7 +19,30 @@ class Channels():
method = request.args.get('method', default=None, type=str)
redirect_url = request.args.get('redirect', default=None, type=str)
if method == "scan":
if method == "get":
channels_info = []
for fhdhr_id in list(self.fhdhr.device.channels.list.keys()):
channel_obj = self.fhdhr.device.channels.list[fhdhr_id]
channel_dict = channel_obj.dict.copy()
channel_dict["play_url"] = channel_obj.play_url()
channel_dict["stream_url"] = channel_obj.stream_url()
channels_info.append(channel_dict)
channels_info_json = json.dumps(channels_info, indent=4)
return Response(status=200,
response=channels_info_json,
mimetype='application/json')
elif method in ["enable", "disable"]:
channel = request.args.get('channel', default=None, type=str)
if not channel:
if redirect_url:
return redirect(redirect_url + "?retmessage=" + urllib.parse.quote("%s Failed" % method))
else:
return "%s Falied" % method
self.fhdhr.device.channels.set_channel_status("number", channel, method)
elif method == "scan":
self.fhdhr.device.station_scan.scan()
else:

View File

@ -19,7 +19,7 @@ class Debug_JSON():
debugjson = {
"base_url": base_url,
"total channels": self.fhdhr.device.channels.get_station_total(),
"total channels": len(self.fhdhr.device.channels.list),
"tuner status": self.fhdhr.device.tuners.status(),
}
cluster_json = json.dumps(debugjson, indent=4)

View File

@ -38,44 +38,50 @@ class M3U():
"x-tvg-url=\"" + xmltvurl + "\"")
)
channel_list = self.fhdhr.device.channels.get_channels()
channel_number_list = [x["number"] for x in channel_list]
channel_items = []
if channel == "all":
channel_items = channel_list
elif channel in channel_number_list:
channel_items = [self.fhdhr.device.channels.get_channel_dict("number", channel)]
fileName = "channels.m3u"
for fhdhr_id in list(self.fhdhr.device.channels.list.keys()):
channel_obj = self.fhdhr.device.channels.list[fhdhr_id]
if channel_obj.enabled:
channel_items.append(channel_obj)
elif channel in self.fhdhr.device.channels.get_channel_list("number"):
channel_obj = self.fhdhr.device.channels.get_channel_obj("number", channel)
fileName = str(channel_obj.number) + ".m3u"
if channel_obj.enabled:
channel_items.append(channel_obj)
else:
return "Channel Disabled"
else:
return "Invalid Channel"
for channel_item in channel_items:
for channel_obj in channel_items:
logourl = ('%s/api/images?method=get&type=channel&id=%s' %
(base_url, str(channel_item['id'])))
if self.fhdhr.config.dict["epg"]["images"] == "proxy" or not channel_obj.thumbnail:
logourl = ('%s/api/images?method=get&type=channel&id=%s' %
(base_url, str(channel_obj.dict['origin_id'])))
else:
logourl = channel_obj.thumbnail
fakefile.write(
"%s\n" % (
RECORD_MARKER + ":0" + " " +
"channelID=\"" + str(channel_item['id']) + "\" " +
"tvg-chno=\"" + str(channel_item['number']) + "\" " +
"tvg-name=\"" + str(channel_item['name']) + "\" " +
"tvg-id=\"" + str(channel_item['number']) + "\" " +
"channelID=\"" + str(channel_obj.dict['origin_id']) + "\" " +
"tvg-chno=\"" + str(channel_obj.dict['number']) + "\" " +
"tvg-name=\"" + str(channel_obj.dict['name']) + "\" " +
"tvg-id=\"" + str(channel_obj.dict['number']) + "\" " +
"tvg-logo=\"" + logourl + "\" " +
"group-title=\"" + self.fhdhr.config.dict["fhdhr"]["friendlyname"] + "\"," + str(channel_item['name']))
"group-title=\"" + self.fhdhr.config.dict["fhdhr"]["friendlyname"] + "\"," + str(channel_obj.dict['name']))
)
fakefile.write(
"%s\n" % (
('%s/auto/v%s' %
(base_url, str(channel_item['number'])))
)
)
fakefile.write("%s\n" % (base_url + channel_obj.stream_url()))
channels_m3u = fakefile.getvalue()
return Response(status=200,
response=channels_m3u,
mimetype='audio/x-mpegurl')
resp = Response(status=200, response=channels_m3u, mimetype='audio/x-mpegurl')
resp.headers["content-disposition"] = "attachment; filename=" + fileName
return resp
if redirect_url:
return redirect(redirect_url + "?retmessage=" + urllib.parse.quote("%s Success" % method))

View File

@ -18,7 +18,9 @@ class Watch():
def get(self, *args):
full_url = request.url
client_address = request.remote_addr
accessed_url = request.args.get('accessed', default=request.url, type=str)
method = request.args.get('method', default=self.fhdhr.config.dict["fhdhr"]["stream_type"], type=str)
@ -32,9 +34,17 @@ class Watch():
if not channel_number:
return "Missing Channel"
if channel_number not in list(self.fhdhr.device.channels.list.keys()):
if channel_number not in self.fhdhr.device.channels.get_channel_list("number"):
response = Response("Not Found", status=404)
response.headers["X-fHDHR-Error"] = "801 - Unknown Channel"
self.fhdhr.logger.error(response.headers["X-fHDHR-Error"])
abort(response)
channel_dict = self.fhdhr.device.channels.get_channel_dict("number", channel_number)
if not channel_dict["enabled"]:
response = Response("Service Unavailable", status=503)
response.headers["X-fHDHR-Error"] = str("806 - Tune Failed")
self.fhdhr.logger.error(response.headers["X-fHDHR-Error"])
abort(response)
duration = request.args.get('duration', default=0, type=int)
@ -44,6 +54,7 @@ class Watch():
if transcode not in valid_transcode_types:
response = Response("Service Unavailable", status=503)
response.headers["X-fHDHR-Error"] = "802 - Unknown Transcode Profile"
self.fhdhr.logger.error(response.headers["X-fHDHR-Error"])
abort(response)
stream_args = {
@ -51,7 +62,8 @@ class Watch():
"method": method,
"duration": duration,
"transcode": transcode,
"accessed": full_url,
"accessed": accessed_url,
"client": client_address
}
try:
@ -64,6 +76,7 @@ class Watch():
% (stream_args["method"], str(stream_args["channel"]), str(e)))
response = Response("Service Unavailable", status=503)
response.headers["X-fHDHR-Error"] = str(e)
self.fhdhr.logger.error(response.headers["X-fHDHR-Error"])
abort(response)
tuner = self.fhdhr.device.tuners.tuners[int(tunernum)]
@ -74,6 +87,7 @@ class Watch():
% (stream_args["method"], str(stream_args["channel"]), str(e)))
response = Response("Service Unavailable", status=503)
response.headers["X-fHDHR-Error"] = str(e)
self.fhdhr.logger.error(response.headers["X-fHDHR-Error"])
tuner.close()
abort(response)

View File

@ -16,7 +16,14 @@ class Lineup_JSON():
base_url = request.url_root[:-1]
jsonlineup = self.fhdhr.device.channels.get_station_list(base_url)
jsonlineup = []
for fhdhr_id in list(self.fhdhr.device.channels.list.keys()):
channel_obj = self.fhdhr.device.channels.list[fhdhr_id]
if channel_obj.enabled:
lineup_dict = channel_obj.lineup_dict()
lineup_dict["URL"] = base_url + lineup_dict["URL"]
jsonlineup.append(lineup_dict)
lineup_json = json.dumps(jsonlineup, indent=4)
return Response(status=200,

View File

@ -17,7 +17,7 @@ class Lineup_Status_JSON():
station_scanning = self.fhdhr.device.station_scan.scanning()
if station_scanning:
jsonlineup = self.scan_in_progress()
elif not self.fhdhr.device.channels.get_station_total():
elif not len(self.fhdhr.device.channels.list):
jsonlineup = self.scan_in_progress()
else:
jsonlineup = self.not_scanning()
@ -28,11 +28,10 @@ class Lineup_Status_JSON():
mimetype='application/json')
def scan_in_progress(self):
channel_count = self.fhdhr.device.channels.get_station_total()
jsonlineup = {
"ScanInProgress": "true",
"Progress": 99,
"Found": channel_count
"Found": len(self.fhdhr.device.channels.list)
}
return jsonlineup

View File

@ -20,13 +20,16 @@ class Lineup_XML():
base_url = request.url_root[:-1]
out = xml.etree.ElementTree.Element('Lineup')
station_list = self.fhdhr.device.channels.get_station_list(base_url)
for station_item in station_list:
program_out = sub_el(out, 'Program')
sub_el(program_out, 'GuideNumber', station_item['GuideNumber'])
sub_el(program_out, 'GuideName', station_item['GuideName'])
sub_el(program_out, 'Tags', ",".join(station_item['Tags']))
sub_el(program_out, 'URL', station_item['URL'])
for fhdhr_id in list(self.fhdhr.device.channels.list.keys()):
channel_obj = self.fhdhr.device.channels.list[fhdhr_id]
if channel_obj.enabled:
lineup_dict = channel_obj.lineup_dict()
lineup_dict["URL"] = base_url + lineup_dict["URL"]
program_out = sub_el(out, 'Program')
sub_el(program_out, 'GuideNumber', lineup_dict['GuideNumber'])
sub_el(program_out, 'GuideName', lineup_dict['GuideName'])
sub_el(program_out, 'Tags', lineup_dict['Tags'])
sub_el(program_out, 'URL', lineup_dict['URL'])
fakefile = BytesIO()
fakefile.write(b'<?xml version="1.0" encoding="UTF-8"?>\n')

View File

@ -2,6 +2,7 @@
from .index_html import Index_HTML
from .origin_html import Origin_HTML
from .channels_html import Channels_HTML
from .guide_html import Guide_HTML
from .cluster_html import Cluster_HTML
from .streams_html import Streams_HTML
@ -18,6 +19,7 @@ class fHDHR_Pages():
self.index_html = Index_HTML(fhdhr)
self.origin_html = Origin_HTML(fhdhr)
self.channels_html = Channels_HTML(fhdhr)
self.guide_html = Guide_HTML(fhdhr)
self.cluster_html = Cluster_HTML(fhdhr)
self.streams_html = Streams_HTML(fhdhr)

View File

@ -0,0 +1,23 @@
from flask import request, render_template
class Channels_HTML():
endpoints = ["/channels", "/channels.html"]
endpoint_name = "channels_html"
def __init__(self, fhdhr):
self.fhdhr = fhdhr
def __call__(self, *args):
return self.get(*args)
def get(self, *args):
channelslist = []
for fhdhr_id in list(self.fhdhr.device.channels.list.keys()):
channel_obj = self.fhdhr.device.channels.list[fhdhr_id]
channel_dict = channel_obj.dict.copy()
channel_dict["play_url"] = channel_obj.play_url()
channelslist.append(channel_dict)
return render_template('channels.html', request=request, fhdhr=self.fhdhr, channelslist=channelslist)

View File

@ -4,7 +4,7 @@ import urllib.parse
class Cluster_HTML():
endpoints = ["/cluster", "/cluster.html"]
endpoint_name = "cluster"
endpoint_name = "cluster_html"
def __init__(self, fhdhr):
self.fhdhr = fhdhr

View File

@ -3,7 +3,7 @@ from flask import request, render_template
class Diagnostics_HTML():
endpoints = ["/diagnostics", "/diagnostics.html"]
endpoint_name = "diagnostics"
endpoint_name = "diagnostics_html"
def __init__(self, fhdhr):
self.fhdhr = fhdhr

View File

@ -6,7 +6,7 @@ from fHDHR.tools import humanized_time
class Guide_HTML():
endpoints = ["/guide", "/guide.html"]
endpoint_name = "guide"
endpoint_name = "guide_html"
def __init__(self, fhdhr):
self.fhdhr = fhdhr
@ -20,13 +20,16 @@ class Guide_HTML():
chan_guide_list = []
for channel in self.fhdhr.device.epg.whats_on_allchans():
source = request.args.get('source', default=self.fhdhr.device.epg.def_method, type=str)
epg_methods = self.fhdhr.device.epg.valid_epg_methods
if source not in epg_methods:
source = self.fhdhr.device.epg.def_method
for channel in self.fhdhr.device.epg.whats_on_allchans(source):
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 = ("/api/m3u?method=get&channel=%s\n" % (channel["number"]))
chan_dict = {
"play_url": play_url,
"name": channel["name"],
"number": channel["number"],
"chan_thumbnail": channel["thumbnail"],
@ -37,4 +40,4 @@ class Guide_HTML():
}
chan_guide_list.append(chan_dict)
return render_template('guide.html', request=request, fhdhr=self.fhdhr, chan_guide_list=chan_guide_list)
return render_template('guide.html', request=request, fhdhr=self.fhdhr, chan_guide_list=chan_guide_list, epg_methods=epg_methods)

View File

@ -3,7 +3,7 @@ from flask import request, render_template
class Index_HTML():
endpoints = ["/", "/index", "/index.html"]
endpoint_name = "root"
endpoint_name = "root_html"
def __init__(self, fhdhr):
self.fhdhr = fhdhr
@ -20,7 +20,7 @@ class Index_HTML():
"Script Directory": str(self.fhdhr.config.internal["paths"]["script_dir"]),
"Config File": str(self.fhdhr.config.config_file),
"Cache Path": str(self.fhdhr.config.internal["paths"]["cache_dir"]),
"Total Channels": str(self.fhdhr.device.channels.get_station_total()),
"Total Channels": len(self.fhdhr.device.channels.list),
"Tuner Usage": ("%s/%s" % (str(tuners_in_use), str(max_tuners))),
}

View File

@ -3,7 +3,7 @@ from flask import request, render_template
class Origin_HTML():
endpoints = ["/origin", "/origin.html"]
endpoint_name = "origin"
endpoint_name = "origin_html"
def __init__(self, fhdhr):
self.fhdhr = fhdhr
@ -13,6 +13,6 @@ class Origin_HTML():
def get(self, *args):
origin_status_dict = self.fhdhr.device.channels.get_origin_status()
origin_status_dict["Total Channels"] = str(self.fhdhr.device.channels.get_station_total())
origin_status_dict = self.fhdhr.origin.get_status_dict()
origin_status_dict["Total Channels"] = len(self.fhdhr.device.channels.list)
return render_template('origin.html', request=request, fhdhr=self.fhdhr, origin_status_dict=origin_status_dict, list=list)

View File

@ -3,7 +3,7 @@ from flask import request, render_template
class Settings_HTML():
endpoints = ["/settings", "/settings.html"]
endpoint_name = "settings"
endpoint_name = "settings_html"
def __init__(self, fhdhr):
self.fhdhr = fhdhr

View File

@ -3,7 +3,7 @@ from flask import request, render_template
class Streams_HTML():
endpoints = ["/streams", "/streams.html"]
endpoint_name = "streams"
endpoint_name = "streams_html"
def __init__(self, fhdhr):
self.fhdhr = fhdhr

View File

@ -3,7 +3,7 @@ from flask import request, render_template
class Version_HTML():
endpoints = ["/version", "/version.html"]
endpoint_name = "version"
endpoint_name = "version_html"
def __init__(self, fhdhr):
self.fhdhr = fhdhr

View File

@ -3,7 +3,7 @@ from flask import request, render_template
class xmlTV_HTML():
endpoints = ["/xmltv", "/xmltv.html"]
endpoint_name = "xmltv"
endpoint_name = "xmltv_html"
def __init__(self, fhdhr):
self.fhdhr = fhdhr

View File

@ -1,6 +1,5 @@
from flask import Response, request, stream_with_context, abort
from fHDHR.exceptions import TunerError
from flask import request, abort, redirect
import urllib.parse
class Auto():
@ -15,7 +14,9 @@ class Auto():
def get(self, channel, *args):
full_url = request.url
method = request.args.get('method', default=self.fhdhr.config.dict["fhdhr"]["stream_type"], type=str)
redirect_url = "/api/watch?method=%s" % (method)
if channel.startswith("v"):
channel_number = channel.replace('v', '')
@ -24,70 +25,21 @@ class Auto():
subchannel = 0
if "-" in channel:
subchannel = channel.replace('ch', '').split("-")[1]
self.fhdhr.logger.error("Not Implemented %s-%s" % (str(channel_freq), str(subchannel)))
abort(501, "Not Implemented %s-%s" % (str(channel_freq), str(subchannel)))
else:
channel_number = channel
if channel_number not in list(self.fhdhr.device.channels.list.keys()):
response = Response("Not Found", status=404)
response.headers["X-fHDHR-Error"] = "801 - Unknown Channel"
abort(response)
redirect_url += "&channel=%s" % str(channel_number)
method = request.args.get('method', default=self.fhdhr.config.dict["fhdhr"]["stream_type"], type=str)
duration = request.args.get('duration', default=0, type=int)
if duration:
redirect_url += "&duration=%s" % str(duration)
transcode = request.args.get('transcode', default=None, type=str)
valid_transcode_types = [None, "heavy", "mobile", "internet720", "internet480", "internet360", "internet240"]
if transcode not in valid_transcode_types:
response = Response("Service Unavailable", status=503)
response.headers["X-fHDHR-Error"] = "802 - Unknown Transcode Profile"
abort(response)
if transcode:
redirect_url += "&transcode=%s" % str(transcode)
stream_args = {
"channel": channel_number,
"method": method,
"duration": duration,
"transcode": transcode,
"accessed": full_url,
}
redirect_url += "&accessed=%s" % urllib.parse.quote(request.url)
try:
tunernum = self.fhdhr.device.tuners.first_available()
except TunerError as e:
self.fhdhr.logger.info("A %s stream request for channel %s was rejected due to %s"
% (stream_args["method"], str(stream_args["channel"]), str(e)))
response = Response("Service Unavailable", status=503)
response.headers["X-fHDHR-Error"] = str(e)
abort(response)
tuner = self.fhdhr.device.tuners.tuners[int(tunernum)]
try:
stream_args = self.fhdhr.device.tuners.get_stream_info(stream_args)
except TunerError as e:
self.fhdhr.logger.info("A %s stream request for channel %s was rejected due to %s"
% (stream_args["method"], str(stream_args["channel"]), str(e)))
response = Response("Service Unavailable", status=503)
response.headers["X-fHDHR-Error"] = str(e)
tuner.close()
abort(response)
self.fhdhr.logger.info("Tuner #" + str(tunernum) + " to be used for stream.")
tuner.set_status(stream_args)
if stream_args["method"] == "direct":
return Response(tuner.get_stream(stream_args, tuner), content_type=stream_args["content_type"], direct_passthrough=True)
elif stream_args["method"] in ["ffmpeg", "vlc"]:
return Response(stream_with_context(tuner.get_stream(stream_args, tuner)), mimetype=stream_args["content_type"])
"""
try:
if stream_args["method"] == "direct":
return Response(tuner.get_stream(stream_args, tuner), content_type=stream_args["content_type"], direct_passthrough=True)
elif stream_args["method"] in ["ffmpeg", "vlc"]:
return Response(stream_with_context(tuner.get_stream(stream_args, tuner)), mimetype=stream_args["content_type"])
except TunerError as e:
tuner.close()
self.fhdhr.logger.info("A %s stream request for channel %s failed due to %s"
% (stream_args["method"], str(stream_args["channel"]), str(e)))
response = Response("Service Unavailable", status=503)
response.headers["X-fHDHR-Error"] = str(e)
abort(response)
"""
return redirect(redirect_url)

View File

@ -1,6 +1,5 @@
from flask import Response, request, stream_with_context, abort
from fHDHR.exceptions import TunerError
from flask import request, abort, redirect
import urllib.parse
class Tuner():
@ -15,7 +14,11 @@ class Tuner():
def get(self, tuner_number, channel, *args):
full_url = request.url
method = request.args.get('method', default=self.fhdhr.config.dict["fhdhr"]["stream_type"], type=str)
redirect_url = "/api/watch?method=%s" % (method)
redirect_url += "&tuner=%s" % str(tuner_number)
if channel.startswith("v"):
channel_number = channel.replace('v', '')
@ -24,70 +27,21 @@ class Tuner():
subchannel = 0
if "-" in channel:
subchannel = channel.replace('ch', '').split("-")[1]
self.fhdhr.logger.error("Not Implemented %s-%s" % (str(channel_freq), str(subchannel)))
abort(501, "Not Implemented %s-%s" % (str(channel_freq), str(subchannel)))
else:
channel_number = channel
if channel_number not in list(self.fhdhr.device.channels.list.keys()):
response = Response("Not Found", status=404)
response.headers["X-fHDHR-Error"] = "801 - Unknown Channel"
abort(response)
redirect_url += "&channel=%s" % str(channel_number)
method = request.args.get('method', default=self.fhdhr.config.dict["fhdhr"]["stream_type"], type=str)
duration = request.args.get('duration', default=0, type=int)
if duration:
redirect_url += "&duration=%s" % str(duration)
transcode = request.args.get('transcode', default=None, type=str)
valid_transcode_types = [None, "heavy", "mobile", "internet720", "internet480", "internet360", "internet240"]
if transcode not in valid_transcode_types:
response = Response("Service Unavailable", status=503)
response.headers["X-fHDHR-Error"] = "802 - Unknown Transcode Profile"
abort(response)
if transcode:
redirect_url += "&transcode=%s" % str(transcode)
stream_args = {
"channel": channel_number,
"method": method,
"duration": duration,
"transcode": transcode,
"accessed": full_url,
}
redirect_url += "&accessed=%s" % urllib.parse.quote(request.url)
try:
tunernum = self.fhdhr.device.tuners.tuner_grab(tuner_number)
except TunerError as e:
self.fhdhr.logger.info("A %s stream request for channel %s was rejected due to %s"
% (stream_args["method"], str(stream_args["channel"]), str(e)))
response = Response("Service Unavailable", status=503)
response.headers["X-fHDHR-Error"] = str(e)
abort(response)
tuner = self.fhdhr.device.tuners.tuners[int(tunernum)]
try:
stream_args = self.fhdhr.device.tuners.get_stream_info(stream_args)
except TunerError as e:
self.fhdhr.logger.info("A %s stream request for channel %s was rejected due to %s"
% (stream_args["method"], str(stream_args["channel"]), str(e)))
response = Response("Service Unavailable", status=503)
response.headers["X-fHDHR-Error"] = str(e)
tuner.close()
abort(response)
self.fhdhr.logger.info("Tuner #" + str(tunernum) + " to be used for stream.")
tuner.set_status(stream_args)
if stream_args["method"] == "direct":
return Response(tuner.get_stream(stream_args, tuner), content_type=stream_args["content_type"], direct_passthrough=True)
elif stream_args["method"] in ["ffmpeg", "vlc"]:
return Response(stream_with_context(tuner.get_stream(stream_args, tuner)), mimetype=stream_args["content_type"])
"""
try:
if stream_args["method"] == "direct":
return Response(tuner.get_stream(stream_args, tuner), content_type=stream_args["content_type"], direct_passthrough=True)
elif stream_args["method"] in ["ffmpeg", "vlc"]:
return Response(stream_with_context(tuner.get_stream(stream_args, tuner)), mimetype=stream_args["content_type"])
except TunerError as e:
tuner.close()
self.fhdhr.logger.info("A %s stream request for channel %s failed due to %s"
% (stream_args["method"], str(stream_args["channel"]), str(e)))
response = Response("Service Unavailable", status=503)
response.headers["X-fHDHR-Error"] = str(e)
abort(response)
"""
return redirect(redirect_url)

View File

@ -20,8 +20,8 @@ class OriginChannels_StandIN():
def get_channels(self):
return []
def get_channel_stream(self, chandict, allchandict):
return [{"number": chandict["number"], "stream_url": None}], False
def get_channel_stream(self, chandict):
return None
class OriginServiceWrapper():
@ -54,8 +54,8 @@ class OriginServiceWrapper():
def get_channels(self):
return self.channels.get_channels()
def get_channel_stream(self, chandict, allchandict):
return self.channels.get_channel_stream(chandict, allchandict)
def get_channel_stream(self, chandict):
return self.channels.get_channel_stream(chandict)
def update_epg(self, channels):
return self.epg.update_epg(channels)

View File

@ -40,10 +40,7 @@ class OriginChannels():
channel_list.append(clean_station_item)
return channel_list
def get_channel_stream(self, chandict, allchandict):
caching = True
streamlist = []
streamdict = {}
def get_channel_stream(self, chandict):
streamurl = ('%s%s:%s/live?channel=%s&client=%s' %
("https://" if self.fhdhr.config.dict["origin"]["ssl"] else "http://",
self.fhdhr.config.dict["origin"]["address"],
@ -51,6 +48,4 @@ class OriginChannels():
str(chandict["number"]),
str(chandict["number"]),
))
streamdict = {"number": chandict["number"], "stream_url": streamurl}
streamlist.append(streamdict)
return streamlist, caching
return streamurl

View File

@ -49,8 +49,8 @@ class OriginEPG():
"callsign": cdict["callsign"],
"name": cdict["name"] or cdict["callsign"],
"number": cdict["number"],
"id": str(cdict["id"]),
"thumbnail": self.get_channel_thumbnail(cdict['id']),
"id": str(cdict["origin_id"]),
"thumbnail": self.get_channel_thumbnail(cdict['origin_id']),
"listing": [],
}
@ -58,7 +58,7 @@ class OriginEPG():
("https://" if self.fhdhr.config.dict["origin"]["ssl"] else "http://",
self.fhdhr.config.dict["origin"]["address"],
str(self.fhdhr.config.dict["origin"]["port"]),
str(cdict["id"]),
str(cdict["origin_id"]),
))
epg_req = self.fhdhr.web.session.get(epg_url)
epg_dict = xmltodict.parse(epg_req.content)