diff --git a/nyaa/api_handler.py b/nyaa/api_handler.py index 665511b..1ca6625 100644 --- a/nyaa/api_handler.py +++ b/nyaa/api_handler.py @@ -6,6 +6,9 @@ from nyaa import models, forms from nyaa import bencode, backend, utils from nyaa import torrents +# For _create_upload_category_choices +from nyaa import routes + import functools import json import os.path @@ -42,87 +45,7 @@ def api_require_user(f): return decorator -def validate_user(upload_request): - auth_info = None - try: - if 'auth_info' in upload_request.files: - auth_info = json.loads(upload_request.files['auth_info'].read().decode('utf-8')) - if 'username' not in auth_info.keys() or 'password' not in auth_info.keys(): - return False, None, None - - username = auth_info['username'] - password = auth_info['password'] - user = models.User.by_username(username) - - if not user: - user = models.User.by_email(username) - - if not user or password != user.password_hash or \ - user.status == models.UserStatusType.INACTIVE: - return False, None, None - - return True, user, None - else: - return False, None, None - - except Exception as e: - return False, None, e - - -def _create_upload_category_choices(): - ''' Turns categories in the database into a list of (id, name)s ''' - choices = [('', '[Select a category]')] - for main_cat in models.MainCategory.query.order_by(models.MainCategory.id): - choices.append((main_cat.id_as_string, main_cat.name, True)) - for sub_cat in main_cat.sub_categories: - choices.append((sub_cat.id_as_string, ' - ' + sub_cat.name)) - return choices - - # #################################### API ROUTES #################################### -def api_upload(upload_request, user): - form_info = None - try: - form_info = json.loads(upload_request.files['torrent_info'].read().decode('utf-8')) - - form_info_as_dict = [] - for k, v in form_info.items(): - if k in ['is_anonymous', 'is_hidden', 'is_remake', 'is_complete', 'is_trusted']: - if v: - form_info_as_dict.append((k, v)) - else: - form_info_as_dict.append((k, v)) - # Hack for while v1 is still being used: default trusted to true - if not [x for x in form_info_as_dict if x[0] == 'is_trusted']: - form_info_as_dict.append(('is_trusted', True)) - form_info = ImmutableMultiDict(form_info_as_dict) - except Exception as e: - return flask.make_response(flask.jsonify( - {'Failure': ['Invalid data. See HELP in api_uploader.py']}), 400) - - try: - torrent_file = upload_request.files['torrent_file'] - torrent_file = ImmutableMultiDict([('torrent_file', torrent_file)]) - except Exception as e: - return flask.make_response(flask.jsonify( - {'Failure': ['No torrent file was attached.']}), 400) - - form = forms.UploadForm(CombinedMultiDict((torrent_file, form_info)), meta={'csrf': False}) - form.category.choices = _create_upload_category_choices() - - if upload_request.method == 'POST' and form.validate(): - torrent = backend.handle_torrent_upload(form, user, True) - - return flask.make_response(flask.jsonify({'Success': int('{0}'.format(torrent.id))}), 200) - else: - return_error_messages = [] - for error_name, error_messages in form.errors.items(): - return_error_messages.extend(error_messages) - - return flask.make_response(flask.jsonify({'Failure': return_error_messages}), 400) - -# V2 below - # Map UploadForm fields to API keys UPLOAD_API_FORM_KEYMAP = { @@ -150,6 +73,7 @@ UPLOAD_API_DEFAULTS = { } +@api_blueprint.route('/upload', methods=['POST']) @api_blueprint.route('/v2/upload', methods=['POST']) @basic_auth_user @api_require_user @@ -170,7 +94,7 @@ def v2_api_upload(): # Flask-WTF (very helpfully!!) automatically grabs the request form, so force a None formdata upload_form = forms.UploadForm(None, data=mapped_dict, meta={'csrf': False}) - upload_form.category.choices = _create_upload_category_choices() + upload_form.category.choices = routes._create_upload_category_choices() if upload_form.validate(): torrent = backend.handle_torrent_upload(upload_form, flask.g.user) diff --git a/nyaa/routes.py b/nyaa/routes.py index d7340cb..cc2c508 100644 --- a/nyaa/routes.py +++ b/nyaa/routes.py @@ -133,9 +133,6 @@ def get_category_id_map(): # Routes start here # -app.register_blueprint(api_handler.api_blueprint, url_prefix='/api') - - def chain_get(source, *args): ''' Tries to return values from source by the given keys. Returns None if none match. @@ -767,10 +764,5 @@ def site_help(): # #################################### API ROUTES #################################### -@app.route('/api/upload', methods=['POST']) -def api_upload(): - is_valid_user, user, debug = api_handler.validate_user(flask.request) - if not is_valid_user: - return flask.make_response(flask.jsonify({"Failure": "Invalid username or password."}), 400) - api_response = api_handler.api_upload(flask.request, user) - return api_response + +app.register_blueprint(api_handler.api_blueprint, url_prefix='/api') diff --git a/nyaa/templates/home.html b/nyaa/templates/home.html index 95d7f28..0e5a06c 100644 --- a/nyaa/templates/home.html +++ b/nyaa/templates/home.html @@ -3,8 +3,8 @@ {% block body %}
-

5/21 Update: We've updated our upload API to v2 (v1 still works). See documentation here.

-

5/17 Update: We've added faster and more accurate search! In addition to your typical keyword search in both English and other languages, you can also now use powerful operators +

2017-05-22 Update: We've updated our upload API to v2 (v1 is now disabled!). See documentation here.

+

2017-05-17 Update: We've added faster and more accurate search! In addition to your typical keyword search in both English and other languages, you can also now use powerful operators like clockwork planet -horrible or commie|horrible|cartel yowamushi to search. For all supported operators, please click here. More features are coming soon!


We welcome you to provide feedback at #nyaa-dev@irc.rizon.net

Our GitHub: https://github.com/nyaadevs - creating issues for features and faults is recommendable!

diff --git a/utils/api_uploader.py b/utils/api_uploader.py deleted file mode 100644 index 5119685..0000000 --- a/utils/api_uploader.py +++ /dev/null @@ -1,137 +0,0 @@ -# Uploads a single torrent file -# Works on nyaa.si and sukebei.nyaa.si - -# Consider using api_uploader_v2.py instead -# It has a nice command line interface - -import json -import requests - -''' -The POST payload to the api endpoint (/api/upload) should be multipart/form-data containing three fields - -'auth_info': file containing "{ - 'username': str, - 'password': str -}", - -'torrent_info': { - 'category': str, # see below - 'display_name': str, # optional - 'information': str, - 'description': str, - 'is_anonymous': boolean, - 'is_hidden': boolean, - 'is_remake': boolean, - 'is_complete': boolean, - 'is_trusted': boolean #optional -}, - -'torrent_file': multi part file format - - -A successful request should return {'Success': int(torrent_id)} -A failed request should return {'Failure': ["Failure 1", "Failure 2"...]]} - -''' - -# ########################################### HELP ############################################ -# ################################# CATEGORIES MUST BE EXACT ################################## -''' -# Nyaa categories only for now, but api still works for sukebei - -Anime - Anime - AMV : '1_1' - Anime - English : '1_2' - Anime - Non-English : '1_3' - Anime - Raw : '1_4' -Audio - Lossless : '2_1' - Lossy : '2_2' -Literature - Literature - English-translated : '3_1' - Literature - Non-English : '3_2' - Literature - Non-English-Translated : '3_3' - Literature - Raw : '3_4' -Live Action - Live Action - English-translated : '4_1' - Live Action - Idol/Promotional Video : '4_2' - Live Action - Non-English-translated : '4_3' - Live Action - Raw : '4_4' -Pictures - Pictures - Graphics : '5_1' - Pictures - Photos : '5_2' -Software - Software - Applications : '6_1' - Software - Games : '6_2' -''' -# ################################# CATEGORIES MUST BE EXACT ################################## - -# ###################################### EXAMPLE REQUEST ###################################### -''' -# Required -username = '' -password = '' -torrent_file = '/path/to/my.torrent' -category = '1_2' - -#Optional -display_name = '' -information = 'API HOWTO' -description = 'Visit #nyaa-dev@irc.rizon.net' - -# Defaults to False, change to True to set -is_anonymous : False, -is_hidden : False, -is_remake : False, -is_complete : False -''' - - -# ######################################## CHANGE HERE ######################################## - -url = 'https://nyaa.si/api/upload' # or 'https://sukebei.nyaa.si/api/upload' or 'http://127.0.0.1:5500/api/upload' - -# Required -username = '' -password = '' -torrent_file = '' -category = '' - -# Optional -display_name = '' -information = '' -description = '' -is_anonymous = False -is_hidden = False -is_remake = False -is_complete = False -is_trusted = True # This will only work if a user is trusted, otherwise the option is ignored - -auth_info = { - 'username' : username, - 'password' : password -} - -metadata={ - 'category' : category, - 'display_name' : display_name, - 'information' : information, - 'description' : description, - 'is_anonymous' : is_anonymous, - 'is_hidden' : is_hidden, - 'is_remake' : is_remake, - 'is_complete' : is_complete, - 'is_trusted' : is_trusted -} - -files = { - 'auth_info' : (json.dumps(auth_info)), - 'torrent_info' : (json.dumps(metadata)), - 'torrent_file' : ('{0}'.format(torrent_file), open(torrent_file, 'rb'), 'application/octet-stream') -} - -response = requests.post(url, files=files) -json_response = response.json() -print(json_response) -# A successful request should print {'Success': int(torrent_id)} diff --git a/utils/api_uploader_v2.py b/utils/api_uploader_v2.py index 60d3c36..6956d81 100755 --- a/utils/api_uploader_v2.py +++ b/utils/api_uploader_v2.py @@ -9,7 +9,7 @@ NYAA_HOST = 'https://nyaa.si' SUKEBEI_HOST = 'https://sukebei.nyaa.si' API_BASE = '/api' -API_UPLOAD = API_BASE + '/v2/upload' +API_UPLOAD = API_BASE + '/upload' NYAA_CATS = '''1_1 - Anime - AMV 1_2 - Anime - English