feat: search pagination

adds support for getting past the first page of search results
This commit is contained in:
2025-10-03 01:16:56 +02:00
parent 7eb4452fec
commit 468795a7a2
3 changed files with 27 additions and 5 deletions

View File

@@ -357,14 +357,19 @@ def browseChannel(ucid: str, params: str = None, ctoken: str = None):
return response_json return response_json
def WEBextractSearchResults(search_query: str) -> list: def WEBextractSearchResults(search_query: str, page: int) -> list:
# Posts a search request to innertube API # Posts a search request to innertube API
# and processes only the relevant part (the actual results) # and processes only the relevant part (the actual results)
if search_query is None: if search_query is None:
return [] return []
web_context = makeWebContext({"query": search_query}) additional_context = {"query": search_query}
if page is not None:
params = ythdd_proto.produceSearchParams(page)
additional_context["params"] = params
web_context = makeWebContext(additional_context)
response = requests.post('https://www.youtube.com/youtubei/v1/search', response = requests.post('https://www.youtube.com/youtubei/v1/search',
params={"prettyPrint": False}, params={"prettyPrint": False},
headers=stage2_headers, headers=stage2_headers,

View File

@@ -23,7 +23,7 @@ import ythdd_struct_parser
# [✓] /api/v1/stats (stats()) # [✓] /api/v1/stats (stats())
# [✓] /streams/dQw4w9WgXcQ (does nothing) # [✓] /streams/dQw4w9WgXcQ (does nothing)
# [✓] /vi/:videoIdXXXX/maxresdefault.jpg # [✓] /vi/:videoIdXXXX/maxresdefault.jpg
# [✓] /api/v1/search?q=... (videos and playlists) # [✓] /api/v1/search?q=... (videos and playlists), pagination
# [✓] /api/v1/search/suggestions?q=...&pq=... # [✓] /api/v1/search/suggestions?q=...&pq=...
# [✓] /api/v1/channel/:ucid # [✓] /api/v1/channel/:ucid
# [✓] /api/v1/channel/:ucid/videos, shorts, playlists, streams # [✓] /api/v1/channel/:ucid/videos, shorts, playlists, streams
@@ -38,6 +38,7 @@ import ythdd_struct_parser
# [X] /api/v1/videos/:videoIdXXXX does not depend on yt-dlp and offloads stream retrieval elsewhere (making initial response fast) # [X] /api/v1/videos/:videoIdXXXX does not depend on yt-dlp and offloads stream retrieval elsewhere (making initial response fast)
# [X] /api/v1/manifest/:videoIdXXXX (above is prerequisite) # [X] /api/v1/manifest/:videoIdXXXX (above is prerequisite)
# [X] rewrite the awful lookup logic # [X] rewrite the awful lookup logic
# [X] /api/v1/search?q=... complex filtering options (https://gitea.invidious.io/iv-org/invidious/src/branch/master/src/invidious/search/filters.cr)
# ---------- # ----------
# IDEAS: # IDEAS:
# [*] /api/v1/popular returns last requested videos by the IP (serving as multi-device history?) # [*] /api/v1/popular returns last requested videos by the IP (serving as multi-device history?)
@@ -579,14 +580,19 @@ def search(data, req):
# ignore paginated requests as we do nothing with the continuation token # ignore paginated requests as we do nothing with the continuation token
page = req.args.get('page') page = req.args.get('page')
if page is not None and page != '1': if page is not None and page != '1':
return send(404, []) try:
page = int(page)
except:
return send(400, {"error": "Wrong page."})
else:
page = None # when page is "1"
if (data[-2].lower() != "search" or data[-1].lower() != "") and data[-1].lower() != "search": if (data[-2].lower() != "search" or data[-1].lower() != "") and data[-1].lower() != "search":
previous_query = req.args.get('pq') previous_query = req.args.get('pq')
suggestions = ythdd_extractor.WEBgetSearchSuggestions(search_query, previous_query) suggestions = ythdd_extractor.WEBgetSearchSuggestions(search_query, previous_query)
return send(200, suggestions) return send(200, suggestions)
results = ythdd_extractor.WEBextractSearchResults(search_query) results = ythdd_extractor.WEBextractSearchResults(search_query, page)
results_list = [] results_list = []
for entry in results: for entry in results:

View File

@@ -83,3 +83,14 @@ def producePlaylistContinuation(plid: str, offset: int = 0) -> str:
b64_ctoken = bbpbToB64(bbpb_dicts, urlsafe=True, padding=True) b64_ctoken = bbpbToB64(bbpb_dicts, urlsafe=True, padding=True)
return b64_ctoken return b64_ctoken
def produceSearchParams(page: int = 1) -> str:
msge = {
"9:int": 20 * (page - 1), # pagination
"30:int": 1 # no self-harm censorship
}
bbpb_dicts = fdictToBbpb(msge)
b64_params = bbpbToB64(bbpb_dicts, urlsafe=True, padding=True)
return b64_params