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

Improve Streams with Headers and Quality Options

This commit is contained in:
deathbybandaid 2021-01-20 16:01:32 -05:00
parent c0ff51a6db
commit 63685f4a0e
9 changed files with 152 additions and 51 deletions

View File

@ -5,7 +5,12 @@
"config_file": true,
"config_web": true
},
"quality": {
"origin_quality": {
"value": "none",
"config_file": true,
"config_web": true
},
"transcode_quality": {
"value": "none",
"config_file": true,
"config_web": true

View File

@ -1,3 +1,4 @@
import m3u8
from fHDHR.exceptions import TunerError
@ -96,12 +97,15 @@ class Tuners():
raise TunerError("806 - Tune Failed")
if isinstance(stream_info, str):
stream_info = {"url": stream_info}
stream_info = {"url": stream_info, "headers": None}
stream_args["stream_info"] = stream_info
if not stream_args["stream_info"]["url"]:
raise TunerError("806 - Tune Failed")
if "headers" not in list(stream_args["stream_info"].keys()):
stream_args["stream_info"]["headers"] = None
if stream_args["stream_info"]["url"].startswith("udp://"):
stream_args["true_content_type"] = "video/mpeg"
stream_args["content_type"] = "video/mpeg"
@ -112,7 +116,81 @@ class Tuners():
if stream_args["true_content_type"].startswith(tuple(["application/", "text/"])):
stream_args["content_type"] = "video/mpeg"
if stream_args["origin_quality"] != -1:
stream_args["stream_info"]["url"] = self.m3u8_quality(stream_args)
else:
stream_args["content_type"] = stream_args["true_content_type"]
return stream_args
def m3u8_quality(self, stream_args):
m3u8_url = stream_args["stream_info"]["url"]
quality_profile = stream_args["origin_quality"]
if not quality_profile:
quality_profile = "high"
self.fhdhr.logger.info("Origin Quality not set in config. Defaulting to Highest Quality")
else:
quality_profile = quality_profile.lower()
self.fhdhr.logger.info("Origin Quality set in config to %s" % (quality_profile))
while True:
self.fhdhr.logger.info("Opening m3u8 for reading %s" % m3u8_url)
if stream_args["stream_info"]["headers"]:
videoUrlM3u = m3u8.load(m3u8_url, headers=stream_args["stream_info"]["headers"])
else:
videoUrlM3u = m3u8.load(m3u8_url)
if len(videoUrlM3u.playlists):
self.fhdhr.logger.info("%s m3u8 varients found" % len(videoUrlM3u.playlists))
# Create list of dicts
playlists, playlist_index = {}, 0
for playlist_item in videoUrlM3u.playlists:
playlist_index += 1
playlist_dict = {
"url": playlist_item.absolute_uri,
"bandwidth": playlist_item.stream_info.bandwidth,
}
if not playlist_item.stream_info.resolution:
playlist_dict["width"] = None
playlist_dict["height"] = None
else:
try:
playlist_dict["width"] = playlist_item.stream_info.resolution[0]
playlist_dict["height"] = playlist_item.stream_info.resolution[1]
except TypeError:
playlist_dict["width"] = None
playlist_dict["height"] = None
playlists[playlist_index] = playlist_dict
sorted_playlists = sorted(playlists, key=lambda i: (
int(playlists[i]['bandwidth']),
int(playlists[i]['width'] or 0),
int(playlists[i]['height'] or 0)
))
sorted_playlists = [playlists[x] for x in sorted_playlists]
if not quality_profile or quality_profile == "high":
selected_index = -1
elif quality_profile == "medium":
selected_index = int((len(sorted_playlists) - 1)/2)
elif quality_profile == "low":
selected_index = 0
m3u8_stats = ",".join(
["%s %s" % (x, sorted_playlists[selected_index][x])
for x in list(sorted_playlists[selected_index].keys())
if x != "url" and sorted_playlists[selected_index][x]])
self.fhdhr.logger.info("Selected m3u8 details: %s" % m3u8_stats)
m3u8_url = sorted_playlists[selected_index]["url"]
else:
self.fhdhr.logger.info("No m3u8 varients found")
break
return m3u8_url

View File

@ -21,18 +21,7 @@ class Direct_M3U8_Stream():
if not self.stream_args["duration"] == 0:
self.stream_args["time_end"] = self.stream_args["duration"] + time.time()
self.fhdhr.logger.info("Detected stream URL is m3u8: %s" % self.stream_args["true_content_type"])
channel_stream_url = self.stream_args["stream_info"]["url"]
while True:
self.fhdhr.logger.info("Opening m3u8 for reading %s" % channel_stream_url)
videoUrlM3u = m3u8.load(channel_stream_url)
if len(videoUrlM3u.playlists):
self.fhdhr.logger.info("%s m3u8 varients found" % len(videoUrlM3u.playlists))
channel_stream_url = videoUrlM3u.playlists[0].absolute_uri
else:
break
self.fhdhr.logger.info("Detected stream of m3u8 URL: %s" % self.stream_args["stream_info"]["url"])
def generate():
@ -42,7 +31,11 @@ class Direct_M3U8_Stream():
while self.tuner.tuner_lock.locked():
playlist = m3u8.load(channel_stream_url)
if self.stream_args["stream_info"]["headers"]:
playlist = m3u8.load(self.stream_args["stream_info"]["url"], headers=self.stream_args["stream_info"]["headers"])
else:
playlist = m3u8.load(self.stream_args["stream_info"]["url"])
segments = playlist.segments
if len(played_chunk_urls):
@ -70,13 +63,19 @@ class Direct_M3U8_Stream():
self.fhdhr.logger.info("Requested Duration Expired.")
self.tuner.close()
chunk = self.fhdhr.web.session.get(chunkurl).content
if self.stream_args["stream_info"]["headers"]:
chunk = self.fhdhr.web.session.get(chunkurl, headers=self.stream_args["stream_info"]["headers"]).content
else:
chunk = self.fhdhr.web.session.get(chunkurl).content
if not chunk:
break
# raise TunerError("807 - No Video Data")
if key:
if key["url"]:
keyfile = self.fhdhr.web.session.get(key["url"]).content
if self.stream_args["stream_info"]["headers"]:
keyfile = self.fhdhr.web.session.get(key["url"], headers=self.stream_args["stream_info"]["headers"]).content
else:
keyfile = self.fhdhr.web.session.get(key["url"]).content
cryptor = AES.new(keyfile, AES.MODE_CBC, keyfile)
self.fhdhr.logger.info("Decrypting Chunk #%s with key: %s" % (len(played_chunk_urls), key["url"]))
chunk = cryptor.decrypt(chunk)

View File

@ -20,7 +20,10 @@ class Direct_Stream():
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["stream_info"]["url"], stream=True)
if self.stream_args["stream_info"]["headers"]:
req = self.fhdhr.web.session.get(self.stream_args["stream_info"]["url"], stream=True, headers=self.stream_args["stream_info"]["headers"])
else:
req = self.fhdhr.web.session.get(self.stream_args["stream_info"]["url"], stream=True)
def generate():

View File

@ -50,12 +50,26 @@ class FFMPEG_Stream():
self.fhdhr.config.dict["ffmpeg"]["path"],
"-i", stream_args["stream_info"]["url"],
]
ffmpeg_command.extend(self.ffmpeg_headers(stream_args))
ffmpeg_command.extend(self.ffmpeg_duration(stream_args))
ffmpeg_command.extend(self.transcode_profiles(stream_args))
ffmpeg_command.extend(self.ffmpeg_loglevel())
ffmpeg_command.extend(["pipe:stdout"])
return ffmpeg_command
def ffmpeg_headers(self, stream_args):
ffmpeg_command = []
if stream_args["stream_info"]["headers"]:
headers_string = ""
if len(list(stream_args["stream_info"]["headers"].keys())) > 1:
for x in list(stream_args["stream_info"]["headers"].keys()):
headers_string += "%s: %s\r\n" % (x, stream_args["stream_info"]["headers"][x])
else:
for x in list(stream_args["stream_info"]["headers"].keys()):
headers_string += "%s: %s" % (x, stream_args["stream_info"]["headers"][x])
ffmpeg_command.extend(["-headers", '\"%s\"' % headers_string])
return ffmpeg_command
def ffmpeg_duration(self, stream_args):
ffmpeg_command = []
if stream_args["duration"]:
@ -103,30 +117,30 @@ class FFMPEG_Stream():
16:9 content, not exceeding 320x240 30fps for 4:3 content
"""
if stream_args["transcode"]:
self.fhdhr.logger.info("Client requested a %s transcode for stream." % stream_args["transcode"])
stream_args["transcode"] = None
if stream_args["transcode_quality"]:
self.fhdhr.logger.info("Client requested a %s transcode for stream." % stream_args["transcode_quality"])
stream_args["transcode_quality"] = None
ffmpeg_command = []
if not stream_args["transcode"]:
if not stream_args["transcode_quality"]:
ffmpeg_command.extend(
[
"-c", "copy",
"-f", "mpegts",
]
)
elif stream_args["transcode"] == "heavy":
elif stream_args["transcode_quality"] == "heavy":
ffmpeg_command.extend([])
elif stream_args["transcode"] == "mobile":
elif stream_args["transcode_quality"] == "mobile":
ffmpeg_command.extend([])
elif stream_args["transcode"] == "internet720":
elif stream_args["transcode_quality"] == "internet720":
ffmpeg_command.extend([])
elif stream_args["transcode"] == "internet480":
elif stream_args["transcode_quality"] == "internet480":
ffmpeg_command.extend([])
elif stream_args["transcode"] == "internet360":
elif stream_args["transcode_quality"] == "internet360":
ffmpeg_command.extend([])
elif stream_args["transcode"] == "internet240":
elif stream_args["transcode_quality"] == "internet240":
ffmpeg_command.extend([])
return ffmpeg_command

View File

@ -51,12 +51,17 @@ class VLC_Stream():
self.fhdhr.config.dict["vlc"]["path"],
"-I", "dummy", stream_args["stream_info"]["url"],
]
# vlc_command.extend(self.vlc_headers(stream_args))
vlc_command.extend(self.vlc_duration(stream_args))
vlc_command.extend(self.vlc_loglevel())
vlc_command.extend(["--sout"])
vlc_command.extend(self.transcode_profiles(stream_args))
return vlc_command
def vlc_headers(self, stream_args):
vlc_command = []
return vlc_command
def vlc_duration(self, stream_args):
vlc_command = []
if stream_args["duration"]:
@ -95,28 +100,28 @@ class VLC_Stream():
"""
vlc_command = []
if stream_args["transcode"]:
self.fhdhr.logger.info("Client requested a %s transcode for stream." % stream_args["transcode"])
stream_args["transcode"] = None
if stream_args["transcode_quality"]:
self.fhdhr.logger.info("Client requested a %s transcode for stream." % stream_args["transcode_quality"])
stream_args["transcode_quality"] = None
vlc_transcode_string = "#std{mux=ts,access=file,dst=-}"
return [vlc_transcode_string]
'#transcode{vcodec=mp2v,vb=4096,acodec=mp2a,ab=192,scale=1,channels=2,deinterlace}:std{access=file,mux=ts,dst=-"}'
if not stream_args["transcode"]:
if not stream_args["transcode_quality"]:
vlc_command.extend([])
elif stream_args["transcode"] == "heavy":
elif stream_args["transcode_quality"] == "heavy":
vlc_command.extend([])
elif stream_args["transcode"] == "mobile":
elif stream_args["transcode_quality"] == "mobile":
vlc_command.extend([])
elif stream_args["transcode"] == "internet720":
elif stream_args["transcode_quality"] == "internet720":
vlc_command.extend([])
elif stream_args["transcode"] == "internet480":
elif stream_args["transcode_quality"] == "internet480":
vlc_command.extend([])
elif stream_args["transcode"] == "internet360":
elif stream_args["transcode_quality"] == "internet360":
vlc_command.extend([])
elif stream_args["transcode"] == "internet240":
elif stream_args["transcode_quality"] == "internet240":
vlc_command.extend([])
return vlc_command

View File

@ -16,10 +16,6 @@ class Tuners():
def __init__(self, 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):
return self.get(*args)
@ -56,12 +52,12 @@ class Tuners():
duration = request.args.get('duration', default=0, type=int)
transcode = request.args.get('transcode', default=self.quality, type=str)
transcode_quality = request.args.get('transcode', default=None, type=str)
valid_transcode_types = [
None, "high", "medium", "low"
"heavy", "mobile", "internet720", "internet480", "internet360", "internet240"
]
if transcode not in valid_transcode_types:
if transcode_quality 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"])
@ -71,7 +67,8 @@ class Tuners():
"channel": channel_number,
"method": method,
"duration": duration,
"transcode": transcode,
"origin_quality": self.fhdhr.config.dict["streaming"]["origin_quality"],
"transcode_quality": transcode_quality,
"accessed": accessed_url,
"client": client_address,
"client_id": session["session_id"]

View File

@ -36,9 +36,9 @@ class Auto():
if duration:
redirect_url += "&duration=%s" % str(duration)
transcode = request.args.get('transcode', default=None, type=str)
if transcode:
redirect_url += "&transcode=%s" % str(transcode)
transcode_quality = request.args.get('transcode', default=None, type=str)
if transcode_quality:
redirect_url += "&transcode=%s" % str(transcode_quality)
redirect_url += "&accessed=%s" % urllib.parse.quote(request.url)

View File

@ -38,9 +38,9 @@ class Tuner():
if duration:
redirect_url += "&duration=%s" % str(duration)
transcode = request.args.get('transcode', default=None, type=str)
if transcode:
redirect_url += "&transcode=%s" % str(transcode)
transcode_quality = request.args.get('transcode', default=None, type=str)
if transcode_quality:
redirect_url += "&transcode=%s" % str(transcode_quality)
redirect_url += "&accessed=%s" % urllib.parse.quote(request.url)