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:
parent
a5d1bfc09f
commit
1d6c6d9354
@ -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'])
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
56
fHDHR/fHDHRweb/fHDHRdevice/channels_m3u.py
Normal file
56
fHDHR/fHDHRweb/fHDHRdevice/channels_m3u.py
Normal 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()
|
||||||
@ -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
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user