print errors, provide an additional audio-only formatStream

This commit is contained in:
2025-01-18 01:45:43 +01:00
parent 71a24a4870
commit 8fa05486f1

View File

@@ -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)