diff --git a/ythdd_api_v1.py b/ythdd_api_v1.py index 62ab976..4024074 100644 --- a/ythdd_api_v1.py +++ b/ythdd_api_v1.py @@ -31,22 +31,34 @@ def stats(): def hot(data): #print(data) + # if we are given not enough data to work with, return bad request. + # example: + # hot related videoId + # 0. 1. 2. [No. of argument] if len(data) <= 2: incrementBadRequests() return 400, f'error: bad request. supply required arguments.', [] + + # check our first argument match data[1]: + + # if it can be handled by yt_dlp, use yt_dlp case "video" | "channel" | "handle" | "playlist": url_lookup = {'video': 'https://www.youtube.com/watch?v=', 'channel': 'https://www.youtube.com/channel/', 'handle': 'https://www.youtube.com/@', 'playlist': 'https://www.youtube.com/playlist?list='} comment_count = "" + # require 3 arguments (if using "c" or "nc") if len(data) <= 3: incrementBadRequests() return 400, f'error: bad request. supply required arguments.', [] + # comment settings should be either Comments, No Comments, or Limited Comments if data[2] not in ("c", "nc", "lc"): incrementBadRequests() return notImplemented(data) + # require 4 arguments (when using "lc" we need to know the number of comments to retrieve) if data[2] == "lc" and len(data) <= 4: incrementBadRequests() return 400, f'error: bad request. limited comments (lc) requires an extra argument specifying amount of comments.', [] + # check if the additional "lc" argument is a number elif data[2] == "lc": try: comment_count = str(int(data[3])) @@ -54,6 +66,7 @@ def hot(data): incrementBadRequests() return 400, f'error: bad request. {data[3]} is not a number.', [] videoId = data[4] + # if the user didn't choose Limited Comments, then the videoId must be the fourth (zero included) argument else: videoId = data[3] @@ -61,19 +74,29 @@ def hot(data): incrementBadRequests() return 400, f'error: bad request. wrong videoId: {videoId} is {len(videoId)} characters long, but should be 11.', [] + # assume we want to get the comments getcomments = True + # unless we're given "nc" if data[2] == "nc": getcomments = False + # try to get the data try: started = time.time() extracted_dict = ythdd_extractor.extract(url_lookup[data[1]] + videoId, getcomments=getcomments, maxcomments=comment_count) extracted_dict["took"] = time.time() - started return 200, "OK", extracted_dict except Exception as e: + # shrink yt_dlp's unnecessarily long error message + # example: \u001b[0;31mERROR:\u001b[0m [youtube] videoIdABCD: Sign in to confirm your age. This video may be inappropriate for some users. + # TODO: perhaps implement proper error handling? so that we don't need this hacky solution incrementBadRequests() - return 400, f'error: failed to get "{videoId}" ({data[2]})', {'error_msg': str(e)} + error_msg = str(e).replace("\u001b[0;31mERROR:\u001b[0m [youtube] " + videoId + ": ", "") # yt_dlp error message with color codes + return 400, f'error: failed to get "{videoId}" ({data[2]})', {'error_msg': error_msg} + + # if some functionality is not supported or buried deep within yt_dlp, as is the case with "related videos" feed, use our own (naive) approach case "related": + # no logic needed for additional arguments here videoId = data[2] if len(videoId) != 11: # videoId sanity check incrementBadRequests() @@ -81,10 +104,16 @@ def hot(data): started = time.time() try: + # try to actually get the data extracted_related = ythdd_extractor.related('https://www.youtube.com/watch?v=' + videoId) extracted_related['took'] = time.time() - started return 200, "OK", extracted_related + except KeyError: + # instead of throwing error at age restricted videos for not having "related videos" feed, + # return an empty feed + return 200, "OK", {'secondaryResults': {'results': []}, 'took': time.time() - started} except Exception as e: + # general exception handler incrementBadRequests() return 400, f'error: unknown error while parsing {videoId}: {e}', [] @@ -92,41 +121,6 @@ def hot(data): incrementBadRequests() return notImplemented(data) - - ''' - if data[1] not in ("video", "channel", "handle", "playlist", "related"): - incrementBadRequests() - return notImplemented(data) - if data[2] not in ("c", "nc", "lc"): # comments, no comments, limited comments - incrementBadRequests() - return notImplemented(data) - if data[2] == "lc": - if len(data) <= 4: - incrementBadRequests() - return 400, f'error: bad request. limited comments (lc) requires an extra argument specifying amount of comments.', [] - try: - comment_count = str(int(data[3])) - except: - incrementBadRequests() - return 400, f'error: bad request. {data[3]} is not a number.', [] - videoId = data[4] - else: - videoId = data[3] - try: - url_lookup = {'video': 'https://www.youtube.com/watch?v=', 'channel': 'https://www.youtube.com/channel/', 'handle': 'https://www.youtube.com/@', 'playlist': 'https://www.youtube.com/playlist?list='} - if data[2] == "nc": - getcomments = False - else: - getcomments = True - started = int(time.time()) - extracted_dict = ythdd_extractor.extract(url_lookup[data[1]] + videoId, getcomments=getcomments, maxcomments=comment_count) - extracted_dict["took"] = int(time.time()) - started - return 200, "OK", extracted_dict - except Exception as e: - incrementBadRequests() - return 400, f'error: failed to get "{videoId}" ({data[2]}). {e}', [] - ''' - def lookup(data): match data[0]: case 'stats':