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):
|
def epochToDate(epoch):
|
||||||
return strftime('%Y-%m-%dT%H:%M:%SZ', gmtime(epoch))
|
return strftime('%Y-%m-%dT%H:%M:%SZ', gmtime(epoch))
|
||||||
|
|
||||||
|
def dateToEpoch(date: str):
|
||||||
|
return datetime.datetime.fromisoformat(date).timestamp()
|
||||||
|
|
||||||
def trending():
|
def trending():
|
||||||
return send(200, [{}])
|
return send(200, [{}])
|
||||||
|
|
||||||
@@ -105,7 +108,6 @@ def genThumbs(videoId: str):
|
|||||||
height = x['height']
|
height = x['height']
|
||||||
quality = x['quality']
|
quality = x['quality']
|
||||||
url = ythdd_globals.config['general']['public_facing_url'] + 'vi/' + videoId + '/' + x['url'] + '.jpg'
|
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})
|
result.append({'quality': quality, 'url': url, 'width': width, 'height': height})
|
||||||
|
|
||||||
return result
|
return result
|
||||||
@@ -113,6 +115,7 @@ 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
|
||||||
|
|
||||||
for x in range(len(data)):
|
for x in range(len(data)):
|
||||||
|
|
||||||
@@ -122,6 +125,9 @@ def rebuildFormats(data):
|
|||||||
except:
|
except:
|
||||||
isVideo = 1
|
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]['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]['index'] = str(data[x]['indexRange']['start']) + "-" + str(data[x]['indexRange']['end'])
|
||||||
result[x]['bitrate'] = str(data[x]['averageBitrate'])
|
result[x]['bitrate'] = str(data[x]['averageBitrate'])
|
||||||
@@ -157,8 +163,32 @@ def rebuildFormats(data):
|
|||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
#if data[x]['itag'] <= 80: # won't be triggered for iOS player as it has no progressive streams
|
# we assume here that a stream with the highest bitrate must be a video stream
|
||||||
# formatStreams.append(result[x])
|
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
|
return result, formatStreams
|
||||||
|
|
||||||
@@ -188,7 +218,7 @@ def videos(data):
|
|||||||
title = safeTraverse(video_details, ['title'], default=video_id)
|
title = safeTraverse(video_details, ['title'], default=video_id)
|
||||||
views = int(safeTraverse(video_details, ['viewCount'], default=0))
|
views = int(safeTraverse(video_details, ['viewCount'], default=0))
|
||||||
length = int(safeTraverse(video_details, ['lengthSeconds'], default=1))
|
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)
|
published_date = epochToDate(published)
|
||||||
premiere_timestamp = safeTraverse(microformat, ['liveBroadcastDetails', 'startTimestamp'], default=0) # let's ignore the nitty gritty for the time being
|
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)
|
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():
|
for x in magnitude.keys():
|
||||||
if x in likes_text:
|
if x in likes_text:
|
||||||
likes *= magnitude[x]
|
likes *= magnitude[x]
|
||||||
description = safeTraverse(microformat, ['description', 'simpleText'], default="\n(ythdd: error ocurred, failed to retrieve 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: error occurred, failed to retrieve short description)")
|
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
|
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={})
|
metadata = safeTraverse(video_secondary_renderer, ['metadataRowContainer', 'metadataRowContainerRenderer', 'rows'], default={})
|
||||||
@@ -274,13 +304,14 @@ def videos(data):
|
|||||||
|
|
||||||
hls_url = safeTraverse(idata, ['stage1', 'streamingData', 'hlsManifestUrl'], default="")
|
hls_url = safeTraverse(idata, ['stage1', 'streamingData', 'hlsManifestUrl'], default="")
|
||||||
adaptive_formats = safeTraverse(idata, ['stage1', 'streamingData', 'adaptiveFormats'], default=[])
|
adaptive_formats = safeTraverse(idata, ['stage1', 'streamingData', 'adaptiveFormats'], default=[])
|
||||||
|
format_streams = []
|
||||||
adaptive_formats, format_streams = rebuildFormats(adaptive_formats)
|
adaptive_formats, format_streams = rebuildFormats(adaptive_formats)
|
||||||
|
|
||||||
if live_now:
|
if live_now:
|
||||||
video_type = "livestream"
|
video_type = "livestream"
|
||||||
elif premiere_timestamp:
|
elif premiere_timestamp:
|
||||||
video_type = "scheduled"
|
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:
|
else:
|
||||||
video_type = "video"
|
video_type = "video"
|
||||||
|
|
||||||
@@ -289,11 +320,6 @@ def videos(data):
|
|||||||
premium = True
|
premium = True
|
||||||
# TODO: detect paywalled patron-only videos
|
# 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 = {
|
response = {
|
||||||
@@ -335,7 +361,7 @@ def videos(data):
|
|||||||
"liveNow": live_now,
|
"liveNow": live_now,
|
||||||
"isPostLiveDvr": post_live_dvr,
|
"isPostLiveDvr": post_live_dvr,
|
||||||
"isUpcoming": is_upcoming,
|
"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,
|
"premiereTimestamp": premiere_timestamp,
|
||||||
|
|
||||||
"hlsUrl": hls_url,
|
"hlsUrl": hls_url,
|
||||||
@@ -418,10 +444,10 @@ def lookup(data):
|
|||||||
case _:
|
case _:
|
||||||
incrementBadRequests()
|
incrementBadRequests()
|
||||||
return notImplemented(data)
|
return notImplemented(data)
|
||||||
elif data[0] == 'ggpht':
|
elif data[0] in ('ggpht', 'vi'):
|
||||||
# for some reason the Materialous client
|
# for some reason the Materialous client
|
||||||
# keeps making requests to these
|
# and FreeTube keep making requests to these
|
||||||
if data[1] == 'ggpht':
|
if data[1] in ('ggpht', 'vi'):
|
||||||
return redirect('/' + "/".join(data[1:]))
|
return redirect('/' + "/".join(data[1:]))
|
||||||
return redirect('/' + "/".join(data[0:]))
|
return redirect('/' + "/".join(data[0:]))
|
||||||
else:
|
else:
|
||||||
@@ -432,7 +458,7 @@ def lookup(data):
|
|||||||
return stats()
|
return stats()
|
||||||
elif data[0] == "streams":
|
elif data[0] == "streams":
|
||||||
return streams()
|
return streams()
|
||||||
elif data[0] == 'ggpht':
|
elif data[0] in ('ggpht', 'vi'):
|
||||||
return redirect('/' + "/".join(data[0:]))
|
return redirect('/' + "/".join(data[0:]))
|
||||||
else:
|
else:
|
||||||
incrementBadRequests()
|
incrementBadRequests()
|
||||||
|
|||||||
Reference in New Issue
Block a user