print errors, provide an additional audio-only formatStream
This commit is contained in:
@@ -85,22 +85,44 @@ def safeTraverse(obj: dict, path: list, default=None):
|
|||||||
result = result[x]
|
result = result[x]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
result = default
|
result = default
|
||||||
|
print(f"error reading: {' -> '.join(path)} - returning: {default}")
|
||||||
finally:
|
finally:
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def getError(idata: dict):
|
||||||
|
unknown_error = {"status": "Unknown error", "reason": "This is a generic ythdd error."}
|
||||||
|
error = ""
|
||||||
|
|
||||||
|
try:
|
||||||
|
playabilityStatus = safeTraverse(idata, ['stage1', 'playabilityStatus'], default=unknown_error)
|
||||||
|
print(playabilityStatus)
|
||||||
|
error += f"({playabilityStatus['status']}) {playabilityStatus['reason']}"
|
||||||
|
except:
|
||||||
|
error += f"Generic error"
|
||||||
|
|
||||||
|
try:
|
||||||
|
errorScreen = safeTraverse(idata, ['stage1', 'playabilityStatus', 'errorScreen', 'playerErrorMessageRenderer', 'subreason', 'runs'], default=[])
|
||||||
|
error += " - "
|
||||||
|
for x in errorScreen:
|
||||||
|
error += f"{x['text']} "
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return error
|
||||||
|
|
||||||
def genThumbs(videoId: str):
|
def genThumbs(videoId: str):
|
||||||
|
|
||||||
result = []
|
result = []
|
||||||
thumbnails = [
|
thumbnails = [
|
||||||
#{'height': 720, 'width': 1280, 'quality': "maxres", 'url': "maxres"}, # for the time being omit the buggy maxres quality
|
#{'height': 720, 'width': 1280, 'quality': "maxres", 'url': "maxres"}, # for the time being omit the buggy maxres quality
|
||||||
{'height': 720, 'width': 1280, 'quality': "maxresdefault", 'url': "maxresdefault"},
|
{'height': 720, 'width': 1280, 'quality': "maxresdefault", 'url': "maxresdefault"},
|
||||||
{'height': 480, 'width': 640, 'quality': "sddefault", 'url': "sddefault"},
|
{'height': 480, 'width': 640, 'quality': "sddefault", 'url': "sddefault"},
|
||||||
{'height': 360, 'width': 480, 'quality': "high", 'url': "hqdefault"},
|
{'height': 360, 'width': 480, 'quality': "high", 'url': "hqdefault"},
|
||||||
{'height': 180, 'width': 320, 'quality': "medium", 'url': "mqdefault"},
|
{'height': 180, 'width': 320, 'quality': "medium", 'url': "mqdefault"},
|
||||||
{'height': 90, 'width': 120, 'quality': "default", 'url': "default"},
|
{'height': 90, 'width': 120, 'quality': "default", 'url': "default"},
|
||||||
{'height': 90, 'width': 120, 'quality': "start", 'url': "1"},
|
{'height': 90, 'width': 120, 'quality': "start", 'url': "1"},
|
||||||
{'height': 90, 'width': 120, 'quality': "middle", 'url': "2"},
|
{'height': 90, 'width': 120, 'quality': "middle", 'url': "2"},
|
||||||
{'height': 90, 'width': 120, 'quality': "end", 'url': "3"},
|
{'height': 90, 'width': 120, 'quality': "end", 'url': "3"},
|
||||||
]
|
]
|
||||||
|
|
||||||
for x in thumbnails:
|
for x in thumbnails:
|
||||||
@@ -115,7 +137,8 @@ def genThumbs(videoId: str):
|
|||||||
def rebuildFormats(data):
|
def rebuildFormats(data):
|
||||||
result = [{} for x in data]
|
result = [{} for x in data]
|
||||||
formatStreams = []
|
formatStreams = []
|
||||||
best_bitrate = 0
|
best_bitrate_video = 0
|
||||||
|
best_bitrate_audio = -1
|
||||||
|
|
||||||
for x in range(len(data)):
|
for x in range(len(data)):
|
||||||
|
|
||||||
@@ -142,6 +165,11 @@ def rebuildFormats(data):
|
|||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
if "audio" == data[x]['mimeType'][:5]:
|
||||||
|
isAudio = 1
|
||||||
|
else:
|
||||||
|
isAudio = 0
|
||||||
|
|
||||||
if isVideo:
|
if isVideo:
|
||||||
result[x]['fps'] = str(data[x]['fps'])
|
result[x]['fps'] = str(data[x]['fps'])
|
||||||
else:
|
else:
|
||||||
@@ -164,29 +192,46 @@ def rebuildFormats(data):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
# we assume here that a stream with the highest bitrate must be a video stream- that may not be the case
|
# we assume here that a stream with the highest bitrate must be a video stream- that may not be the case
|
||||||
if data[x]['averageBitrate'] > data[best_bitrate]['averageBitrate'] and isVideo:
|
if data[x]['averageBitrate'] > data[best_bitrate_video]['averageBitrate'] and isVideo:
|
||||||
best_bitrate = x
|
best_bitrate_video = x
|
||||||
|
if data[x]['averageBitrate'] > data[best_bitrate_audio]['averageBitrate'] and isAudio:
|
||||||
|
best_bitrate_audio = x
|
||||||
|
|
||||||
# makes FreeTube work, unfortunately it's a video-only stream
|
# makes FreeTube work, unfortunately it's a video-only stream
|
||||||
formatStreams = [
|
formatStreams = [
|
||||||
{
|
{
|
||||||
"url": data[best_bitrate]['url'],
|
"url": data[best_bitrate_video]['url'],
|
||||||
"itag": str(data[best_bitrate]['itag']),
|
"itag": str(data[best_bitrate_video]['itag']),
|
||||||
"type": data[best_bitrate]['mimeType'],
|
"type": data[best_bitrate_video]['mimeType'],
|
||||||
"quality": data[best_bitrate]['quality'],
|
"quality": data[best_bitrate_video]['quality'],
|
||||||
"bitrate": str(data[best_bitrate]['averageBitrate']),
|
"bitrate": str(data[best_bitrate_video]['averageBitrate']),
|
||||||
"fps": data[best_bitrate]['fps'],
|
"fps": data[best_bitrate_video]['fps'],
|
||||||
"size": "", # workaround for clipious, which requires ANYTHING to be passed, or else it will throw and error and won't load the video
|
"size": "", # workaround for clipious, which requires ANYTHING to be passed, or else it will throw and error and won't load the video
|
||||||
"resolution": str(invidious_formats.FORMATS[data[best_bitrate]['itag']]['height']) + "p",
|
"resolution": str(invidious_formats.FORMATS[data[best_bitrate_video]['itag']]['height']) + "p",
|
||||||
"qualityLabel": str(invidious_formats.FORMATS[data[best_bitrate]['itag']]['height']) + "p",
|
"qualityLabel": str(invidious_formats.FORMATS[data[best_bitrate_video]['itag']]['height']) + "p",
|
||||||
"container": invidious_formats.FORMATS[data[best_bitrate]['itag']]['ext'],
|
"container": invidious_formats.FORMATS[data[best_bitrate_video]['itag']]['ext'],
|
||||||
"encoding": invidious_formats.FORMATS[data[best_bitrate]['itag']]['vcodec']
|
"encoding": invidious_formats.FORMATS[data[best_bitrate_video]['itag']]['vcodec']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"audioChannels": data[best_bitrate_audio]['audioChannels'],
|
||||||
|
"init": result[best_bitrate_audio]['init'],
|
||||||
|
"index": result[best_bitrate_audio]['index'],
|
||||||
|
"bitrate": str(data[best_bitrate_audio]['averageBitrate']),
|
||||||
|
"url": data[best_bitrate_audio]['url'],
|
||||||
|
"itag": str(data[best_bitrate_audio]['itag']),
|
||||||
|
"type": data[best_bitrate_audio]['mimeType'],
|
||||||
|
"clen": result[best_bitrate_audio]['clen'],
|
||||||
|
"lmt": result[best_bitrate_audio]['lmt'],
|
||||||
|
"projectionType": result[best_bitrate_audio]['projectionType'],
|
||||||
|
"audioQuality": result[best_bitrate_audio]['audioQuality'],
|
||||||
|
"audioSampleRate": result[best_bitrate_audio]['audioSampleRate'],
|
||||||
|
"qualityLabel": "audio"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
# not all itags have width and/or height
|
# not all itags have width and/or height
|
||||||
try:
|
try:
|
||||||
formatStreams["size"] = str(invidious_formats.FORMATS[data[best_bitrate]['itag']]['width']) + "x" + str(invidious_formats.FORMATS[data[best_bitrate]['itag']]['height'])
|
formatStreams[0]["size"] = str(invidious_formats.FORMATS[data[best_bitrate]['itag']]['width']) + "x" + str(invidious_formats.FORMATS[data[best_bitrate]['itag']]['height'])
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -201,6 +246,13 @@ def videos(data):
|
|||||||
|
|
||||||
#print("requesting idata from IOSextract")
|
#print("requesting idata from IOSextract")
|
||||||
idata = ythdd_extractor.IOSextract(data[3])
|
idata = ythdd_extractor.IOSextract(data[3])
|
||||||
|
|
||||||
|
hls_url = safeTraverse(idata, ['stage1', 'streamingData', 'hlsManifestUrl'], default="")
|
||||||
|
adaptive_formats = safeTraverse(idata, ['stage1', 'streamingData', 'adaptiveFormats'], default=[])
|
||||||
|
if not hls_url:
|
||||||
|
print(f"serious error: couldn't get hls_url or adaptive_formats!\ndumping idata:\n{idata}")
|
||||||
|
return send(500, {'error': getError(idata)})
|
||||||
|
|
||||||
wdata = ythdd_extractor.WEBextractSinglePage(data[3])
|
wdata = ythdd_extractor.WEBextractSinglePage(data[3])
|
||||||
|
|
||||||
#return send(200, {'idata': idata, 'wdata': wdata})
|
#return send(200, {'idata': idata, 'wdata': wdata})
|
||||||
@@ -303,9 +355,6 @@ def videos(data):
|
|||||||
# that is - without making another request
|
# that is - without making another request
|
||||||
author_verified = False
|
author_verified = False
|
||||||
|
|
||||||
|
|
||||||
hls_url = safeTraverse(idata, ['stage1', 'streamingData', 'hlsManifestUrl'], default="")
|
|
||||||
adaptive_formats = safeTraverse(idata, ['stage1', 'streamingData', 'adaptiveFormats'], default=[])
|
|
||||||
format_streams = []
|
format_streams = []
|
||||||
adaptive_formats, format_streams = rebuildFormats(adaptive_formats)
|
adaptive_formats, format_streams = rebuildFormats(adaptive_formats)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user