From c655463078e4bd295ffdb2349fbff9f080589934 Mon Sep 17 00:00:00 2001 From: TheAMM Date: Thu, 31 Oct 2019 22:56:47 +0200 Subject: [PATCH] search: Allow specifying multiple usernames... ...by repeating &u=user in the GET parameters. No UX for this yet. Reworks the RSS URL generator to fit with duplicated keys. --- nyaa/search.py | 64 ++++++++++++++------------------------ nyaa/templates/layout.html | 4 +-- nyaa/views/main.py | 24 ++++++++------ nyaa/views/users.py | 4 +-- 4 files changed, 41 insertions(+), 55 deletions(-) diff --git a/nyaa/search.py b/nyaa/search.py index f6932af..7ce5445 100644 --- a/nyaa/search.py +++ b/nyaa/search.py @@ -3,6 +3,7 @@ import re import shlex import threading import time +from urllib.parse import quote, urlencode import flask from flask_sqlalchemy import Pagination @@ -60,17 +61,18 @@ def _get_index_name(column): return table_indexes.get(column.name) -def _generate_query_string(term, category, filter, user): - params = {} +def _generate_query_string(term, category, filter, user_names): + params = [] if term: - params['q'] = str(term) + params.append(('q', str(term))) if category: - params['c'] = str(category) + params.append(('c', str(category))) if filter: - params['f'] = str(filter) - if user: - params['u'] = str(user) - return params + params.append(('f', str(filter))) + for name in user_names: + params.append(('u', name)) + + return urlencode(params, quote_via=quote) # For preprocessing ES search terms in _parse_es_search_terms @@ -181,7 +183,7 @@ def _parse_es_search_terms(search, search_terms): return search -def search_elastic(term='', user=None, sort='id', order='desc', +def search_elastic(term='', user_ids=None, sort='id', order='desc', category='0_0', quality_filter='0', page=1, rss=False, admin=False, logged_in_user=None, per_page=75, max_search_results=1000): @@ -261,17 +263,9 @@ def search_elastic(term='', user=None, sort='id', order='desc', if not main_category: flask.abort(400) - # This might be useless since we validate users - # before coming into this method, but just to be safe... - if user: - user = models.User.by_id(user) - if not user: - flask.abort(404) - user = user.id - same_user = False if logged_in_user: - same_user = user == logged_in_user.id + same_user = len(user_ids) == 1 and logged_in_user.id in user_ids s = Search(using=es_client, index=app.config.get('ES_INDEX_NAME')) # todo, sukebei prefix @@ -281,8 +275,8 @@ def search_elastic(term='', user=None, sort='id', order='desc', s = _parse_es_search_terms(s, term) # User view (/user/username) - if user: - s = s.filter('term', uploader_id=user) + if user_ids: + s = s.filter('terms', uploader_id=user_ids) if not admin: # Hide all DELETED torrents if regular user @@ -370,7 +364,7 @@ class QueryPairCaller(object): return wrapper -def search_db(term='', user=None, sort='id', order='desc', category='0_0', +def search_db(term='', user_ids=None, sort='id', order='desc', category='0_0', quality_filter='0', page=1, rss=False, admin=False, logged_in_user=None, per_page=75): if page > 4294967295: @@ -380,7 +374,7 @@ def search_db(term='', user=None, sort='id', order='desc', category='0_0', same_user = False if logged_in_user: - same_user = logged_in_user.id == user + same_user = len(user_ids) == 1 and logged_in_user.id in user_ids # Logged in users should always be able to view their full listing. if same_user or admin: @@ -426,12 +420,6 @@ def search_db(term='', user=None, sort='id', order='desc', category='0_0', if filter_tuple is sentinel: flask.abort(400) - if user: - user = models.User.by_id(user) - if not user: - flask.abort(404) - user = user.id - main_category = None sub_category = None main_cat_id = 0 @@ -469,8 +457,8 @@ def search_db(term='', user=None, sort='id', order='desc', category='0_0', qpc = QueryPairCaller(query, count_query) # User view (/user/username) - if user: - qpc.filter(models.Torrent.uploader_id == user) + if user_ids: + qpc.filter(models.Torrent.uploader_id.in_(user_ids)) if not admin: # Hide all DELETED torrents if regular user @@ -610,7 +598,7 @@ BAKED_FILTER_LAMBDAS = { } -def search_db_baked(term='', user=None, sort='id', order='desc', category='0_0', +def search_db_baked(term='', user_ids=None, sort='id', order='desc', category='0_0', quality_filter='0', page=1, rss=False, admin=False, logged_in_user=None, per_page=75): if page > 4294967295: @@ -631,12 +619,6 @@ def search_db_baked(term='', user=None, sort='id', order='desc', category='0_0', if filter_lambda is sentinel: flask.abort(400) - if user: - user = models.User.by_id(user) - if not user: - flask.abort(404) - user = user.id - main_cat_id = 0 sub_cat_id = 0 @@ -664,7 +646,7 @@ def search_db_baked(term='', user=None, sort='id', order='desc', category='0_0', same_user = False if logged_in_user: - same_user = logged_in_user.id == user + same_user = len(user_ids) == 1 and logged_in_user.id in user_ids if term: query = bakery(lambda session: session.query(models.TorrentNameSearch)) @@ -685,9 +667,9 @@ def search_db_baked(term='', user=None, sort='id', order='desc', category='0_0', baked_params = {} # User view (/user/username) - if user: - qpc += lambda q: q.filter(models.Torrent.uploader_id == bp('user')) - baked_params['user'] = user + if user_ids: + qpc += lambda q: q.filter(models.Torrent.uploader_id.in_(bp('user_ids', expanding=True))) + baked_params['user_ids'] = user_ids if not admin: # Hide all DELETED torrents if regular user diff --git a/nyaa/templates/layout.html b/nyaa/templates/layout.html index 029b1e3..30179b6 100644 --- a/nyaa/templates/layout.html +++ b/nyaa/templates/layout.html @@ -9,7 +9,7 @@ - + @@ -93,7 +93,7 @@
  • Trusted
  • -
  • RSS
  • +
  • RSS
  • {% if config.SITE_FLAVOR == 'nyaa' %}
  • Fap
  • {% elif config.SITE_FLAVOR == 'sukebei' %} diff --git a/nyaa/views/main.py b/nyaa/views/main.py index d6f9a72..d6c0f47 100644 --- a/nyaa/views/main.py +++ b/nyaa/views/main.py @@ -76,7 +76,7 @@ def home(rss): category = chain_get(req_args, 'c', 'cats') quality_filter = chain_get(req_args, 'f', 'filter') - user_name = chain_get(req_args, 'u', 'user') + user_names = set(req_args.getlist('u') + req_args.getlist('user')) page_number = chain_get(req_args, 'p', 'page', 'offset') try: page_number = max(1, int(page_number)) @@ -88,12 +88,16 @@ def home(rss): results_per_page = app.config.get('RESULTS_PER_PAGE', DEFAULT_PER_PAGE) - user_id = None - if user_name: - user = models.User.by_username(user_name) - if not user: + user_ids = [] + if user_names: + for name in user_names: + user = models.User.by_username(name) + if user: + user_ids.append(user.id) + # If we have usernames to look up but find none, 404 + if not user_ids: flask.abort(404) - user_id = user.id + user_ids = tuple(user_ids) special_results = { 'first_word_user': None, @@ -101,7 +105,7 @@ def home(rss): 'infohash_torrent': None } # Add advanced features to searches (but not RSS or user searches) - if search_term and not render_as_rss and not user_id: + if search_term and not render_as_rss and not user_ids: # Check if the first word of the search is an existing user user_word_match = re.match(r'^([a-zA-Z0-9_-]+) *(.*|$)', search_term) if user_word_match: @@ -122,7 +126,7 @@ def home(rss): special_results['infohash_torrent'] = matched_torrent query_args = { - 'user': user_id, + 'user_ids': user_ids, 'sort': sort_key or 'id', 'order': sort_order or 'desc', 'category': category or '0_0', @@ -166,7 +170,7 @@ def home(rss): use_elastic=True, magnet_links=use_magnet_links) else: rss_query_string = _generate_query_string( - search_term, category, quality_filter, user_name) + search_term, category, quality_filter, user_names) max_results = min(max_search_results, query_results['hits']['total']) # change p= argument to whatever you change page_parameter to or pagination breaks pagination = Pagination(p=query_args['page'], per_page=results_per_page, @@ -195,7 +199,7 @@ def home(rss): return render_rss('Home', query, use_elastic=False, magnet_links=use_magnet_links) else: rss_query_string = _generate_query_string( - search_term, category, quality_filter, user_name) + search_term, category, quality_filter, user_names) # Use elastic is always false here because we only hit this section # if we're browsing without a search term (which means we default to DB) # or if ES is disabled diff --git a/nyaa/views/users.py b/nyaa/views/users.py index decf3f6..1bcd460 100644 --- a/nyaa/views/users.py +++ b/nyaa/views/users.py @@ -130,7 +130,7 @@ def view_user(user_name): query_args = { 'term': search_term or '', - 'user': user.id, + 'user_ids': (user.id,), # Tuple! 'sort': sort_key or 'id', 'order': sort_order or 'desc', 'category': category or '0_0', @@ -146,7 +146,7 @@ def view_user(user_name): query_args['admin'] = True # Use elastic search for term searching - rss_query_string = _generate_query_string(search_term, category, quality_filter, user_name) + rss_query_string = _generate_query_string(search_term, category, quality_filter, [user_name]) use_elastic = app.config.get('USE_ELASTIC_SEARCH') if use_elastic and search_term: query_args['term'] = search_term