diff --git a/fHDHR/fHDHRweb/__init__.py b/fHDHR/fHDHRweb/__init__.py index c7048ee..4ff735d 100644 --- a/fHDHR/fHDHRweb/__init__.py +++ b/fHDHR/fHDHRweb/__init__.py @@ -1,11 +1,7 @@ from gevent.pywsgi import WSGIServer -from flask import (Flask, send_from_directory, request, Response, - abort, stream_with_context) -import requests -import subprocess +from flask import Flask, send_from_directory, request, abort, Response, stream_with_context from . import fHDHRdevice -from fHDHR.fHDHRerrors import TunerError class HDHR_Hub(): @@ -23,6 +19,7 @@ class HDHR_Hub(): 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.htmlerror = fHDHRdevice.HTMLerror(settings) @@ -68,6 +65,9 @@ class HDHR_Hub(): def get_image(self, request_args): return self.images.get_image(request_args) + def get_stream(self, request_args): + return self.watch.get_stream(request_args) + hdhr = HDHR_Hub() @@ -147,74 +147,10 @@ class HDHR_HTTP_Server(): @app.route('/watch', methods=['GET']) def watch(): - if 'method' in list(request.args.keys()) and 'channel' in list(request.args.keys()): - - method = str(request.args["method"]) - channel_id = str(request.args["channel"]) - - try: - hdhr.tuner_grab() - except TunerError: - print("A " + method + " stream request for channel " + - str(channel_id) + " was rejected do to a lack of available tuners.") - abort(503) - - print("Attempting a " + method + " stream request for channel " + str(channel_id)) - - channelUri = hdhr.origserv.get_channel_stream(channel_id) - # print("Proxy URL determined as " + str(channelUri)) - - if method == "direct": - chunksize = int(hdhr.config.dict["direct_stream"]['chunksize']) - - req = requests.get(channelUri, stream=True) - - def generate(): - try: - for chunk in req.iter_content(chunk_size=chunksize): - yield chunk - except GeneratorExit: - req.close() - print("Connection Closed.") - hdhr.tuner_close() - - return Response(generate(), content_type=req.headers['content-type'], direct_passthrough=True) - - elif method == "ffmpeg": - - bytes_per_read = int(hdhr.config.dict["ffmpeg"]["bytes_per_read"]) - - ffmpeg_command = [hdhr.config.dict["ffmpeg"]["ffmpeg_path"], - "-i", channelUri, - "-c", "copy", - "-f", "mpegts", - "-nostats", "-hide_banner", - "-loglevel", "warning", - "pipe:stdout" - ] - - ffmpeg_proc = subprocess.Popen(ffmpeg_command, stdout=subprocess.PIPE) - - def generate(): - try: - while True: - videoData = ffmpeg_proc.stdout.read(bytes_per_read) - if not videoData: - break - try: - yield videoData - except Exception as e: - ffmpeg_proc.terminate() - ffmpeg_proc.communicate() - print("Connection Closed: " + str(e)) - except GeneratorExit: - ffmpeg_proc.terminate() - ffmpeg_proc.communicate() - print("Connection Closed.") - hdhr.tuner_close() - - return Response(stream_with_context(generate()), mimetype="audio/mpeg") + return Response(stream_with_context(hdhr.get_stream(request.args))) + else: + abort(503) @app.route('/lineup.post', methods=['POST']) def lineup_post(): diff --git a/fHDHR/fHDHRweb/fHDHRdevice/__init__.py b/fHDHR/fHDHRweb/fHDHRdevice/__init__.py index 5045fe9..fa77db4 100644 --- a/fHDHR/fHDHRweb/fHDHRdevice/__init__.py +++ b/fHDHR/fHDHRweb/fHDHRdevice/__init__.py @@ -1,6 +1,7 @@ # 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 diff --git a/fHDHR/fHDHRweb/fHDHRdevice/watch.py b/fHDHR/fHDHRweb/fHDHRdevice/watch.py new file mode 100644 index 0000000..7ad66f7 --- /dev/null +++ b/fHDHR/fHDHRweb/fHDHRdevice/watch.py @@ -0,0 +1,84 @@ +import subprocess + +from fHDHR.fHDHRerrors import TunerError +import fHDHR.tools + + +class WatchStream(): + + def __init__(self, settings, origserv, tuners): + self.config = settings + self.origserv = origserv + self.tuners = tuners + self.web = fHDHR.tools.WebReq() + + def direct_stream(self, channelUri): + chunksize = int(self.tuners.config.dict["direct_stream"]['chunksize']) + + req = self.web.session.get(channelUri, stream=True) + + def generate(): + try: + for chunk in req.iter_content(chunk_size=chunksize): + yield chunk + except GeneratorExit: + req.close() + print("Connection Closed.") + self.tuners.tuner_close() + + return generate() + + def ffmpeg_stream(self, channelUri): + bytes_per_read = int(self.config.dict["ffmpeg"]["bytes_per_read"]) + + ffmpeg_command = [self.config.dict["ffmpeg"]["ffmpeg_path"], + "-i", channelUri, + "-c", "copy", + "-f", "mpegts", + "-nostats", "-hide_banner", + "-loglevel", "warning", + "pipe:stdout" + ] + + ffmpeg_proc = subprocess.Popen(ffmpeg_command, stdout=subprocess.PIPE) + + def generate(): + try: + while True: + videoData = ffmpeg_proc.stdout.read(bytes_per_read) + if not videoData: + break + try: + yield videoData + except Exception as e: + ffmpeg_proc.terminate() + ffmpeg_proc.communicate() + print("Connection Closed: " + str(e)) + except GeneratorExit: + ffmpeg_proc.terminate() + ffmpeg_proc.communicate() + print("Connection Closed.") + self.tuners.tuner_close() + return generate() + + def get_stream(self, request_args): + + method = str(request_args["method"]) + channel_id = str(request_args["channel"]) + + try: + self.tuners.tuner_grab() + except TunerError: + print("A " + method + " stream request for channel " + + str(channel_id) + " was rejected do to a lack of available tuners.") + return + + print("Attempting a " + method + " stream request for channel " + str(channel_id)) + + channelUri = self.origserv.get_channel_stream(channel_id) + # print("Proxy URL determined as " + str(channelUri)) + + if method == "ffmpeg": + return self.ffmpeg_stream(channelUri) + elif method == "direct": + return self.direct_stream(channelUri) diff --git a/requirements.txt b/requirements.txt index 055bb2c..62a5b49 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,3 +3,4 @@ gevent flask image xmltodict +m3u8