mirror of
https://gitlab.com/SIGBUS/nyaa.git
synced 2024-12-22 09:50:00 +00:00
parent
abfb5c178e
commit
570a06bd9e
|
@ -1,7 +1,7 @@
|
||||||
"""Add uploader_ip column to torrents table.
|
"""Add uploader_ip column to torrents table.
|
||||||
|
|
||||||
Revision ID: 3001f79b7722
|
Revision ID: 3001f79b7722
|
||||||
Revises:
|
Revises:
|
||||||
Create Date: 2017-05-21 18:01:35.472717
|
Create Date: 2017-05-21 18:01:35.472717
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
@ -19,6 +19,7 @@ TABLE_PREFIXES = ('nyaa', 'sukebei')
|
||||||
|
|
||||||
|
|
||||||
def upgrade():
|
def upgrade():
|
||||||
|
|
||||||
for prefix in TABLE_PREFIXES:
|
for prefix in TABLE_PREFIXES:
|
||||||
op.add_column(prefix + '_torrents', sa.Column('uploader_ip', sa.Binary(), nullable=True))
|
op.add_column(prefix + '_torrents', sa.Column('uploader_ip', sa.Binary(), nullable=True))
|
||||||
# ### end Alembic commands ###
|
# ### end Alembic commands ###
|
||||||
|
|
|
@ -12,6 +12,9 @@ from nyaa import routes
|
||||||
import functools
|
import functools
|
||||||
import json
|
import json
|
||||||
import os.path
|
import os.path
|
||||||
|
import re
|
||||||
|
|
||||||
|
import binascii
|
||||||
|
|
||||||
api_blueprint = flask.Blueprint('api', __name__)
|
api_blueprint = flask.Blueprint('api', __name__)
|
||||||
|
|
||||||
|
@ -254,3 +257,75 @@ def ghetto_import():
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
return 'success'
|
return 'success'
|
||||||
|
|
||||||
|
|
||||||
|
# ####################################### INFO #######################################
|
||||||
|
ID_PATTERN = '^[1-9][0-9]*$'
|
||||||
|
INFO_HASH_PATTERN = '^[0-9a-fA-F]{40}$' # INFO_HASH as string
|
||||||
|
|
||||||
|
|
||||||
|
@api_blueprint.route('/info/<torrent_id_or_hash>', methods=['GET'])
|
||||||
|
@basic_auth_user
|
||||||
|
@api_require_user
|
||||||
|
def v2_api_info(torrent_id_or_hash):
|
||||||
|
torrent_id_or_hash = torrent_id_or_hash.lower().strip()
|
||||||
|
|
||||||
|
matchID = re.match(ID_PATTERN, torrent_id_or_hash)
|
||||||
|
matchHASH = re.match(INFO_HASH_PATTERN, torrent_id_or_hash)
|
||||||
|
|
||||||
|
torrent = None
|
||||||
|
|
||||||
|
if matchID:
|
||||||
|
torrent = models.Torrent.by_id(int(torrent_id_or_hash))
|
||||||
|
elif matchHASH:
|
||||||
|
# Convert the string representation of a torrent hash back into a binary representation
|
||||||
|
a2b_hash = binascii.unhexlify(torrent_id_or_hash)
|
||||||
|
torrent = models.Torrent.by_info_hash(a2b_hash)
|
||||||
|
else:
|
||||||
|
return flask.jsonify({'errors': ['Query was not a valid id or hash.']}), 400
|
||||||
|
|
||||||
|
viewer = flask.g.user
|
||||||
|
|
||||||
|
if not torrent:
|
||||||
|
return flask.jsonify({'errors': ['Query was not a valid id or hash.']}), 400
|
||||||
|
|
||||||
|
# Only allow admins see deleted torrents
|
||||||
|
if torrent.deleted and not (viewer and viewer.is_superadmin):
|
||||||
|
return flask.jsonify({'errors': ['Query was not a valid id or hash.']}), 400
|
||||||
|
|
||||||
|
submitter = None
|
||||||
|
if not torrent.anonymous and torrent.user:
|
||||||
|
submitter = torrent.user.username
|
||||||
|
if torrent.user and (viewer == torrent.user or viewer.is_moderator):
|
||||||
|
submitter = torrent.user.username
|
||||||
|
|
||||||
|
files = {}
|
||||||
|
if torrent.filelist:
|
||||||
|
files = json.loads(torrent.filelist.filelist_blob.decode('utf-8'))
|
||||||
|
|
||||||
|
# Create a response dict with relevant data
|
||||||
|
torrent_metadata = {
|
||||||
|
'submitter': submitter,
|
||||||
|
'url': flask.url_for('view_torrent', torrent_id=torrent.id, _external=True),
|
||||||
|
'id': torrent.id,
|
||||||
|
'name': torrent.display_name,
|
||||||
|
'creation_date': torrent.created_time.strftime('%Y-%m-%d %H:%M UTC'),
|
||||||
|
'hash_b32': torrent.info_hash_as_b32, # as used in magnet uri
|
||||||
|
'hash_hex': torrent.info_hash_as_hex, # .hex(), #as shown in torrent client
|
||||||
|
'magnet': torrent.magnet_uri,
|
||||||
|
'main_category': torrent.main_category.name,
|
||||||
|
'main_category_id': torrent.main_category.id,
|
||||||
|
'sub_category': torrent.sub_category.name,
|
||||||
|
'sub_category_id': torrent.sub_category.id,
|
||||||
|
'information': torrent.information,
|
||||||
|
'description': torrent.description,
|
||||||
|
'stats': {'seeders': torrent.stats.seed_count, 'leechers': torrent.stats.leech_count, 'downloads': torrent.stats.download_count},
|
||||||
|
'filesize': torrent.filesize,
|
||||||
|
'files': files,
|
||||||
|
# reduce torrent flags to True/False
|
||||||
|
'is_trusted': torrent.trusted,
|
||||||
|
'is_complete': torrent.complete,
|
||||||
|
'is_remake': torrent.remake
|
||||||
|
}
|
||||||
|
|
||||||
|
return flask.jsonify(torrent_metadata), 200
|
||||||
|
|
97
utils/api_info.py
Normal file
97
utils/api_info.py
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import re
|
||||||
|
import os
|
||||||
|
import argparse
|
||||||
|
import requests
|
||||||
|
import json
|
||||||
|
from pprint import pprint
|
||||||
|
|
||||||
|
NYAA_HOST = 'https://nyaa.si'
|
||||||
|
SUKEBEI_HOST = 'https://sukebei.nyaa.si'
|
||||||
|
|
||||||
|
API_BASE = '/api'
|
||||||
|
API_INFO = API_BASE + '/info'
|
||||||
|
|
||||||
|
ID_PATTERN = '^[1-9][0-9]*$'
|
||||||
|
INFO_HASH_PATTERN = '^[0-9a-fA-F]{40}$'
|
||||||
|
|
||||||
|
environment_epillog = '''You may also provide environment variables NYAA_API_HOST, NYAA_API_USERNAME and NYAA_API_PASSWORD for connection info.'''
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description='Query torrent info on Nyaa.si', epilog=environment_epillog)
|
||||||
|
|
||||||
|
conn_group = parser.add_argument_group('Connection options')
|
||||||
|
|
||||||
|
conn_group.add_argument('-s', '--sukebei', default=False,
|
||||||
|
action='store_true', help='Query torrent info on sukebei.Nyaa.si')
|
||||||
|
|
||||||
|
conn_group.add_argument('-u', '--user', help='Username or email')
|
||||||
|
conn_group.add_argument('-p', '--password', help='Password')
|
||||||
|
conn_group.add_argument('--host', help='Select another api host (for debugging purposes)')
|
||||||
|
|
||||||
|
parser.add_argument('hash_or_id', help='Torrent by id or hash Required.')
|
||||||
|
|
||||||
|
parser.add_argument('--raw', default=False, action='store_true',
|
||||||
|
help='Print only raw response (JSON)')
|
||||||
|
|
||||||
|
|
||||||
|
def easy_file_size(filesize):
|
||||||
|
for prefix in ['B', 'KiB', 'MiB', 'GiB', 'TiB']:
|
||||||
|
if filesize < 1024.0:
|
||||||
|
return '{0:.1f} {1}'.format(filesize, prefix)
|
||||||
|
filesize = filesize / 1024.0
|
||||||
|
return '{0:.1f} {1}'.format(filesize, prefix)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
# Use debug host from args or environment, if set
|
||||||
|
debug_host = args.host or os.getenv('NYAA_API_HOST')
|
||||||
|
api_host = (debug_host or (args.sukebei and SUKEBEI_HOST or NYAA_HOST)).rstrip('/')
|
||||||
|
|
||||||
|
api_query = args.hash_or_id.lower().strip()
|
||||||
|
|
||||||
|
# Verify query is either a valid id or valid hash
|
||||||
|
matchID = re.match(ID_PATTERN, api_query)
|
||||||
|
matchHASH = re.match(INFO_HASH_PATTERN, api_query)
|
||||||
|
|
||||||
|
if not (matchID or matchHASH):
|
||||||
|
raise Exception('Query was not a valid id or valid hash.')
|
||||||
|
|
||||||
|
api_info_url = api_host + API_INFO + '/' + api_query
|
||||||
|
|
||||||
|
api_username = args.user or os.getenv('NYAA_API_USERNAME')
|
||||||
|
api_password = args.password or os.getenv('NYAA_API_PASSWORD')
|
||||||
|
|
||||||
|
if not (api_username and api_password):
|
||||||
|
raise Exception('No authorization found from arguments or environment variables.')
|
||||||
|
|
||||||
|
auth = (api_username, api_password)
|
||||||
|
|
||||||
|
# Go!
|
||||||
|
r = requests.get(api_info_url, auth=auth)
|
||||||
|
|
||||||
|
if args.raw:
|
||||||
|
print(r.text)
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
rj = r.json()
|
||||||
|
except ValueError:
|
||||||
|
print('Bad response:')
|
||||||
|
print(r.text)
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
errors = rj.get('errors')
|
||||||
|
|
||||||
|
if errors:
|
||||||
|
print('Info request failed:', errors)
|
||||||
|
exit(1)
|
||||||
|
else:
|
||||||
|
rj['filesize'] = easy_file_size(rj['filesize'])
|
||||||
|
rj['is_trusted'] = 'Yes' if rj['is_trusted'] else 'No'
|
||||||
|
rj['is_complete'] = 'Yes' if rj['is_complete'] else 'No'
|
||||||
|
rj['is_remake'] = 'Yes' if rj['is_remake'] else 'No'
|
||||||
|
print("Torrent #{} '{}' uploaded by '{}' ({}) (Created on: {}) ({} - {}) (Trusted: {}, Complete: {}, Remake: {})\n{}".format(
|
||||||
|
rj['id'], rj['name'], rj['submitter'], rj['filesize'], rj['creation_date'], rj['main_category'], rj['sub_category'], rj['is_trusted'], rj['is_complete'], rj['is_remake'], rj['magnet']))
|
Loading…
Reference in a new issue