mirror of
https://github.com/fHDHR/fHDHR_NextPVR.git
synced 2025-12-06 15:56:57 -05:00
commit
9fd7d8f205
@ -78,7 +78,7 @@ class Tuners():
|
|||||||
channelUri_headers = self.fhdhr.web.session.head(stream_args["channelUri"]).headers
|
channelUri_headers = self.fhdhr.web.session.head(stream_args["channelUri"]).headers
|
||||||
stream_args["true_content_type"] = channelUri_headers['Content-Type']
|
stream_args["true_content_type"] = channelUri_headers['Content-Type']
|
||||||
|
|
||||||
if stream_args["true_content_type"].startswith("application/"):
|
if stream_args["true_content_type"].startswith(tuple(["application/", "text/"])):
|
||||||
stream_args["content_type"] = "video/mpeg"
|
stream_args["content_type"] = "video/mpeg"
|
||||||
else:
|
else:
|
||||||
stream_args["content_type"] = stream_args["true_content_type"]
|
stream_args["content_type"] = stream_args["true_content_type"]
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
import time
|
import time
|
||||||
import re
|
import m3u8
|
||||||
import urllib.parse
|
|
||||||
|
from Crypto.Cipher import AES
|
||||||
|
|
||||||
# from fHDHR.exceptions import TunerError
|
# from fHDHR.exceptions import TunerError
|
||||||
|
|
||||||
@ -20,14 +20,18 @@ class Direct_Stream():
|
|||||||
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()
|
||||||
|
|
||||||
if not re.match('^(.*m3u8)[\n\r]*$', self.stream_args["channelUri"]):
|
if not self.stream_args["true_content_type"].startswith(tuple(["application/", "text/"])):
|
||||||
|
|
||||||
self.fhdhr.logger.info("Direct Stream of URL: %s" % self.stream_args["channelUri"])
|
self.fhdhr.logger.info("Direct Stream of %s URL: %s" % (self.stream_args["true_content_type"], self.stream_args["channelUri"]))
|
||||||
|
|
||||||
req = self.fhdhr.web.session.get(self.stream_args["channelUri"], stream=True)
|
req = self.fhdhr.web.session.get(self.stream_args["channelUri"], stream=True)
|
||||||
|
|
||||||
def generate():
|
def generate():
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
||||||
|
chunk_counter = 1
|
||||||
|
|
||||||
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.chunksize):
|
||||||
@ -41,7 +45,12 @@ class Direct_Stream():
|
|||||||
if not chunk:
|
if not chunk:
|
||||||
break
|
break
|
||||||
# raise TunerError("807 - No Video Data")
|
# raise TunerError("807 - No Video Data")
|
||||||
|
|
||||||
|
self.fhdhr.logger.info("Passing Through Chunk #%s with size %s" % (chunk_counter, self.chunksize))
|
||||||
yield chunk
|
yield chunk
|
||||||
|
|
||||||
|
chunk_counter += 1
|
||||||
|
|
||||||
self.fhdhr.logger.info("Connection Closed: Tuner Lock Removed")
|
self.fhdhr.logger.info("Connection Closed: Tuner Lock Removed")
|
||||||
|
|
||||||
except GeneratorExit:
|
except GeneratorExit:
|
||||||
@ -57,15 +66,14 @@ class Direct_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"])
|
||||||
|
|
||||||
# Determine if this m3u8 contains variants or chunks
|
|
||||||
channelUri = self.stream_args["channelUri"]
|
channelUri = self.stream_args["channelUri"]
|
||||||
self.fhdhr.logger.info("Opening m3u8 URL: %s" % channelUri)
|
while True:
|
||||||
m3u8_get = self.fhdhr.web.session.get(self.stream_args["channelUri"])
|
|
||||||
m3u8_content = m3u8_get.text
|
videoUrlM3u = m3u8.load(channelUri)
|
||||||
variants = [urllib.parse.urljoin(self.stream_args["channelUri"], line) for line in m3u8_content.split('\n') if re.match('^(.*m3u8)[\n\r]*$', line)]
|
if len(videoUrlM3u.playlists):
|
||||||
if len(variants):
|
channelUri = videoUrlM3u.playlists[0].absolute_uri
|
||||||
channelUri = variants[0]
|
else:
|
||||||
self.fhdhr.logger.info("m3u8 contained variants. Using URL: %s" % channelUri)
|
break
|
||||||
|
|
||||||
def generate():
|
def generate():
|
||||||
|
|
||||||
@ -75,20 +83,29 @@ class Direct_Stream():
|
|||||||
|
|
||||||
while self.tuner.tuner_lock.locked():
|
while self.tuner.tuner_lock.locked():
|
||||||
|
|
||||||
m3u8_get = self.fhdhr.web.session.get(channelUri)
|
playlist = m3u8.load(channelUri)
|
||||||
m3u8_content = m3u8_get.text
|
segments = playlist.segments
|
||||||
chunk_urls_detect = [urllib.parse.urljoin(channelUri, line) for line in m3u8_content.split('\n') if re.match('^(.*ts)[\n\r]*$', line)]
|
|
||||||
|
if len(played_chunk_urls):
|
||||||
|
newsegments = 0
|
||||||
|
for segment in segments:
|
||||||
|
if segment.absolute_uri not in played_chunk_urls:
|
||||||
|
newsegments += 1
|
||||||
|
self.fhdhr.logger.info("Refreshing m3u8, Loaded %s new segments." % str(newsegments))
|
||||||
|
else:
|
||||||
|
self.fhdhr.logger.info("Loaded %s segments." % str(len(segments)))
|
||||||
|
|
||||||
|
if playlist.keys != [None]:
|
||||||
|
keys = [{"url": key.uri, "method": key.method, "iv": key.iv} for key in playlist.keys if key]
|
||||||
|
else:
|
||||||
|
keys = [None for i in range(0, len(segments))]
|
||||||
|
|
||||||
|
for segment, key in zip(segments, keys):
|
||||||
|
chunkurl = segment.absolute_uri
|
||||||
|
|
||||||
chunk_urls_play = []
|
|
||||||
for chunkurl in chunk_urls_detect:
|
|
||||||
if chunkurl not in played_chunk_urls:
|
if chunkurl not in played_chunk_urls:
|
||||||
chunk_urls_play.append(chunkurl)
|
|
||||||
played_chunk_urls.append(chunkurl)
|
played_chunk_urls.append(chunkurl)
|
||||||
|
|
||||||
for chunkurl in chunk_urls_play:
|
|
||||||
|
|
||||||
self.fhdhr.logger.info("Passing Through Chunk: %s" % chunkurl)
|
|
||||||
|
|
||||||
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"]):
|
||||||
self.fhdhr.logger.info("Requested Duration Expired.")
|
self.fhdhr.logger.info("Requested Duration Expired.")
|
||||||
@ -98,7 +115,17 @@ class Direct_Stream():
|
|||||||
if not chunk:
|
if not chunk:
|
||||||
break
|
break
|
||||||
# raise TunerError("807 - No Video Data")
|
# raise TunerError("807 - No Video Data")
|
||||||
|
if key:
|
||||||
|
keyfile = self.fhdhr.web.session.get(key["url"]).content
|
||||||
|
cryptor = AES.new(keyfile, AES.MODE_CBC, keyfile)
|
||||||
|
chunk = cryptor.decrypt(chunk)
|
||||||
|
|
||||||
|
self.fhdhr.logger.info("Passing Through Chunk: %s" % chunkurl)
|
||||||
yield chunk
|
yield chunk
|
||||||
|
|
||||||
|
if playlist.target_duration:
|
||||||
|
time.sleep(int(playlist.target_duration))
|
||||||
|
|
||||||
self.fhdhr.logger.info("Connection Closed: Tuner Lock Removed")
|
self.fhdhr.logger.info("Connection Closed: Tuner Lock Removed")
|
||||||
|
|
||||||
except GeneratorExit:
|
except GeneratorExit:
|
||||||
|
|||||||
@ -4,3 +4,5 @@ flask
|
|||||||
image
|
image
|
||||||
xmltodict
|
xmltodict
|
||||||
sqlalchemy
|
sqlalchemy
|
||||||
|
pycrypto
|
||||||
|
m3u8
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user