Compare commits
3 Commits
bedcaff7c0
...
509e81aafa
| Author | SHA1 | Date | |
|---|---|---|---|
| 509e81aafa | |||
| caa9e0c2b1 | |||
| 873abbd413 |
11
views.py
11
views.py
@@ -54,4 +54,15 @@ def gucProxy(received_request):
|
||||
guc.raw.decode_content = True
|
||||
response = Response(guc.raw, mimetype=guc.headers['content-type'], status=guc.status_code)
|
||||
|
||||
return response
|
||||
|
||||
def imgProxy(received_request):
|
||||
|
||||
# will proxy /img/no_thumbnail.jpg
|
||||
prefix = "https://i.ytimg.com/"
|
||||
|
||||
thumbnail = requests.get(prefix + "img/" + received_request, headers=ythdd_globals.getHeaders(caller='proxy'), stream=True)
|
||||
thumbnail.raw.decode_content = True
|
||||
response = Response(thumbnail.raw, mimetype=thumbnail.headers['content-type'], status=thumbnail.status_code)
|
||||
|
||||
return response
|
||||
1
ythdd.py
1
ythdd.py
@@ -66,6 +66,7 @@ def setup():
|
||||
app.add_url_rule('/vi/<path:received_request>', view_func=views.thumbnailProxy)
|
||||
app.add_url_rule('/ggpht/<path:received_request>', view_func=views.ggphtProxy)
|
||||
app.add_url_rule('/guc/<path:received_request>', view_func=views.gucProxy)
|
||||
app.add_url_rule('/img/<path:received_request>', view_func=views.imgProxy)
|
||||
db = ythdd_db.initDB(app, config)
|
||||
|
||||
with app.app_context():
|
||||
|
||||
@@ -81,7 +81,7 @@ stage3_headers = {
|
||||
"Sec-Fetch-Mode": "navigate",
|
||||
"Content-Type": "application/json",
|
||||
"X-Youtube-Client-Name": "1",
|
||||
"X-Youtube-Client-Version": "2.20250919.00.00",
|
||||
"X-Youtube-Client-Version": "2.20250923.01.00",
|
||||
"Origin": "https://www.youtube.com",
|
||||
"Accept-Encoding": "gzip, deflate, br",
|
||||
"Cookie": "PREF=hl=en&tz=UTC; SOCS=CAI"
|
||||
@@ -93,7 +93,7 @@ stage3_body = {
|
||||
"client":
|
||||
{
|
||||
"clientName": "WEB",
|
||||
"clientVersion": "2.20250919.00.00",
|
||||
"clientVersion": "2.20250923.01.00",
|
||||
"hl": "en",
|
||||
"timeZone": "UTC",
|
||||
"utcOffsetMinutes": 0
|
||||
@@ -113,7 +113,7 @@ web_context_dict = {
|
||||
'deviceModel': '',
|
||||
'userAgent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:143.0) Gecko/20100101 Firefox/143.0,gzip(gfe)',
|
||||
'clientName': 'WEB',
|
||||
'clientVersion': '2.20250919.00.00',
|
||||
'clientVersion': '2.20250923.01.00',
|
||||
'osName': 'Windows',
|
||||
'osVersion': '10.0',
|
||||
'screenPixelDensity': 2,
|
||||
|
||||
@@ -84,26 +84,64 @@ def getHeaders(caller="proxy"):
|
||||
|
||||
return headers
|
||||
|
||||
def translateLinks(link):
|
||||
def translateLinks(link: str, remove_params: bool = True):
|
||||
|
||||
link = link.replace("https://i.ytimg.com/", config['general']['public_facing_url'])
|
||||
link = link.replace("https://yt3.ggpht.com/", config['general']['public_facing_url'] + "ggpht/")
|
||||
link = link.replace("https://yt3.googleusercontent.com/", config['general']['public_facing_url'] + "guc/")
|
||||
|
||||
# try to remove tracking params
|
||||
if remove_params and "?" in link:
|
||||
link = link[:link.find("?")]
|
||||
|
||||
return link
|
||||
|
||||
def getUptime():
|
||||
return int(time.time()) - starttime
|
||||
|
||||
def safeTraverse(obj: dict, path: list, default=None, quiet: bool = False):
|
||||
"""
|
||||
Traverse dynamic objects with fallback to default values
|
||||
|
||||
This function can take an Ellipsis as part of traversal path,
|
||||
meaning that it will return the object from the list
|
||||
that contains the next key. This has been introduced
|
||||
so that no matter which object in a list holds the relevant
|
||||
model, it will find it (meaning no assumptions are necessary).
|
||||
Kepp in mind that only one ellipsis at a time is supported,
|
||||
thus ["some_key", ..., ..., "some_other_key"] won't work.
|
||||
|
||||
:param obj: Traversed object
|
||||
:type obj: dict
|
||||
:param path: Path which shall be traversed
|
||||
:type path: list
|
||||
:param default: Default value returned on failure
|
||||
:type default: any, None by default
|
||||
:param quiet: Quiet flag
|
||||
:type quiet: bool
|
||||
"""
|
||||
result = obj
|
||||
try:
|
||||
for x in path:
|
||||
#print(f"traversing {result} with respect to {x}")
|
||||
result = result[x]
|
||||
# for every item in path and its position
|
||||
for pos, iterable_key in enumerate(path):
|
||||
# if the key is not an ellipsis, traverse it
|
||||
if iterable_key is not Ellipsis:
|
||||
result = result[iterable_key]
|
||||
# if it is an ellipsis, and there is another key beside it
|
||||
elif pos < len(path) - 1:
|
||||
# then iterate through all of the list contents
|
||||
for list_content in result:
|
||||
# in search of the next traversal key
|
||||
if path[pos + 1] in list_content:
|
||||
result = list_content
|
||||
# show an error message if ellipsis is used incorrectly
|
||||
else:
|
||||
print("error(safeTraverse): Traversal path can't end with an Ellipsis!")
|
||||
raise TypeError()
|
||||
# handle exceptions
|
||||
except (KeyError, TypeError, IndexError):
|
||||
result = default
|
||||
if not quiet: print(f"error reading: {' -> '.join(path)} - returning: {default}")
|
||||
if not quiet: print(f"error reading: {' -> '.join([str(x) for x in path])} - returning: {default}")
|
||||
finally:
|
||||
return result
|
||||
|
||||
|
||||
@@ -23,11 +23,10 @@ import ythdd_struct_parser
|
||||
# [✓] /vi/videoIdXXXX/maxresdefault.jpg (todo: add a fallback for 404s)
|
||||
# [✓] /api/v1/search?q=... (videos and playlists)
|
||||
# [✓] /api/v1/search/suggestions?q=...&pq=...
|
||||
# [✓] /api/v1/channels/:ucid
|
||||
# [✓] /api/v1/channels/:ucid/videos, shorts, playlists
|
||||
# [✓] /api/v1/channel/:ucid
|
||||
# [✓] /api/v1/channel/:ucid/videos, shorts, playlists, streams
|
||||
# [✓] /api/v1/comments/:videoid?continuation=...
|
||||
# [✓] /api/v1/videos/videoIdXXXX
|
||||
# [X] /api/v1/channels/:ucid/streams
|
||||
# [X] /api/v1/playlists/:plid
|
||||
# [X] /api/v1/storyboards/:videoIdXXXX
|
||||
# [*] /api/v1/auth/subscriptions (stub? db?)
|
||||
@@ -35,6 +34,7 @@ import ythdd_struct_parser
|
||||
# [*] /api/v1/auth/playlists (stub? db?)
|
||||
|
||||
DEFAULT_AVATAR = "https://yt3.ggpht.com/a/default-user=s176-c-k-c0x00ffffff-no-rj"
|
||||
DEFAULT_VIDEO = "https://i.ytimg.com/img/no_thumbnail.jpg" # todo: replace this with a custom, local asset
|
||||
|
||||
def incrementBadRequests():
|
||||
ythdd_globals.apiFailedRequests += 1
|
||||
|
||||
Reference in New Issue
Block a user