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

Add More Functionality

This commit is contained in:
deathbybandaid 2020-10-12 08:15:35 -04:00
parent a5d1bfc09f
commit 1d6c6d9354
4 changed files with 122 additions and 23 deletions

View File

@ -22,6 +22,7 @@ class HDHR_Hub():
self.watch = fHDHRdevice.WatchStream(settings, origserv, self.tuners) self.watch = fHDHRdevice.WatchStream(settings, origserv, self.tuners)
self.station_scan = fHDHRdevice.Station_Scan(settings, origserv) self.station_scan = fHDHRdevice.Station_Scan(settings, origserv)
self.xmltv = fHDHRdevice.xmlTV_XML(settings, epghandling) self.xmltv = fHDHRdevice.xmlTV_XML(settings, epghandling)
self.m3u = fHDHRdevice.channels_M3U(settings, origserv)
self.htmlerror = fHDHRdevice.HTMLerror(settings) self.htmlerror = fHDHRdevice.HTMLerror(settings)
self.debug = fHDHRdevice.Debug_JSON(settings, origserv, epghandling) self.debug = fHDHRdevice.Debug_JSON(settings, origserv, epghandling)
@ -65,11 +66,14 @@ class HDHR_Hub():
def get_image(self, request_args): def get_image(self, request_args):
return self.images.get_image(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, request_args): def get_stream_info(self, request_args):
return self.watch.get_stream_info(request_args) return self.watch.get_stream_info(request_args)
def get_stream(self, channel_id, method, channelUri, content_type): def get_stream(self, channel_id, method, channelUri, content_type, duration):
return self.watch.get_stream(channel_id, method, channelUri, content_type) return self.watch.get_stream(channel_id, method, channelUri, content_type, duration)
hdhr = HDHR_Hub() hdhr = HDHR_Hub()
@ -137,8 +141,8 @@ class HDHR_HTTP_Server():
@app.route('/api/xmltv') @app.route('/api/xmltv')
def api_xmltv(): def api_xmltv():
if 'DeviceAuth' in list(request.args.keys()): DeviceAuth = request.args.get('DeviceAuth', default=None, type=str)
if request.args['DeviceAuth'] == hdhr.config.dict["dev"]["device_auth"]: if DeviceAuth == hdhr.config.dict["dev"]["device_auth"]:
base_url = request.headers["host"] base_url = request.headers["host"]
xmltv = hdhr.get_xmltv(base_url) xmltv = hdhr.get_xmltv(base_url)
return Response(status=200, return Response(status=200,
@ -154,6 +158,15 @@ class HDHR_HTTP_Server():
response=debugreport, response=debugreport,
mimetype='application/json') mimetype='application/json')
@app.route('/api/channels.m3u')
@app.route('/channels.m3u', methods=['GET'])
def channels_m3u():
base_url = request.headers["host"]
channels_m3u = hdhr.get_channels_m3u(base_url)
return Response(status=200,
response=channels_m3u,
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 = hdhr.get_image(request.args)
@ -163,15 +176,16 @@ class HDHR_HTTP_Server():
def auto(channel): def auto(channel):
request_args = { request_args = {
"channel": channel.replace('v', ''), "channel": channel.replace('v', ''),
"method": hdhr.config.dict["fhdhr"]["stream_type"] "method": hdhr.config.dict["fhdhr"]["stream_type"],
"duration": request.args.get('duration', default=0, type=int),
} }
channel_id = request_args["channel"] channel_id = request_args["channel"]
method, channelUri, content_type = hdhr.get_stream_info(request_args) method, channelUri, content_type, duration = hdhr.get_stream_info(request_args)
if channelUri: if channelUri:
if method == "direct": if method == "direct":
return Response(hdhr.get_stream(channel_id, method, channelUri, content_type), content_type=content_type, direct_passthrough=True) return Response(hdhr.get_stream(channel_id, method, channelUri, content_type, duration), content_type=content_type, direct_passthrough=True)
elif method == "ffmpeg": elif method == "ffmpeg":
return Response(stream_with_context(hdhr.get_stream(channel_id, method, channelUri, content_type)), mimetype="video/mpeg") return Response(stream_with_context(hdhr.get_stream(channel_id, method, channelUri, content_type, duration)), mimetype="video/mpeg")
abort(503) abort(503)
@app.route('/watch', methods=['GET']) @app.route('/watch', methods=['GET'])
@ -179,12 +193,12 @@ class HDHR_HTTP_Server():
if 'method' in list(request.args.keys()) and 'channel' in list(request.args.keys()): if 'method' in list(request.args.keys()) and 'channel' in list(request.args.keys()):
channel_id = str(request.args["channel"]) channel_id = str(request.args["channel"])
method = str(request.args["method"]) method = str(request.args["method"])
method, channelUri, content_type = hdhr.get_stream_info(request.args) method, channelUri, content_type, duration = hdhr.get_stream_info(request.args)
if channelUri: if channelUri:
if method == "direct": if method == "direct":
return Response(hdhr.get_stream(channel_id, method, channelUri, content_type), content_type=content_type, direct_passthrough=True) return Response(hdhr.get_stream(channel_id, method, channelUri, content_type, duration), content_type=content_type, direct_passthrough=True)
elif method == "ffmpeg": elif method == "ffmpeg":
return Response(stream_with_context(hdhr.get_stream(channel_id, method, channelUri, content_type)), mimetype="video/mpeg") return Response(stream_with_context(hdhr.get_stream(channel_id, method, channelUri, content_type, duration)), mimetype="video/mpeg")
abort(503) abort(503)
@app.route('/lineup.post', methods=['POST']) @app.route('/lineup.post', methods=['POST'])

View File

@ -11,5 +11,6 @@ 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 .htmlerror import HTMLerror from .htmlerror import HTMLerror

View File

@ -0,0 +1,56 @@
from io import StringIO
class channels_M3U():
def __init__(self, settings, origserv):
self.config = settings
self.origserv = origserv
def get_channels_m3u(self, base_url):
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 + "\"")
)
for channel in self.origserv.get_channels():
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/watch?method=%s&channel=%s' %
("http://",
base_url,
self.config.dict["fhdhr"]["stream_type"],
str(channel['number'])))
)
)
return fakefile.getvalue()

View File

@ -1,4 +1,5 @@
import subprocess import subprocess
import time
from fHDHR.fHDHRerrors import TunerError from fHDHR.fHDHRerrors import TunerError
import fHDHR.tools import fHDHR.tools
@ -12,15 +13,26 @@ class WatchStream():
self.tuners = tuners self.tuners = tuners
self.web = fHDHR.tools.WebReq() self.web = fHDHR.tools.WebReq()
def direct_stream(self, channel_id, method, channelUri, content_type): def direct_stream(self, channel_id, method, channelUri, content_type, duration):
chunksize = int(self.tuners.config.dict["direct_stream"]['chunksize']) chunksize = int(self.tuners.config.dict["direct_stream"]['chunksize'])
if not duration == 0:
duration += time.time()
req = self.web.session.get(channelUri, stream=True) req = self.web.session.get(channelUri, stream=True)
def generate(): def generate():
try: try:
for chunk in req.iter_content(chunk_size=chunksize): for chunk in req.iter_content(chunk_size=chunksize):
if not duration == 0 and not time.time() < duration:
req.close()
print("Requested Duration Expired.")
break
yield chunk yield chunk
except GeneratorExit: except GeneratorExit:
req.close() req.close()
print("Connection Closed.") print("Connection Closed.")
@ -28,7 +40,8 @@ class WatchStream():
return generate() return generate()
def ffmpeg_stream(self, channel_id, method, channelUri, content_type): def ffmpeg_stream(self, channel_id, method, channelUri, content_type, duration):
bytes_per_read = int(self.config.dict["ffmpeg"]["bytes_per_read"]) bytes_per_read = int(self.config.dict["ffmpeg"]["bytes_per_read"])
ffmpeg_command = [self.config.dict["ffmpeg"]["ffmpeg_path"], ffmpeg_command = [self.config.dict["ffmpeg"]["ffmpeg_path"],
@ -40,28 +53,42 @@ class WatchStream():
"pipe:stdout" "pipe:stdout"
] ]
if not duration == 0:
duration += time.time()
ffmpeg_proc = subprocess.Popen(ffmpeg_command, stdout=subprocess.PIPE) ffmpeg_proc = subprocess.Popen(ffmpeg_command, stdout=subprocess.PIPE)
def generate(): def generate():
try: try:
while True: while True:
if not duration == 0 and not time.time() < duration:
ffmpeg_proc.terminate()
ffmpeg_proc.communicate()
print("Requested Duration Expired.")
break
videoData = ffmpeg_proc.stdout.read(bytes_per_read) videoData = ffmpeg_proc.stdout.read(bytes_per_read)
if not videoData: if not videoData:
break break
try: try:
yield videoData yield videoData
except Exception as e: except Exception as e:
ffmpeg_proc.terminate() ffmpeg_proc.terminate()
ffmpeg_proc.communicate() ffmpeg_proc.communicate()
print("Connection Closed: " + str(e)) print("Connection Closed: " + str(e))
except GeneratorExit: except GeneratorExit:
ffmpeg_proc.terminate() ffmpeg_proc.terminate()
ffmpeg_proc.communicate() ffmpeg_proc.communicate()
print("Connection Closed.") print("Connection Closed.")
self.tuners.tuner_close() self.tuners.tuner_close()
return generate() return generate()
def get_stream(self, channel_id, method, channelUri, content_type): def get_stream(self, channel_id, method, channelUri, content_type, duration):
try: try:
self.tuners.tuner_grab() self.tuners.tuner_grab()
@ -73,20 +100,21 @@ class WatchStream():
print("Attempting a " + method + " stream request for channel " + str(channel_id)) print("Attempting a " + method + " stream request for channel " + str(channel_id))
if method == "ffmpeg": if method == "ffmpeg":
return self.ffmpeg_stream(channel_id, method, channelUri, content_type) return self.ffmpeg_stream(channel_id, method, channelUri, content_type, duration)
elif method == "direct": elif method == "direct":
return self.direct_stream(channel_id, method, channelUri, content_type) return self.direct_stream(channel_id, method, channelUri, content_type, duration)
def get_stream_info(self, request_args): def get_stream_info(self, request_args):
method = str(request_args["method"]) method = str(request_args["method"])
channel_id = str(request_args["channel"]) channel_id = str(request_args["channel"])
duration = int(request_args["duration"])
channelUri = self.origserv.get_channel_stream(channel_id) channelUri = self.origserv.get_channel_stream(channel_id)
if not channelUri: if not channelUri:
return None, None, None return None, None, None, None
channelUri_headers = self.web.session.head(channelUri).headers channelUri_headers = self.web.session.head(channelUri).headers
content_type = channelUri_headers['Content-Type'] content_type = channelUri_headers['Content-Type']
return method, channelUri, content_type return method, channelUri, content_type, duration