Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

API Info v2 #157

Merged
merged 11 commits into from Jun 3, 2017
Merged

API Info v2 #157

merged 11 commits into from Jun 3, 2017

Conversation

kyamiko
Copy link
Contributor

@kyamiko kyamiko commented May 21, 2017

Added API Info query.

As close to API Upload v2 behavior as possible.

if torrent.user and (viewer == torrent.user or viewer.is_moderator):
submitter = torrent.user.username

files = ''
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just default to a {} - mixing types is not nice for API users.

matchHASH = re.match(INFO_HASH_PATTERN, torrent_id_or_hash)

if matchID:
torrent = models.Torrent.by_id(torrent_id_or_hash)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

torrent_id_or_hash is a str here - Torrent.id is an int.

@@ -1,7 +1,7 @@
"""Add uploader_ip column to torrents table.

Revision ID: 3001f79b7722
Revises:
Revises:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this touched?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kyamiko You should revert that change, IMO

'url': flask.url_for('view_torrent', torrent_id=torrent.id, _external=True),
'id': torrent.id,
'name': torrent.display_name,
'hash': torrent.info_hash.hex(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

INFO_HASH_PATTERN = '^[0-9a-fA-F]{40}$' # INFO_HASH as string


@api_blueprint.route('/v2/info/<torrent_id_or_hash>', methods=['GET'])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't have to be v2, does it? We never really had a proper v1 api set.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No it doesn't. I tried to keep it as close to /v2/upload as possible.

if torrent.deleted and not (viewer and viewer.is_superadmin):
return flask.jsonify({'errors': ['Query was not a valid id or hash.']}), 400

submitter = 'anonymous'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is a name, I'd make it Anonymous - or possibly, since /user/Anonymous can exist, None

'hash': torrent.info_hash.hex(),
'magnet': torrent.magnet_uri,
'main_category': torrent.main_category.name,
'sub_category': torrent.sub_category.name,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could add the category IDs as well, separately or as N_N.


info_group = parser.add_argument_group('Info options')

info_group.add_argument('-q', '--query', required=True, help='Torrent by id or hash Required.')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use a positional for this, because there's nothing else you can input - the main point is the ID/hash, just like in upload you have to have a torrent.

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.query.lower().strip()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is moot and if it has to be done, it should be done on the server.

matchID = re.match(ID_PATTERN, api_query)
matchHASH = re.match(INFO_HASH_PATTERN, api_query)

if (not matchID) and (not matchHASH):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if not (matchID or matchHASH):

print(r.text)
else:
try:
response = r.json()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no point in parsing the JSON if we're not even prettyprinting it. Just print r.text.

Copy link
Contributor

@TheAMM TheAMM left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left some notes (hopefully this'll work)

Copy link
Contributor

@TheAMM TheAMM left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As asked.

info_group = parser.add_argument_group('Info options')

info_group.add_argument('-q', '--query', required=True, help='Torrent by id or hash Required.')
parser.add_argument('-q', '--query', required=True, help='Torrent by id or hash Required.')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could still be just a query with a metavar='ID_OR_HASH' - it's the only required argument for the script, so don't make the user type a useless -q. It's clear enough what the argument is.

INFO_HASH_PATTERN = '^[0-9a-fA-F]{40}$' # INFO_HASH as string


@api_blueprint.route('/v2/info/<torrent_id_or_hash>', methods=['GET'])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make this just a /info/, no need for v2.

'information': torrent.information,
'description': torrent.description,
'stats': {'seeders': torrent.stats.seed_count, 'leechers': torrent.stats.leech_count, 'downloads': torrent.stats.download_count},
'filelist': files
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On another look, this could just be files, clean and simple - it's not really a list either.

'id': torrent.id,
'name': torrent.display_name,
'hash_b32': torrent.info_hash_as_b32, #as used in magnet uri
'hash_hex': torrent.info_hash.hex(), #as shown in torrent client
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We still have a property for this, but I suppose world doesn't end at that.

SUKEBEI_HOST = 'https://sukebei.nyaa.si'

API_BASE = '/api'
API_INFO = API_BASE + '/v2/info'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remember to change this as well if you change the route.

@TheAMM
Copy link
Contributor

TheAMM commented May 24, 2017

Also, do rebase or otherwise resolve the merge conflict.

@@ -335,7 +335,11 @@ def v2_api_info(torrent_id_or_hash):
'information': torrent.information,
'description': torrent.description,
'stats': {'seeders': torrent.stats.seed_count, 'leechers': torrent.stats.leech_count, 'downloads': torrent.stats.download_count},
'filelist': files
'files': files,
# reduce torrent flags to 0/1
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why? JSON can do boolean true/false just fine. And they are booleans.

print(r.text)
#print(r.text)

# For printing unicode instead of unicode escape sequences
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the output supposed to be JSON or human readable? pprint will be just rude to the user, since they can't use the raw JSON in another script; pprint does python-repr compatible output (generally).

It's better to implement --raw like the upload api to do print(r.text) and otherwise format the output into something like

#12345: 'Some Torrent Name' uploaded by 'Username' (123Mb)
  Anime / English-translated - (Complete, Trusted)

etc etc.
But, you know, on second thought maybe I'll do all that myself.

# reduce torrent flags to True/False
'is_trusted': True if torrent.trusted else False,
'is_complete': True if torrent.complete else False,
'is_remake': True if torrent.remake else False
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just for the record, you could do a bool(somevalue) call for the same effect.

return_error_messages.extend(error_messages)

return flask.make_response(flask.jsonify({'Failure': return_error_messages}), 400)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this intentional/correct? 'Cause the code is unreachable as is (see the return statement a few lines above for context).

Copy link
Contributor

@sharkykh sharkykh May 30, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code is indented but doesn't seem to be in the correct function.
I think this is It appears to be a remanent of the V1 API route mistakenly re-added in a rebase/pull. But I might be wrong.
Removed in f04c3e1#diff-81eaa857cacfa258a7a0040bbdb1dd7bL113

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that shouldn't be there in that case.

'is_trusted': 1 if torrent.trusted else 0,
'is_complete': 1 if torrent.complete else 0,
'is_remake': 1 if torrent.remake else 0
# reduce torrent flags to True/False
Copy link
Contributor

@TheAMM TheAMM May 30, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I cleaned up the properties (051f3f3) and they will be proper booleans (not just truthy), so just do torrent.trusted.

print('Info request failed:', errors)
exit(1)
else:
print("Torrent #{} '{}' uploaded by '{}' ({} bytes) {} / {}\n{}".format(rj['id'], rj['name'], rj['submitter'], rj['filesize'], rj['main_category'], rj['sub_category'], rj['magnet']))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Much better!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well now, would you look at that... I didn't know that was possible.
You can refrence the object's elements from the format replacement fields instead of adding each element to the format function.
https://docs.python.org/2/library/string.html#format-string-syntax

>>> t = {}
>>> t = {'one': 1, 'two': 2}
>>> '{0[one]}'.format(t)
'1'
>>>

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or even better, unpack the dictionary.

>>> '{one}'.format(**{'one': 1, 'two': 2})
'1'

Copy link
Contributor

@sharkykh sharkykh May 30, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@shm0o
I believe there's a vformat function (not sure about the name) so you can use it directly on the dictionary (no packing then unpacking) (Irrelevant)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On this PR, I'll take what I get.

@sharkykh
Copy link
Contributor

@@ -19,6 +19,7 @@


def upgrade():

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This too. There's just no reason for this file to be modified.

@kyamiko
Copy link
Contributor Author

kyamiko commented May 31, 2017

The lint script only applies to files in ./nyaa, not ./utils. This should be fixed in a different commit.

ReimuHakurei pushed a commit that referenced this pull request Jun 1, 2017
* Fix PEP8 E301 on nyaa/models.py

* Add utils/ to lint checker

* Run of lint.sh + manual fixes

As suggested #157 (comment)

* Fix backwards tick in README

* Updated script

* Update Travis config
conn_group = parser.add_argument_group('Connection options')

conn_group.add_argument('-s', '--sukebei', default=False,
action='store_true', help='Upload to sukebei.nyaa.si')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'Upload to sukebei.nyaa.si' is wrong.

@@ -0,0 +1,98 @@
#!/usr/bin/env python3
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not a v2 of the info api script, this is the first one. Plain api_info.py should be fine.

conn_group.add_argument('-p', '--password', help='Password')
conn_group.add_argument('--host', help='Select another api host (for debugging purposes)')

parser.add_argument('-q', '--query', required=True, help='Torrent by id or hash Required.')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should still be just a plain parser.add_argument('hash_or_id', help='Torrent ID or hash (40-char hex string)')

@TheAMM
Copy link
Contributor

TheAMM commented Jun 3, 2017

Well, lessee if this squash will work out

@TheAMM TheAMM merged commit 570a06b into nyaadevs:master Jun 3, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
4 participants