feat: search pagination
adds support for getting past the first page of search results
This commit is contained in:
@@ -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,
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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
|
||||||
Reference in New Issue
Block a user