make FreeTube work (by using video only streams), fix livestream dates
This commit is contained in:
@@ -69,6 +69,9 @@ def streams():
|
||||
def epochToDate(epoch):
|
||||
return strftime('%Y-%m-%dT%H:%M:%SZ', gmtime(epoch))
|
||||
|
||||
def dateToEpoch(date: str):
|
||||
return datetime.datetime.fromisoformat(date).timestamp()
|
||||
|
||||
def trending():
|
||||
return send(200, [{}])
|
||||
|
||||
@@ -105,7 +108,6 @@ def genThumbs(videoId: str):
|
||||
height = x['height']
|
||||
quality = x['quality']
|
||||
url = ythdd_globals.config['general']['public_facing_url'] + 'vi/' + videoId + '/' + x['url'] + '.jpg'
|
||||
#url = '/vi/' + videoId + '/' + x['url'] + '.jpg'
|
||||
result.append({'quality': quality, 'url': url, 'width': width, 'height': height})
|
||||
|
||||
return result
|
||||
@@ -113,6 +115,7 @@ def genThumbs(videoId: str):
|
||||
def rebuildFormats(data):
|
||||
result = [{} for x in data]
|
||||
formatStreams = []
|
||||
best_bitrate = 0
|
||||
|
||||
for x in range(len(data)):
|
||||
|
||||
@@ -122,6 +125,9 @@ def rebuildFormats(data):
|
||||
except:
|
||||
isVideo = 1
|
||||
|
||||
if not "initRange" in data[x]: # for livestreams?
|
||||
continue
|
||||
|
||||
result[x]['init'] = str(data[x]['initRange']['start']) + "-" + str(data[x]['initRange']['end'])
|
||||
result[x]['index'] = str(data[x]['indexRange']['start']) + "-" + str(data[x]['indexRange']['end'])
|
||||
result[x]['bitrate'] = str(data[x]['averageBitrate'])
|
||||
@@ -157,8 +163,32 @@ def rebuildFormats(data):
|
||||
except:
|
||||
pass
|
||||
|
||||
#if data[x]['itag'] <= 80: # won't be triggered for iOS player as it has no progressive streams
|
||||
# formatStreams.append(result[x])
|
||||
# we assume here that a stream with the highest bitrate must be a video stream
|
||||
if data[x]['averageBitrate'] > data[best_bitrate]['averageBitrate']:
|
||||
best_bitrate = x
|
||||
|
||||
# makes FreeTube work, unfortunately it's a video-only stream
|
||||
formatStreams = [
|
||||
{
|
||||
"url": data[best_bitrate]['url'],
|
||||
"itag": str(data[best_bitrate]['itag']),
|
||||
"type": data[best_bitrate]['mimeType'],
|
||||
"quality": data[best_bitrate]['quality'],
|
||||
"bitrate": str(data[best_bitrate]['averageBitrate']),
|
||||
"fps": data[best_bitrate]['fps'],
|
||||
"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",
|
||||
"qualityLabel": str(invidious_formats.FORMATS[data[best_bitrate]['itag']]['height']) + "p",
|
||||
"container": invidious_formats.FORMATS[data[best_bitrate]['itag']]['ext'],
|
||||
"encoding": invidious_formats.FORMATS[data[best_bitrate]['itag']]['vcodec']
|
||||
}
|
||||
]
|
||||
|
||||
# not all itags have width and/or height
|
||||
try:
|
||||
formatStreams["size"] = str(invidious_formats.FORMATS[data[best_bitrate]['itag']]['width']) + "x" + str(invidious_formats.FORMATS[data[best_bitrate]['itag']]['height'])
|
||||
except:
|
||||
pass
|
||||
|
||||
return result, formatStreams
|
||||
|
||||
@@ -188,7 +218,7 @@ def videos(data):
|
||||
title = safeTraverse(video_details, ['title'], default=video_id)
|
||||
views = int(safeTraverse(video_details, ['viewCount'], default=0))
|
||||
length = int(safeTraverse(video_details, ['lengthSeconds'], default=1))
|
||||
published = datetime.datetime.fromisoformat(safeTraverse(microformat, ['publishDate'], default="1970-01-02T00:00:00Z")).timestamp() # ISO format to Unix timestamp
|
||||
published = dateToEpoch(safeTraverse(microformat, ['publishDate'], default="1970-01-02T00:00:00Z")) # ISO format to Unix timestamp
|
||||
published_date = epochToDate(published)
|
||||
premiere_timestamp = safeTraverse(microformat, ['liveBroadcastDetails', 'startTimestamp'], default=0) # let's ignore the nitty gritty for the time being
|
||||
premiere_timestamp = premiere_timestamp if premiere_timestamp else safeTraverse(microformat, ['playabilityStatus', 'liveStreamability', 'liveStreamabilityRenderer', 'offlineSlate', 'liveStreamOfflineSlateRenderer', 'scheduledStartTime'], default=0)
|
||||
@@ -244,8 +274,8 @@ def videos(data):
|
||||
for x in magnitude.keys():
|
||||
if x in likes_text:
|
||||
likes *= magnitude[x]
|
||||
description = safeTraverse(microformat, ['description', 'simpleText'], default="\n(ythdd: error ocurred, failed to retrieve description)")
|
||||
short_description = safeTraverse(wdata, ['ec1', 'videoDetails', 'shortDescription'], default="(ythdd: error occurred, failed to retrieve short description)")
|
||||
description = safeTraverse(microformat, ['description', 'simpleText'], default="\n(ythdd: failed to retrieve description, perhaps it's empty?)")
|
||||
short_description = safeTraverse(wdata, ['ec1', 'videoDetails', 'shortDescription'], default="(ythdd: failed to retrieve short description, perhaps it's empty?)")
|
||||
description_html = "<p>" + description + "</p>" # sorry, not happening right now, TODO: https://github.com/iv-org/invidious/blob/master/src/invidious/videos/parser.cr#L329
|
||||
|
||||
metadata = safeTraverse(video_secondary_renderer, ['metadataRowContainer', 'metadataRowContainerRenderer', 'rows'], default={})
|
||||
@@ -274,13 +304,14 @@ def videos(data):
|
||||
|
||||
hls_url = safeTraverse(idata, ['stage1', 'streamingData', 'hlsManifestUrl'], default="")
|
||||
adaptive_formats = safeTraverse(idata, ['stage1', 'streamingData', 'adaptiveFormats'], default=[])
|
||||
format_streams = []
|
||||
adaptive_formats, format_streams = rebuildFormats(adaptive_formats)
|
||||
|
||||
if live_now:
|
||||
video_type = "livestream"
|
||||
elif premiere_timestamp:
|
||||
video_type = "scheduled"
|
||||
published = premiere_timestamp if premiere_timestamp else int(time.time())
|
||||
published = dateToEpoch(premiere_timestamp) if premiere_timestamp else int(time.time())
|
||||
else:
|
||||
video_type = "video"
|
||||
|
||||
@@ -289,11 +320,6 @@ def videos(data):
|
||||
premium = True
|
||||
# TODO: detect paywalled patron-only videos
|
||||
|
||||
if not format_streams:
|
||||
format_streams = []
|
||||
# providing format streams breaks Clipious client
|
||||
#format_streams.append(adaptive_formats[0])
|
||||
#format_streams.append(adaptive_formats[1])
|
||||
|
||||
#'''
|
||||
response = {
|
||||
@@ -335,7 +361,7 @@ def videos(data):
|
||||
"liveNow": live_now,
|
||||
"isPostLiveDvr": post_live_dvr,
|
||||
"isUpcoming": is_upcoming,
|
||||
"dashUrl": "/dash/not/implemented/", # not implemented
|
||||
"dashUrl": ythdd_globals.config['general']['public_facing_url'] + "/dash/not/implemented/", # not implemented
|
||||
"premiereTimestamp": premiere_timestamp,
|
||||
|
||||
"hlsUrl": hls_url,
|
||||
@@ -418,10 +444,10 @@ def lookup(data):
|
||||
case _:
|
||||
incrementBadRequests()
|
||||
return notImplemented(data)
|
||||
elif data[0] == 'ggpht':
|
||||
elif data[0] in ('ggpht', 'vi'):
|
||||
# for some reason the Materialous client
|
||||
# keeps making requests to these
|
||||
if data[1] == 'ggpht':
|
||||
# and FreeTube keep making requests to these
|
||||
if data[1] in ('ggpht', 'vi'):
|
||||
return redirect('/' + "/".join(data[1:]))
|
||||
return redirect('/' + "/".join(data[0:]))
|
||||
else:
|
||||
@@ -432,7 +458,7 @@ def lookup(data):
|
||||
return stats()
|
||||
elif data[0] == "streams":
|
||||
return streams()
|
||||
elif data[0] == 'ggpht':
|
||||
elif data[0] in ('ggpht', 'vi'):
|
||||
return redirect('/' + "/".join(data[0:]))
|
||||
else:
|
||||
incrementBadRequests()
|
||||
|
||||
Reference in New Issue
Block a user