mirror of
https://github.com/fHDHR/fHDHR_NextPVR.git
synced 2025-12-06 15:16:58 -05:00
Streaming Enhancements
This commit is contained in:
parent
90fb90a92e
commit
cf64aecf7b
@ -1,14 +1,21 @@
|
|||||||
{
|
{
|
||||||
|
"streaming":{
|
||||||
|
"bytes_per_read": {
|
||||||
|
"value": 1152000,
|
||||||
|
"config_file": true,
|
||||||
|
"config_web": true
|
||||||
|
},
|
||||||
|
"quality": {
|
||||||
|
"value": "none",
|
||||||
|
"config_file": true,
|
||||||
|
"config_web": true
|
||||||
|
}
|
||||||
|
},
|
||||||
"ffmpeg":{
|
"ffmpeg":{
|
||||||
"path":{
|
"path":{
|
||||||
"value": "ffmpeg",
|
"value": "ffmpeg",
|
||||||
"config_file": true,
|
"config_file": true,
|
||||||
"config_web": true
|
"config_web": true
|
||||||
},
|
|
||||||
"bytes_per_read":{
|
|
||||||
"value": 1152000,
|
|
||||||
"config_file": true,
|
|
||||||
"config_web": true
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"vlc":{
|
"vlc":{
|
||||||
@ -16,18 +23,6 @@
|
|||||||
"value": "cvlc",
|
"value": "cvlc",
|
||||||
"config_file": true,
|
"config_file": true,
|
||||||
"config_web": true
|
"config_web": true
|
||||||
},
|
|
||||||
"bytes_per_read":{
|
|
||||||
"value": 1152000,
|
|
||||||
"config_file": true,
|
|
||||||
"config_web": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"direct_stream":{
|
|
||||||
"chunksize":{
|
|
||||||
"value": 1048576,
|
|
||||||
"config_file": true,
|
|
||||||
"config_web": true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -111,8 +111,8 @@ class Channels():
|
|||||||
|
|
||||||
return [self.list[x].dict for x in list(self.list.keys())]
|
return [self.list[x].dict for x in list(self.list.keys())]
|
||||||
|
|
||||||
def get_channel_stream(self, channel_number):
|
def get_channel_stream(self, stream_args):
|
||||||
return self.origin.get_channel_stream(self.get_channel_dict("number", channel_number))
|
return self.origin.get_channel_stream(self.get_channel_dict("number", stream_args["channel"]), stream_args)
|
||||||
|
|
||||||
def get_channel_dict(self, keyfind, valfind):
|
def get_channel_dict(self, keyfind, valfind):
|
||||||
return self.get_channel_obj(keyfind, valfind).dict
|
return self.get_channel_obj(keyfind, valfind).dict
|
||||||
|
|||||||
@ -91,17 +91,24 @@ class Tuners():
|
|||||||
|
|
||||||
def get_stream_info(self, stream_args):
|
def get_stream_info(self, stream_args):
|
||||||
|
|
||||||
stream_args["channelUri"] = self.channels.get_channel_stream(str(stream_args["channel"]))
|
stream_info = self.channels.get_channel_stream(stream_args)
|
||||||
if not stream_args["channelUri"]:
|
if not stream_info:
|
||||||
raise TunerError("806 - Tune Failed")
|
raise TunerError("806 - Tune Failed")
|
||||||
|
|
||||||
if stream_args["channelUri"].startswith("udp://"):
|
if isinstance(stream_info, str):
|
||||||
|
stream_info = {"url": stream_info}
|
||||||
|
stream_args["stream_info"] = stream_info
|
||||||
|
|
||||||
|
if "quality_from_origin" not in list(stream_args["stream_info"].keys()):
|
||||||
|
stream_args["stream_info"]["quality_from_origin"] = False
|
||||||
|
|
||||||
|
if stream_args["stream_info"]["url"].startswith("udp://"):
|
||||||
stream_args["true_content_type"] = "video/mpeg"
|
stream_args["true_content_type"] = "video/mpeg"
|
||||||
stream_args["content_type"] = "video/mpeg"
|
stream_args["content_type"] = "video/mpeg"
|
||||||
else:
|
else:
|
||||||
|
|
||||||
channelUri_headers = self.fhdhr.web.session.head(stream_args["channelUri"]).headers
|
channel_stream_url_headers = self.fhdhr.web.session.head(stream_args["stream_info"]["url"]).headers
|
||||||
stream_args["true_content_type"] = channelUri_headers['Content-Type']
|
stream_args["true_content_type"] = channel_stream_url_headers['Content-Type']
|
||||||
|
|
||||||
if stream_args["true_content_type"].startswith(tuple(["application/", "text/"])):
|
if stream_args["true_content_type"].startswith(tuple(["application/", "text/"])):
|
||||||
stream_args["content_type"] = "video/mpeg"
|
stream_args["content_type"] = "video/mpeg"
|
||||||
|
|||||||
@ -14,7 +14,7 @@ class Direct_M3U8_Stream():
|
|||||||
self.stream_args = stream_args
|
self.stream_args = stream_args
|
||||||
self.tuner = tuner
|
self.tuner = tuner
|
||||||
|
|
||||||
self.chunksize = int(self.fhdhr.config.dict["direct_stream"]['chunksize'])
|
self.bytes_per_read = int(self.fhdhr.config.dict["streaming"]["bytes_per_read"])
|
||||||
|
|
||||||
def get(self):
|
def get(self):
|
||||||
|
|
||||||
@ -23,14 +23,14 @@ class Direct_M3U8_Stream():
|
|||||||
|
|
||||||
self.fhdhr.logger.info("Detected stream URL is m3u8: %s" % self.stream_args["true_content_type"])
|
self.fhdhr.logger.info("Detected stream URL is m3u8: %s" % self.stream_args["true_content_type"])
|
||||||
|
|
||||||
channelUri = self.stream_args["channelUri"]
|
channel_stream_url = self.stream_args["stream_info"]["url"]
|
||||||
while True:
|
while True:
|
||||||
|
|
||||||
self.fhdhr.logger.info("Opening m3u8 for reading %s" % channelUri)
|
self.fhdhr.logger.info("Opening m3u8 for reading %s" % channel_stream_url)
|
||||||
videoUrlM3u = m3u8.load(channelUri)
|
videoUrlM3u = m3u8.load(channel_stream_url)
|
||||||
if len(videoUrlM3u.playlists):
|
if len(videoUrlM3u.playlists):
|
||||||
self.fhdhr.logger.info("%s m3u8 varients found" % len(videoUrlM3u.playlists))
|
self.fhdhr.logger.info("%s m3u8 varients found" % len(videoUrlM3u.playlists))
|
||||||
channelUri = videoUrlM3u.playlists[0].absolute_uri
|
channel_stream_url = videoUrlM3u.playlists[0].absolute_uri
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
|
|
||||||
@ -42,7 +42,7 @@ class Direct_M3U8_Stream():
|
|||||||
|
|
||||||
while self.tuner.tuner_lock.locked():
|
while self.tuner.tuner_lock.locked():
|
||||||
|
|
||||||
playlist = m3u8.load(channelUri)
|
playlist = m3u8.load(channel_stream_url)
|
||||||
segments = playlist.segments
|
segments = playlist.segments
|
||||||
|
|
||||||
if len(played_chunk_urls):
|
if len(played_chunk_urls):
|
||||||
|
|||||||
@ -11,16 +11,16 @@ class Direct_Stream():
|
|||||||
self.stream_args = stream_args
|
self.stream_args = stream_args
|
||||||
self.tuner = tuner
|
self.tuner = tuner
|
||||||
|
|
||||||
self.chunksize = int(self.fhdhr.config.dict["direct_stream"]['chunksize'])
|
self.bytes_per_read = int(self.fhdhr.config.dict["streaming"]["bytes_per_read"])
|
||||||
|
|
||||||
def get(self):
|
def get(self):
|
||||||
|
|
||||||
if not self.stream_args["duration"] == 0:
|
if not self.stream_args["duration"] == 0:
|
||||||
self.stream_args["time_end"] = self.stream_args["duration"] + time.time()
|
self.stream_args["time_end"] = self.stream_args["duration"] + time.time()
|
||||||
|
|
||||||
self.fhdhr.logger.info("Direct Stream of %s URL: %s" % (self.stream_args["true_content_type"], self.stream_args["channelUri"]))
|
self.fhdhr.logger.info("Direct Stream of %s URL: %s" % (self.stream_args["true_content_type"], self.stream_args["stream_info"]["url"]))
|
||||||
|
|
||||||
req = self.fhdhr.web.session.get(self.stream_args["channelUri"], stream=True)
|
req = self.fhdhr.web.session.get(self.stream_args["stream_info"]["url"], stream=True)
|
||||||
|
|
||||||
def generate():
|
def generate():
|
||||||
|
|
||||||
@ -30,7 +30,7 @@ class Direct_Stream():
|
|||||||
|
|
||||||
while self.tuner.tuner_lock.locked():
|
while self.tuner.tuner_lock.locked():
|
||||||
|
|
||||||
for chunk in req.iter_content(chunk_size=self.chunksize):
|
for chunk in req.iter_content(chunk_size=self.bytes_per_read):
|
||||||
|
|
||||||
if (not self.stream_args["duration"] == 0 and
|
if (not self.stream_args["duration"] == 0 and
|
||||||
not time.time() < self.stream_args["time_end"]):
|
not time.time() < self.stream_args["time_end"]):
|
||||||
|
|||||||
@ -11,7 +11,7 @@ class FFMPEG_Stream():
|
|||||||
self.stream_args = stream_args
|
self.stream_args = stream_args
|
||||||
self.tuner = tuner
|
self.tuner = tuner
|
||||||
|
|
||||||
self.bytes_per_read = int(self.fhdhr.config.dict["ffmpeg"]["bytes_per_read"])
|
self.bytes_per_read = int(self.fhdhr.config.dict["streaming"]["bytes_per_read"])
|
||||||
self.ffmpeg_command = self.ffmpeg_command_assemble(stream_args)
|
self.ffmpeg_command = self.ffmpeg_command_assemble(stream_args)
|
||||||
|
|
||||||
def get(self):
|
def get(self):
|
||||||
@ -48,7 +48,7 @@ class FFMPEG_Stream():
|
|||||||
def ffmpeg_command_assemble(self, stream_args):
|
def ffmpeg_command_assemble(self, stream_args):
|
||||||
ffmpeg_command = [
|
ffmpeg_command = [
|
||||||
self.fhdhr.config.dict["ffmpeg"]["path"],
|
self.fhdhr.config.dict["ffmpeg"]["path"],
|
||||||
"-i", stream_args["channelUri"],
|
"-i", stream_args["stream_info"]["url"],
|
||||||
]
|
]
|
||||||
ffmpeg_command.extend(self.ffmpeg_duration(stream_args))
|
ffmpeg_command.extend(self.ffmpeg_duration(stream_args))
|
||||||
ffmpeg_command.extend(self.transcode_profiles(stream_args))
|
ffmpeg_command.extend(self.transcode_profiles(stream_args))
|
||||||
|
|||||||
@ -11,7 +11,7 @@ class VLC_Stream():
|
|||||||
self.stream_args = stream_args
|
self.stream_args = stream_args
|
||||||
self.tuner = tuner
|
self.tuner = tuner
|
||||||
|
|
||||||
self.bytes_per_read = int(self.fhdhr.config.dict["vlc"]["bytes_per_read"])
|
self.bytes_per_read = int(self.fhdhr.config.dict["streaming"]["bytes_per_read"])
|
||||||
self.vlc_command = self.vlc_command_assemble(stream_args)
|
self.vlc_command = self.vlc_command_assemble(stream_args)
|
||||||
|
|
||||||
def get(self):
|
def get(self):
|
||||||
@ -49,7 +49,7 @@ class VLC_Stream():
|
|||||||
def vlc_command_assemble(self, stream_args):
|
def vlc_command_assemble(self, stream_args):
|
||||||
vlc_command = [
|
vlc_command = [
|
||||||
self.fhdhr.config.dict["vlc"]["path"],
|
self.fhdhr.config.dict["vlc"]["path"],
|
||||||
"-I", "dummy", stream_args["channelUri"],
|
"-I", "dummy", stream_args["stream_info"]["url"],
|
||||||
]
|
]
|
||||||
vlc_command.extend(self.vlc_duration(stream_args))
|
vlc_command.extend(self.vlc_duration(stream_args))
|
||||||
vlc_command.extend(self.vlc_loglevel())
|
vlc_command.extend(self.vlc_loglevel())
|
||||||
|
|||||||
@ -88,7 +88,7 @@ class Tuner():
|
|||||||
"method": stream_args["method"],
|
"method": stream_args["method"],
|
||||||
"accessed": [stream_args["accessed"]],
|
"accessed": [stream_args["accessed"]],
|
||||||
"channel": stream_args["channel"],
|
"channel": stream_args["channel"],
|
||||||
"proxied_url": stream_args["channelUri"],
|
"proxied_url": stream_args["stream_info"]["url"],
|
||||||
"time_start": datetime.datetime.utcnow(),
|
"time_start": datetime.datetime.utcnow(),
|
||||||
"downloaded": 0
|
"downloaded": 0
|
||||||
}
|
}
|
||||||
|
|||||||
@ -36,8 +36,8 @@ class OriginServiceWrapper():
|
|||||||
def get_channels(self):
|
def get_channels(self):
|
||||||
return self.channels.get_channels()
|
return self.channels.get_channels()
|
||||||
|
|
||||||
def get_channel_stream(self, chandict):
|
def get_channel_stream(self, chandict, stream_args):
|
||||||
return self.channels.get_channel_stream(chandict)
|
return self.channels.get_channel_stream(chandict, stream_args)
|
||||||
|
|
||||||
def update_epg(self, channels):
|
def update_epg(self, channels):
|
||||||
return self.epg.update_epg(channels)
|
return self.epg.update_epg(channels)
|
||||||
|
|||||||
@ -7,5 +7,5 @@ class OriginChannels_StandIN():
|
|||||||
def get_channels(self):
|
def get_channels(self):
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def get_channel_stream(self, chandict):
|
def get_channel_stream(self, chandict, stream_args):
|
||||||
return None
|
return None
|
||||||
|
|||||||
@ -14,6 +14,10 @@ class Tuners():
|
|||||||
def __init__(self, fhdhr):
|
def __init__(self, fhdhr):
|
||||||
self.fhdhr = fhdhr
|
self.fhdhr = fhdhr
|
||||||
|
|
||||||
|
self.quality = self.fhdhr.config.dict["streaming"]["quality"]
|
||||||
|
if self.quality:
|
||||||
|
self.quality = str(self.quality).lower()
|
||||||
|
|
||||||
def __call__(self, *args):
|
def __call__(self, *args):
|
||||||
return self.get(*args)
|
return self.get(*args)
|
||||||
|
|
||||||
@ -50,8 +54,11 @@ class Tuners():
|
|||||||
|
|
||||||
duration = request.args.get('duration', default=0, type=int)
|
duration = request.args.get('duration', default=0, type=int)
|
||||||
|
|
||||||
transcode = request.args.get('transcode', default=None, type=str)
|
transcode = request.args.get('transcode', default=self.quality, type=str)
|
||||||
valid_transcode_types = [None, "heavy", "mobile", "internet720", "internet480", "internet360", "internet240"]
|
valid_transcode_types = [
|
||||||
|
None, "high", "medium", "low"
|
||||||
|
"heavy", "mobile", "internet720", "internet480", "internet360", "internet240"
|
||||||
|
]
|
||||||
if transcode not in valid_transcode_types:
|
if transcode not in valid_transcode_types:
|
||||||
response = Response("Service Unavailable", status=503)
|
response = Response("Service Unavailable", status=503)
|
||||||
response.headers["X-fHDHR-Error"] = "802 - Unknown Transcode Profile"
|
response.headers["X-fHDHR-Error"] = "802 - Unknown Transcode Profile"
|
||||||
@ -98,10 +105,7 @@ class Tuners():
|
|||||||
tuner.set_status(stream_args)
|
tuner.set_status(stream_args)
|
||||||
session["tuner_used"] = tunernum
|
session["tuner_used"] = tunernum
|
||||||
|
|
||||||
if stream_args["method"] == "direct":
|
return Response(stream_with_context(tuner.get_stream(stream_args, tuner)), mimetype=stream_args["content_type"])
|
||||||
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"])
|
|
||||||
|
|
||||||
elif method == "close":
|
elif method == "close":
|
||||||
|
|
||||||
|
|||||||
@ -50,7 +50,7 @@ class OriginChannels():
|
|||||||
channel_list.append(clean_station_item)
|
channel_list.append(clean_station_item)
|
||||||
return channel_list
|
return channel_list
|
||||||
|
|
||||||
def get_channel_stream(self, chandict):
|
def get_channel_stream(self, chandict, stream_args):
|
||||||
streamurl = ('%s%s:%s/live?channel_id=%s&client=%s' %
|
streamurl = ('%s%s:%s/live?channel_id=%s&client=%s' %
|
||||||
("https://" if self.fhdhr.config.dict["origin"]["ssl"] else "http://",
|
("https://" if self.fhdhr.config.dict["origin"]["ssl"] else "http://",
|
||||||
self.fhdhr.config.dict["origin"]["address"],
|
self.fhdhr.config.dict["origin"]["address"],
|
||||||
@ -58,4 +58,7 @@ class OriginChannels():
|
|||||||
str(chandict["origin_id"]),
|
str(chandict["origin_id"]),
|
||||||
"fhdhr_" + str(chandict["origin_number"]),
|
"fhdhr_" + str(chandict["origin_number"]),
|
||||||
))
|
))
|
||||||
return streamurl
|
|
||||||
|
stream_info = {"url": streamurl}
|
||||||
|
|
||||||
|
return stream_info
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user