diff --git a/pirate/data.py b/pirate/data.py
index dd3fe1f..192bd01 100644
--- a/pirate/data.py
+++ b/pirate/data.py
@@ -5,7 +5,8 @@ import pkgutil
def get_resource(filename):
return pkgutil.get_data(__package__, 'data/' + filename)
-version = '0.3.7'
+
+version = '0.4.0'
categories = json.loads(get_resource('categories.json').decode())
sorts = json.loads(get_resource('sorts.json').decode())
@@ -14,5 +15,5 @@ blacklist = set(json.loads(get_resource('blacklist.json').decode()))
default_headers = {'User-Agent': 'pirate get'}
default_timeout = 10
-default_mirror = 'https://thepiratebay.org/'
+default_mirror = 'https://apibay.org'
mirror_list = 'https://proxybay.bz/list.txt'
diff --git a/pirate/data/sorts.json b/pirate/data/sorts.json
index d5f5ce1..72d8bd1 100644
--- a/pirate/data/sorts.json
+++ b/pirate/data/sorts.json
@@ -1,15 +1,15 @@
{
-"TitleDsc": 1,
-"TitleAsc": 2,
-"DateDsc": 3,
-"DateAsc": 4,
-"SizeDsc": 5,
-"SizeAsc": 6,
-"SeedersDsc": 7,
-"SeedersAsc": 8,
-"LeechersDsc": 9,
-"LeechersAsc": 10,
-"CategoryDsc": 13,
-"CategoryAsc": 14,
-"Default": 99
-}
\ No newline at end of file
+"TitleDsc": [1, "name", true],
+"TitleAsc": [2, "name", false],
+"DateDsc": [3, "raw_uploaded", true],
+"DateAsc": [4, "raw_uploaded", false],
+"SizeDsc": [5, "raw_size", true],
+"SizeAsc": [6, "raw_size", false],
+"SeedersDsc": [7, "seeders", true],
+"SeedersAsc": [8, "seeders", false],
+"LeechersDsc": [9, "leechers", true],
+"LeechersAsc": [10, "leechers", false],
+"CategoryDsc": [13, "category", true],
+"CategoryAsc": [14, "category", false],
+"Default": [99, "seeders", true]
+}
diff --git a/pirate/pirate.py b/pirate/pirate.py
index 59c8824..dae16e2 100755
--- a/pirate/pirate.py
+++ b/pirate/pirate.py
@@ -8,7 +8,7 @@ import socket
import urllib.request as request
import urllib.error
import builtins
-
+import json
import webbrowser
import pirate.data
@@ -42,6 +42,7 @@ def parse_config_file(text):
config.set('Misc', 'transmission-port', '') # for backward compatibility
config.set('Misc', 'colors', 'true')
config.set('Misc', 'mirror', pirate.data.default_mirror)
+ config.set('Misc', 'timeout', pirate.data.default_timeout)
config.read_string(text)
@@ -122,31 +123,33 @@ def parse_torrent_command(l):
def parse_args(args_in):
parser = argparse.ArgumentParser(
description='finds and downloads torrents from the Pirate Bay')
- parser.add_argument('-b', dest='browse',
+ parser.add_argument('-b', '--browse',
action='store_true',
help='display in Browse mode')
- parser.add_argument('search', metavar='search',
+ parser.add_argument('search',
nargs='*', help='term to search for')
- parser.add_argument('-c', dest='category', metavar='category',
+ parser.add_argument('-c', '--category',
help='specify a category to search', default='All')
- parser.add_argument('-s', dest='sort', metavar='sort',
+ parser.add_argument('-s', '--sort',
help='specify a sort option', default='SeedersDsc')
- parser.add_argument('-R', dest='recent', action='store_true',
- help='torrents uploaded in the last 48hours.'
+ parser.add_argument('-R', '--recent',
+ action='store_true',
+ help='torrents uploaded in the last 48hours. '
'*ignored in searches*')
- parser.add_argument('-l', dest='list_categories',
+ parser.add_argument('-l', '--list-categories',
action='store_true',
help='list categories')
- parser.add_argument('--list_sorts', dest='list_sorts',
+ parser.add_argument('--list-sorts', '--list_sorts',
action='store_true',
- help='list Sortable Types')
+ help='list types by which results can be sorted')
+ parser.add_argument('-p', '--pages',
+ default=1, type=int,
+ help='the number of pages to fetch. '
+ '(only used with --recent)')
parser.add_argument('-L', '--local', dest='database',
help='a csv file containing the Pirate Bay database '
'downloaded from '
'https://thepiratebay.org/static/dump/csv/')
- parser.add_argument('-p', dest='pages', default=1, type=int,
- help='the number of pages to fetch '
- "(doesn't work with --local)")
parser.add_argument('-0', dest='first',
action='store_true',
help='choose the top result')
@@ -182,9 +185,14 @@ def parse_args(args_in):
parser.add_argument('-m', '--mirror',
type=str, nargs='+',
help='the pirate bay mirror(s) to use')
+ parser.add_argument('-z', '--timeout', type=int,
+ help='timeout in seconds for http requests')
parser.add_argument('-v', '--version',
action='store_true',
help='print pirate-get version number')
+ parser.add_argument('-j', '--json',
+ action='store_true',
+ help='print results in JSON format to stdout')
args = parser.parse_args(args_in)
return args
@@ -223,6 +231,9 @@ def combine_configs(config, args):
if not args.mirror:
args.mirror = config.get('Misc', 'mirror').split()
+ if not args.timeout:
+ args.timeout = int(config.get('Misc', 'timeout'))
+
args.transmission_command = ['transmission-remote']
if args.endpoint:
args.transmission_command.append(args.endpoint)
@@ -261,6 +272,7 @@ def combine_configs(config, args):
def connect_mirror(mirror, printer, args):
try:
printer.print('Trying', mirror, end='... ')
+ url = pirate.torrent.find_api(mirror, args.timeout)
results = pirate.torrent.remote(
printer=printer,
pages=args.pages,
@@ -268,7 +280,8 @@ def connect_mirror(mirror, printer, args):
sort=pirate.torrent.parse_sort(printer, args.sort),
mode=args.action,
terms=args.search,
- mirror=mirror)
+ mirror=url,
+ timeout=args.timeout)
except (urllib.error.URLError, socket.timeout, IOError, ValueError) as e:
printer.print('Failed', color='WARN', end=' ')
printer.print('(', e, ')', sep='')
@@ -282,14 +295,14 @@ def search_mirrors(printer, args):
# try default or user mirrors
for mirror in args.mirror:
result = connect_mirror(mirror, printer, args)
- if result:
+ if result is not None:
return result
# download mirror list
try:
req = request.Request(pirate.data.mirror_list,
headers=pirate.data.default_headers)
- f = request.urlopen(req, timeout=pirate.data.default_timeout)
+ f = request.urlopen(req, timeout=args.timeout)
except urllib.error.URLError as e:
raise IOError('Could not fetch mirrors', e.reason)
@@ -304,7 +317,7 @@ def search_mirrors(printer, args):
if mirror in pirate.data.blacklist:
continue
result = connect_mirror(mirror, printer, args)
- if result:
+ if result is not None:
return result
else:
raise IOError('No more available mirrors')
@@ -313,6 +326,13 @@ def search_mirrors(printer, args):
def pirate_main(args):
printer = Printer(args.color)
+ # browse mode needs a specific category
+ if args.browse:
+ if args.category == 'All' or args.category == 0:
+ printer.print('You must select a specific category in browse mode.'
+ ' ("All" is not valid)', color='ERROR')
+ sys.exit(1)
+
# print version
if args.version:
printer.print('pirate-get, version {}'.format(pirate.data.version))
@@ -340,7 +360,7 @@ def pirate_main(args):
cur_color = 'zebra_0'
for key, value in sorted(pirate.data.sorts.items()):
cur_color = 'zebra_0' if cur_color == 'zebra_1' else 'zebra_1'
- printer.print(str(value), '\t', key, sep='', color=cur_color)
+ printer.print(str(value[0]), '\t', key, sep='', color=cur_color)
return
# fetch torrents
@@ -365,7 +385,11 @@ def pirate_main(args):
printer.print('No results')
return
- printer.search_results(results, local=args.source == 'local_tpb')
+ if args.json:
+ print(json.dumps(results))
+ return
+ else:
+ printer.search_results(results, local=args.source == 'local_tpb')
# number of results to pick
if args.first:
@@ -380,13 +404,13 @@ def pirate_main(args):
printer.print("\nSelect links (Type 'h' for more options"
", 'q' to quit)", end='\b', color='alt')
try:
- l = builtins.input(': ')
+ cmd = builtins.input(': ')
except (KeyboardInterrupt, EOFError):
printer.print('\nCancelled.')
return
try:
- code, choices = parse_torrent_command(l)
+ code, choices = parse_torrent_command(cmd)
# Act on option, if supplied
printer.print('')
if code == 'h':
@@ -403,9 +427,9 @@ def pirate_main(args):
printer.print('Bye.', color='alt')
return
elif code == 'd':
- printer.descriptions(choices, results, site)
+ printer.descriptions(choices, results, site, args.timeout)
elif code == 'f':
- printer.file_lists(choices, results, site)
+ printer.file_lists(choices, results, site, args.timeout)
elif code == 'p':
printer.search_results(results)
elif code == 'm':
@@ -415,8 +439,9 @@ def pirate_main(args):
pirate.torrent.copy_magnets(printer, choices, results)
elif code == 't':
pirate.torrent.save_torrents(printer, choices, results,
- args.save_directory)
- elif not l:
+ args.save_directory,
+ args.timeout)
+ elif not cmd:
printer.print('No links entered!', color='WARN')
else:
break
@@ -435,7 +460,8 @@ def pirate_main(args):
if args.output == 'save_torrent_files':
printer.print('Saving selected torrents...')
pirate.torrent.save_torrents(printer, choices,
- results, args.save_directory)
+ results, args.save_directory,
+ args.timeout)
return
for choice in choices:
diff --git a/pirate/print.py b/pirate/print.py
index c7490c2..adc3614 100644
--- a/pirate/print.py
+++ b/pirate/print.py
@@ -1,17 +1,18 @@
import builtins
import re
import gzip
-import urllib.parse as parse
import urllib.request as request
import shutil
+import json
+import sys
import pirate.data
+import pirate.torrent
import colorama
-import veryprettytable
+import veryprettytable as pretty
from io import BytesIO
-from http.cookiejar import CookieJar
class Printer:
@@ -33,10 +34,10 @@ class Printer:
c = color_dict[kwargs.pop('color')]
args = (c + args[0],) + args[1:] + (colorama.Style.RESET_ALL,)
kwargs.pop('color', None)
- return builtins.print(*args, **kwargs)
+ return builtins.print(*args, file=sys.stderr, **kwargs)
else:
kwargs.pop('color', None)
- return builtins.print(*args, **kwargs)
+ return builtins.print(*args, file=sys.stderr, **kwargs)
# TODO: extract the name from the search results
# instead of from the magnet link when possible
@@ -45,14 +46,14 @@ class Printer:
even = True
if local:
- table = veryprettytable.VeryPrettyTable(['LINK', 'DATE', 'SIZE', 'NAME'])
+ table = pretty.VeryPrettyTable(['LINK', 'DATE', 'SIZE', 'NAME'])
table.align['SIZE'] = 'r'
table.align['NAME'] = 'l'
else:
- table = veryprettytable.VeryPrettyTable(['LINK', 'SEED', 'LEECH',
- 'RATIO', 'SIZE',
- 'UPLOAD', 'NAME'])
+ table = pretty.VeryPrettyTable(['LINK', 'SEED', 'LEECH',
+ 'RATIO', 'SIZE',
+ 'UPLOAD', 'NAME'])
table.align['NAME'] = 'l'
table.align['SEED'] = 'r'
table.align['LEECH'] = 'r'
@@ -65,21 +66,15 @@ class Printer:
table.padding_width = 1
for n, result in enumerate(results):
-
- name = re.search(r'dn=([^\&]*)', result['magnet'])
- torrent_name = parse.unquote_plus(name.group(1))
+ torrent_name = result['name']
if local:
- content = [n, result['date'], result['size'], torrent_name[:columns - 42]]
+ content = [n, result['date'], result['size'],
+ torrent_name[:columns - 42]]
else:
- no_seeders = int(result['seeds'])
+ no_seeders = int(result['seeders'])
no_leechers = int(result['leechers'])
- if result['size'] != []:
- size = float(result['size'][0])
- unit = result['size'][1]
- else:
- size = 0
- unit = '???'
+ size = result['size']
date = result['uploaded']
# compute the S/L ratio (Higher is better)
@@ -90,8 +85,7 @@ class Printer:
content = [n, no_seeders, no_leechers,
'{:.1f}'.format(ratio),
- '{:.1f} '.format(size) + unit,
- date, torrent_name[:columns - 50]]
+ size, date, torrent_name[:columns - 50]]
if even or not self.enable_color:
table.add_row(content)
@@ -102,65 +96,60 @@ class Printer:
even = not even
self.print(table)
- def descriptions(self, chosen_links, results, site):
- jar = CookieJar()
- opener = request.build_opener(
- request.HTTPErrorProcessor,
- request.HTTPCookieProcessor(jar))
-
+ def descriptions(self, chosen_links, results, site, timeout):
for link in chosen_links:
- path = '/torrent/%s/' % results[link]['id']
- req = request.Request(site + path,
- headers=pirate.data.default_headers)
+ result = results[link]
+ req = request.Request(
+ site + '/t.php?id=' + str(result['id']),
+ headers=pirate.data.default_headers)
req.add_header('Accept-encoding', 'gzip')
- f = opener.open(req, timeout=pirate.data.default_timeout)
+ f = request.urlopen(req, timeout=timeout)
if f.info().get('Content-Encoding') == 'gzip':
f = gzip.GzipFile(fileobj=BytesIO(f.read()))
- res = f.read().decode('utf-8')
- name = re.search(r'dn=([^\&]*)', results[link]['magnet'])
- torrent_name = parse.unquote(name.group(1)).replace('+', ' ')
- desc = re.search(r'
\s*
(.+?)(?=
)',
- res, re.DOTALL).group(1)
+ res = json.load(f)
# Replace HTML links with markdown style versions
desc = re.sub(r'
]*>(\s*)([^<]+?)(\s*'
- r')', r'\2[\3](\1)\4', desc)
+ r')', r'\2[\3](\1)\4', res['descr'])
- self.print('Description for "%s":' % torrent_name, color='zebra_1')
+ self.print('Description for "{}":'.format(result['name']),
+ color='zebra_1')
self.print(desc, color='zebra_0')
- def file_lists(self, chosen_links, results, site):
- jar = CookieJar()
- opener = request.build_opener(
- request.HTTPErrorProcessor,
- request.HTTPCookieProcessor(jar))
+ def file_lists(self, chosen_links, results, site, timeout):
+ # the API may returns object instead of list
+ def get(obj):
+ try:
+ return obj[0]
+ except KeyError:
+ return obj['0']
for link in chosen_links:
- path = '/ajax_details_filelist.php'
- query = '?id=' + results[link]['id']
- req = request.Request(site + path + query,
- headers=pirate.data.default_headers)
+ result = results[link]
+ req = request.Request(
+ site + '/f.php?id=' + str(result['id']),
+ headers=pirate.data.default_headers)
req.add_header('Accept-encoding', 'gzip')
- f = opener.open(req, timeout=pirate.data.default_timeout)
+ f = request.urlopen(req, timeout=timeout)
if f.info().get('Content-Encoding') == 'gzip':
f = gzip.GzipFile(fileobj=BytesIO(f.read()))
- # TODO: proper html decoding/parsing
- res = f.read().decode('utf-8').replace(' ', ' ')
- if 'File list not available.' in res:
+ res = json.load(f)
+
+ if len(res) == 1 and 'not found' in get(res[0]['name']):
self.print('File list not available.')
return
- files = re.findall(r'
\s*([^<]+?)\s* | \s*([^<]+?)\s*', res)
- name = re.search(r'dn=([^\&]*)', results[link]['magnet'])
- torrent_name = parse.unquote(name.group(1)).replace('+', ' ')
- self.print('Files in "%s":' % torrent_name, color='zebra_1')
+ self.print('Files in {}:'.format(result['name']), color='zebra_1')
cur_color = 'zebra_0'
- for f in files:
- self.print('{0[0]:>11} {0[1]}'.format(f), color=cur_color)
+ for f in res:
+ name = get(f['name'])
+ size = pirate.torrent.pretty_size(int(get(f['size'])))
+ self.print('{:>11} {}'.format(
+ size, name),
+ color=cur_color)
cur_color = 'zebra_0' if cur_color == 'zebra_1' else 'zebra_1'
diff --git a/pirate/torrent.py b/pirate/torrent.py
index defccbc..ec39f77 100644
--- a/pirate/torrent.py
+++ b/pirate/torrent.py
@@ -8,13 +8,10 @@ import urllib.error
import os.path
import pirate.data
+import json
-from bs4 import BeautifulSoup
+from datetime import datetime
from io import BytesIO
-from http.cookiejar import CookieJar
-
-
-parser_regex = r'"(magnet\:\?xt=[^"]*)| | ([^<]+) | '
def parse_category(printer, category):
@@ -36,208 +33,184 @@ def parse_sort(printer, sort):
sort = int(sort)
except ValueError:
pass
- if sort in pirate.data.sorts.values():
- return sort
- elif sort in pirate.data.sorts.keys():
- return pirate.data.sorts[sort]
+ for key, val in pirate.data.sorts.items():
+ if sort == key or sort == val[0]:
+ return val[1:]
else:
printer.print('Invalid sort ignored', color='WARN')
- return 99
+ return pirate.data.sorts['Default'][1:]
-# TODO:
-# * warn users when using a sort in a mode that doesn't accept sorts
-# * warn users when using search terms in a mode
-# that doesn't accept search terms
-# * same with page parameter for top and top48h
-# * warn the user if trying to use a minor category with top48h
-def build_request_path(page, category, sort, mode, terms):
- if mode == 'browse':
- if(category == 0):
- category = 100
- return '/browse/{}/{}/{}'.format(category, page, sort)
- elif mode == 'recent':
- # This is not a typo. There is no / between 48h and the category.
- path = '/top/48h'
- # only major categories can be used with this mode
- if(category == 0):
- return path + 'all'
- else:
- return path + str(category)
- elif mode == 'top':
- path = '/top/'
- if(category == 0):
- return path + 'all'
- else:
- return path + str(category)
- elif mode == 'search':
- query = urllib.parse.quote_plus(' '.join(terms))
- return '/search/{}/{}/{}/{}'.format(query, page, sort, category)
- else:
- raise Exception('Unknown mode.')
-
-
-# this returns a list of dictionaries
-def parse_page(html):
- soup = BeautifulSoup(html, 'html.parser')
- tables = soup.find_all('table', id='searchResult')
- no_results = re.search(r'No hits\. Try adding an asterisk in '
- r'you search phrase\.', html)
-
- # check for a blocked mirror
- if not tables and not no_results:
- # Contradiction - we found no results,
- # but the page didn't say there were no results.
- # The page is probably not actually the pirate bay,
- # so let's try another mirror
- raise IOError('Blocked mirror detected.')
-
- if no_results:
- return []
-
- # handle ads disguised as fake result tables
- for table in tables:
- results = parse_table(table)
- if results:
- break
- else:
- raise IOError('Mirror does not contain magnets.')
-
- return results
-
-
-def parse_table(table):
+def parse_page(page):
results = []
+ try:
+ data = json.load(page)
+ except json.decoder.JSONDecodeError:
+ raise IOError('invalid JSON in API reply: blocked mirror?')
- # parse the rows one by one (skipping headings)
- for row in table('tr')[1:]:
- # grab info about the row
- row_link = row.find('a', class_='detLink')
- if row_link is None:
- continue
+ if len(data) == 1 and 'No results' in data[0]['name']:
+ return results
- id_ = row_link['href'].split('/')[2]
- seeds, leechers = [i.text for i in row('td')[-2:]]
- magnet_tag = row.find(lambda tag: tag.name == 'a' and
- tag['href'].startswith('magnet'))
- if magnet_tag is None:
- continue
- magnet = magnet_tag['href']
-
- # parse descriptions separately
- description = row.find('font', class_='detDesc').text
- size = re.findall(r'(?<=Size )[0-9.]+\s[KMGT]*[i ]*B',
- description)[0].split()
- uploaded = re.findall(r'(?<=Uploaded ).+(?=\, Size)',
- description)[0]
-
- results.append({
- 'magnet': magnet,
- 'seeds': seeds,
- 'leechers': leechers,
- 'size': size,
- 'uploaded': uploaded,
- 'id': id_
- })
+ for res in data:
+ res['raw_size'] = int(res['size'])
+ res['size'] = pretty_size(int(res['size']))
+ res['magnet'] = build_magnet(res['name'], res['info_hash'])
+ res['info_hash'] = int(res['info_hash'], 16)
+ res['raw_uploaded'] = int(res['added'])
+ res['uploaded'] = pretty_date(res['added'])
+ res['seeders'] = int(res['seeders'])
+ res['leechers'] = int(res['leechers'])
+ res['category'] = int(res['category'])
+ results.append(res)
return results
-def remote(printer, pages, category, sort, mode, terms, mirror):
- res_l = []
+def sort_results(sort, res):
+ key, reverse = sort
+ return sorted(res, key=lambda x: x[key], reverse=reverse)
- if pages < 1:
- raise ValueError('Please provide an integer greater than 0 '
- 'for the number of pages to fetch.')
- # Catch the Ctrl-C exception and exit cleanly
- try:
- jar = CookieJar()
- opener = request.build_opener(
- request.HTTPErrorProcessor,
- request.HTTPCookieProcessor(jar))
+def pretty_size(size):
+ ranges = [('PiB', 1125899906842624),
+ ('TiB', 1099511627776),
+ ('GiB', 1073741824),
+ ('MiB', 1048576),
+ ('KiB', 1024)]
+ for unit, value in ranges:
+ if size >= value:
+ return '{:.1f} {}'.format(size/value, unit)
+ return str(size) + ' B'
- for page in range(pages):
- path = build_request_path(page, category, sort, mode, terms)
- req = request.Request(mirror + path,
- headers=pirate.data.default_headers)
- req.add_header('Accept-encoding', 'gzip')
+def pretty_date(ts):
+ date = datetime.fromtimestamp(int(ts))
+ return date.strftime('%Y-%m-%d %H:%M')
+
+def build_magnet(name, info_hash):
+ return 'magnet:?xt=urn:btih:{}&dn={}'.format(
+ info_hash, parse.quote(name, ''))
+
+
+def build_request_path(mode, page, category, terms):
+ if mode == 'search':
+ query = '/q.php?q={}&cat={}'.format(' '.join(terms), category)
+ elif mode == 'top':
+ cat = 'all' if category == 0 else category
+ query = '/precompiled/data_top100_{}.json'.format(cat)
+ elif mode == 'recent':
+ query = '/precompiled/data_top100_recent_{}.json'.format(page)
+ elif mode == 'browse':
+ if category == 0:
+ raise Exception('You must specify a category')
+ query = '/q.php?q=category:{}'.format(category)
+ else:
+ raise Exception('Invalid mode', mode)
+
+ return parse.quote(query, '?=&/')
+
+
+def remote(printer, pages, category, sort, mode, terms, mirror, timeout):
+ results = []
+ for i in range(1, pages + 1):
+ query = build_request_path(mode, i, category, terms)
+
+ # Catch the Ctrl-C exception and exit cleanly
+ try:
+ req = request.Request(
+ mirror + query,
+ headers=pirate.data.default_headers)
try:
- f = opener.open(req, timeout=pirate.data.default_timeout)
+ f = request.urlopen(req, timeout=timeout)
except urllib.error.URLError as e:
- res = e.fp.read().decode()
- if e.code == 503 and 'cf-browser-verification' in res:
- raise IOError('Cloudflare protected')
raise e
if f.info().get('Content-Encoding') == 'gzip':
f = gzip.GzipFile(fileobj=BytesIO(f.read()))
- res = f.read().decode('utf-8')
+ except KeyboardInterrupt:
+ printer.print('\nCancelled.')
+ sys.exit(0)
- res_l += parse_page(res)
+ results.extend(parse_page(f))
- except KeyboardInterrupt:
- printer.print('\nCancelled.')
- sys.exit(0)
-
- return res_l
+ return sort_results(sort, results)
-def get_torrent(info_hash):
+def find_api(mirror, timeout):
+ # try common paths
+ for path in ['', '/apip', '/api.php?url=']:
+ req = request.Request(mirror + path + '/q.php?q=test&cat=0',
+ headers=pirate.data.default_headers)
+ try:
+ f = request.urlopen(req, timeout=timeout)
+ if f.info().get_content_type() == 'application/json':
+ return mirror + path
+ except urllib.error.URLError as e:
+ res = e.fp.read().decode()
+ if e.code == 503 and 'cf-browser-verification' in res:
+ raise IOError('Cloudflare protected')
+
+ # extract api path from main.js
+ req = request.Request(mirror + '/static/main.js',
+ headers=pirate.data.default_headers)
+ try:
+ f = request.urlopen(req, timeout=timeout)
+ if f.info().get_content_type() == 'application/javascript':
+ match = re.search("var server='([^']+)'", f.read().decode())
+ return mirror + match.group(1)
+ except urllib.error.URLError:
+ raise IOError('API not found: no main.js')
+
+ raise IOError('API not found')
+
+
+def get_torrent(info_hash, timeout):
url = 'http://itorrents.org/torrent/{:X}.torrent'
req = request.Request(url.format(info_hash),
headers=pirate.data.default_headers)
req.add_header('Accept-encoding', 'gzip')
- torrent = request.urlopen(req, timeout=pirate.data.default_timeout)
+ torrent = request.urlopen(req, timeout=timeout)
if torrent.info().get('Content-Encoding') == 'gzip':
torrent = gzip.GzipFile(fileobj=BytesIO(torrent.read()))
return torrent.read()
-def save_torrents(printer, chosen_links, results, folder):
+def save_torrents(printer, chosen_links, results, folder, timeout):
for link in chosen_links:
- magnet = results[link]['magnet']
- name = re.search(r'dn=([^\&]*)', magnet)
- torrent_name = parse.unquote(name.group(1)).replace('+', ' ')
- info_hash = int(re.search(r'btih:([a-f0-9]{40})', magnet).group(1), 16)
- torrent_name = torrent_name.replace('/', '_').replace('\\', '_')
+ result = results[link]
+ torrent_name = result['name'].replace('/', '_').replace('\\', '_')
file = os.path.join(folder, torrent_name + '.torrent')
try:
- torrent = get_torrent(info_hash)
+ torrent = get_torrent(result['info_hash'], timeout)
except urllib.error.HTTPError as e:
printer.print('There is no cached file for this torrent :('
' \nCode: {} - {}'.format(e.code, e.reason),
color='ERROR')
else:
open(file, 'wb').write(torrent)
- printer.print('Saved {:X} in {}'.format(info_hash, file))
+ printer.print('Saved {:X} in {}'.format(result['info_hash'], file))
def save_magnets(printer, chosen_links, results, folder):
for link in chosen_links:
- magnet = results[link]['magnet']
- name = re.search(r'dn=([^\&]*)', magnet)
- torrent_name = parse.unquote(name.group(1)).replace('+', ' ')
- info_hash = int(re.search(r'btih:([a-f0-9]{40})', magnet).group(1), 16)
- torrent_name = torrent_name.replace('/', '_').replace('\\', '_')
+ result = results[link]
+ torrent_name = result['name'].replace('/', '_').replace('\\', '_')
file = os.path.join(folder, torrent_name + '.magnet')
- printer.print('Saved {:X} in {}'.format(info_hash, file))
+ printer.print('Saved {:X} in {}'.format(result['info_hash'], file))
with open(file, 'w') as f:
- f.write(magnet + '\n')
+ f.write(result['magnet'] + '\n')
def copy_magnets(printer, chosen_links, results):
clipboard_text = ''
for link in chosen_links:
- magnet = results[link]['magnet']
- info_hash = int(re.search(r'btih:([a-fA-F0-9]{40})', magnet).group(1), 16)
- clipboard_text += magnet + "\n"
- printer.print('Copying {:X} to clipboard'.format(info_hash))
+ result = results[link]
+ clipboard_text += result['magnet'] + "\n"
+ printer.print('Copying {:X} to clipboard'.format(result['info_hash']))
pyperclip.copy(clipboard_text)
diff --git a/setup.py b/setup.py
index 8330ca1..6ee0fdf 100755
--- a/setup.py
+++ b/setup.py
@@ -18,12 +18,11 @@ if __name__ == '__main__':
author_email='me@viktorstanchev.com',
license='AGPL',
packages=find_packages(),
- package_data={'': ["data/*.json"]},
+ package_data={'': ["data/*.json", "tests/data/*"]},
entry_points={
'console_scripts': ['pirate-get = pirate.pirate:main']
},
install_requires=['colorama>=0.3.3',
- 'beautifulsoup4>=4.4.1',
'veryprettytable>=0.8.1',
'pyperclip>=1.6.2'],
keywords=['torrent', 'magnet', 'download', 'tpb', 'client'],
diff --git a/tests/data/dan_bull_search.html b/tests/data/dan_bull_search.html
deleted file mode 100644
index b7799c8..0000000
--- a/tests/data/dan_bull_search.html
+++ /dev/null
@@ -1,461 +0,0 @@
-
-
-
-
The Pirate Bay - The galaxy's most resilient bittorrent site
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Search results: dan bull Displaying hits from 0 to 15 (approx 15 found)
-
-
-
-
-
-
-
-
-
-
-
-
- Audio
- (Music)
-
- |
-
-
-
- Uploaded 04-04 2014, Size 89.33 MiB, ULed by Capajebo
- |
- 16 |
- 1 |
-
-
-
-
- Audio
- (Other)
-
- |
-
-
-
- Uploaded 03-02 2014, Size 294 MiB, ULed by Vakume
- |
- 4 |
- 0 |
-
-
-
-
- Audio
- (Music)
-
- |
-
-
-
- Uploaded 01-19 2013, Size 54.86 MiB, ULed by blowingfish
- |
- 2 |
- 0 |
-
-
-
-
- Audio
- (Other)
-
- |
-
-
-
- Uploaded 01-21 2010, Size 236.78 MiB, ULed by SuperSaru
- |
- 1 |
- 0 |
-
-
-
-
- Audio
- (Music)
-
- |
-
-
-
- Uploaded 09-02 2014, Size 36.27 MiB, ULed by Bazookus
- |
- 1 |
- 0 |
-
-
-
-
- Audio
- (Music)
-
- |
-
-
-
- Uploaded 09-27 2009, Size 5.51 MiB, ULed by oneanight
- |
- 0 |
- 1 |
-
-
-
-
- Audio
- (Music)
-
- |
-
-
-
- Uploaded 11-29 2009, Size 5.07 MiB, ULed by epiclawl
- |
- 0 |
- 0 |
-
-
-
-
- Audio
- (Music)
-
- |
-
-
-
- Uploaded 11-10 2011, Size 5.34 MiB, ULed by Imperator42
- |
- 0 |
- 0 |
-
-
-
-
- Audio
- (Music)
-
- |
-
-
-
- Uploaded 12-20 2011, Size 4.8 MiB, ULed by lerdie
- |
- 0 |
- 0 |
-
-
-
-
- Audio
- (Music)
-
- |
-
-
-
- Uploaded 12-21 2011, Size 3.4 MiB, ULed by mattdow
- |
- 0 |
- 1 |
-
-
-
-
- Audio
- (Music)
-
- |
-
-
-
- Uploaded 12-21 2011, Size 4.8 MiB, ULed by lerdie
- |
- 0 |
- 1 |
-
-
-
-
- Audio
- (Other)
-
- |
-
-
-
- Uploaded 03-09 2012, Size 60.72 MiB, ULed by oneanight
- |
- 0 |
- 1 |
-
-
-
-
- Audio
- (Music)
-
- |
-
-
-
- Uploaded 10-24 2012, Size 6.29 MiB, ULed by PIRATE300
- |
- 0 |
- 0 |
-
-
-
-
- Audio
- (Music)
-
- |
-
-
-
- Uploaded 11-10 2012, Size 6.41 MiB, ULed by AdpoX10
- |
- 0 |
- 0 |
-
-
-
-
- Audio
- (Other)
-
- |
-
-
-
- Uploaded 01-19 2013, Size 54.87 MiB, ULed by blowingfish
- |
- 0 |
- 1 |
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/tests/data/debian_iso.json b/tests/data/debian_iso.json
new file mode 100644
index 0000000..ed15837
--- /dev/null
+++ b/tests/data/debian_iso.json
@@ -0,0 +1 @@
+[{"id":"6089103","name":"Linux Mint Debian [201012] [ISO] [64-Bit] [geno7744] ","info_hash":"0E270463CD1AD2D856EA89859829BC1E63E3B228","leechers":"1","seeders":"1","num_files":"1","size":"1021560832","username":"geno7744","added":"1294464892","status":"member","category":"303","imdb":""},{"id":"8442441","name":"debian-7.0.0-i386-DVD-1.iso","info_hash":"7E919FB33216E2BC6C31ADE3594A3866D260B9BF","leechers":"0","seeders":"1","num_files":"1","size":"3998007296","username":"mmxx_01","added":"1367797189","status":"member","category":"303","imdb":""},{"id":"3357155","name":"debian_gnu_linux_31r0a_ia64_1_iso","info_hash":"78B281DF90ED64F8E6D13555C9108A54C9472606","leechers":"1","seeders":"0","num_files":"2","size":"4640233753","username":"gigli","added":"1121550810","status":"member","category":"303","imdb":""},{"id":"3441282","name":"Debian 3.1r1 i386 binary No. 1 (of 14) ISO file","info_hash":"A1690B1129AE13E5F01457338F5D06223F8174F1","leechers":"0","seeders":"0","num_files":"1","size":"669585408","username":"PixelHermit","added":"1139313316","status":"member","category":"303","imdb":""},{"id":"3441283","name":"Debian 3.1r1 i386 binary No. 2 (of 14) ISO file","info_hash":"B226D8C4192EB6994FEAF0DBCA04C6126DC7FBC4","leechers":"0","seeders":"0","num_files":"1","size":"671037440","username":"PixelHermit","added":"1139313488","status":"member","category":"303","imdb":""},{"id":"3441284","name":"Debian 3.1r1 i386 binary No. 3 (of 14) ISO file","info_hash":"EAF6853B9438EB757D066BFDE0D13AD9F657F2B1","leechers":"1","seeders":"0","num_files":"1","size":"673679360","username":"PixelHermit","added":"1139313671","status":"member","category":"303","imdb":""},{"id":"3578845","name":"debian-31r4-i386-netinst.iso","info_hash":"D08019524B9FD86A48D31347BFC3714408B09D5B","leechers":"0","seeders":"0","num_files":"1","size":"117620736","username":"keep_patrick","added":"1166383842","status":"member","category":"303","imdb":""},{"id":"3795842","name":"Debian 4.0 ETCH.iso","info_hash":"3BC2C61C420A62DE7442E44698E6AED05A499078","leechers":"0","seeders":"0","num_files":"1","size":"3833810944","username":"lkonti","added":"1188950149","status":"member","category":"303","imdb":""},{"id":"3860812","name":"debian-live-etch-i386-standard.iso","info_hash":"41BA5E3833E9D678DB41673183EC49AD32EA366C","leechers":"0","seeders":"0","num_files":"1","size":"85168128","username":"superunix","added":"1193471893","status":"member","category":"303","imdb":""},{"id":"3872700","name":"debian-kde4beta4-live-cd-i386.iso","info_hash":"2CAAABE42A03A3EFB28F33DE93C2FBB278EAA891","leechers":"0","seeders":"0","num_files":"1","size":"438484992","username":"jknotzke","added":"1194191588","status":"member","category":"303","imdb":""},{"id":"3947454","name":"Debian 4.0 r1 Update.iso","info_hash":"6A52953C8C8C6DAE89D867EB3DEB1C66AC43E7BC","leechers":"0","seeders":"0","num_files":"1","size":"1374822400","username":"shasi420","added":"1198595636","status":"member","category":"303","imdb":""},{"id":"4224808","name":"debian-hamm-source-cs.iso","info_hash":"0ACE55B2D81A56E1139A2E7C070F54B2F82F319B","leechers":"0","seeders":"0","num_files":"1","size":"631373824","username":"geier","added":"1212764102","status":"trusted","category":"303","imdb":""},{"id":"4244856","name":"debian-40r3-i386-netinst.iso","info_hash":"8527DE9E12B4196A1C443853C2DD349C8E264AA6","leechers":"0","seeders":"0","num_files":"1","size":"167313408","username":"rabenkind","added":"1213728149","status":"member","category":"303","imdb":""},{"id":"4512019","name":"debian-testing-amd64-i386-powerpc-netinst.iso","info_hash":"A755AAE126AAF9047B89D0075FF19F702B6B4DDF","leechers":"0","seeders":"0","num_files":"1","size":"492965888","username":"AMDFreak78","added":"1226817859","status":"member","category":"303","imdb":""},{"id":"4725085","name":"debian-500-i386-businesscard.iso","info_hash":"018FF30FE833FA680A6E35B73C1B5DD6F0EAA539","leechers":"0","seeders":"0","num_files":"1","size":"37136384","username":"dreamszz","added":"1234708307","status":"member","category":"303","imdb":""},{"id":"4727053","name":"debian-500-i386-netinst.iso","info_hash":"F1AED2E515A0BDF141549D440047AB97812B6916","leechers":"0","seeders":"0","num_files":"1","size":"157204480","username":"dreamszz","added":"1234806400","status":"member","category":"303","imdb":""},{"id":"4734148","name":"debian-500-i386-DVD-1.iso","info_hash":"CF92138268871BC3BB964ED69DB1EC36C2CE9759","leechers":"1","seeders":"0","num_files":"1","size":"4698322944","username":"KRZR","added":"1235186603","status":"member","category":"303","imdb":""},{"id":"4774275","name":"debian-40r6-i386-amd64-powerpc-source-DVD-1.iso","info_hash":"AC58CDFD2577CCE93F7040F88BB4B782B5244FC3","leechers":"0","seeders":"0","num_files":"1","size":"4385832960","username":"CriogenicFear","added":"1237149753","status":"member","category":"303","imdb":""},{"id":"5247569","name":"Debian 5.03 on a Blu-Ray ISO. Downloaded only a few days ago.","info_hash":"2CB339DD437A0A05BC65DACFF89E8A68EF671FFE","leechers":"0","seeders":"0","num_files":"1","size":"20066349056","username":"cerre","added":"1262216973","status":"member","category":"303","imdb":""},{"id":"5724168","name":"clonezilla-live-1.2.5.38 iso files (Testing Debian-based)","info_hash":"E1E684A0061FB8C26A6FEF7A912347A7AB09B8EA","leechers":"1","seeders":"0","num_files":"3","size":"383778816","username":"Anonymous","added":"1280391570","status":"member","category":"699","imdb":""},{"id":"5772142","name":"Debian-Linux-2010.iso","info_hash":"AB58CC1423C06E30343BDCA6856A533D63531053","leechers":"0","seeders":"0","num_files":"1","size":"4681738240","username":"Maizer911","added":"1282132638","status":"member","category":"303","imdb":""},{"id":"5817670","name":"Linux Mint Debian [201009] [ISO] [geno7744]","info_hash":"C88ED91734EFE4AB5DA1C1851F3F6CF472157DB1","leechers":"1","seeders":"0","num_files":"1","size":"917123072","username":"geno7744","added":"1283915768","status":"member","category":"303","imdb":""},{"id":"5948268","name":"debian-506-i386-DVD-1.iso","info_hash":"8D8A114979524D0A2F881E758F42BD14C10A91A7","leechers":"0","seeders":"0","num_files":"1","size":"4675819520","username":"NickShim","added":"1289441340","status":"member","category":"699","imdb":""},{"id":"6039270","name":"debian-privacy-remix.iso","info_hash":"FFACF7F1C5922C70FEF99A887D94D8765BAE45BD","leechers":"0","seeders":"0","num_files":"1","size":"1531969536","username":"Brage P","added":"1292329538","status":"member","category":"303","imdb":""},{"id":"6089092","name":"Linux Mint Debian [201101] [ISO] [32-Bit] [geno7744] ","info_hash":"2E99D97F1768644A86A8E99BFD80C816490F959B","leechers":"0","seeders":"0","num_files":"1","size":"1033945088","username":"geno7744","added":"1294464609","status":"member","category":"303","imdb":""},{"id":"6235937","name":"debian-live-6.0.0-i686-openbox-installer.iso","info_hash":"5A81DE1AEA89A3CEA01FAF1BF24F5CE1922E9AA1","leechers":"0","seeders":"0","num_files":"1","size":"159383552","username":"alretz","added":"1299868684","status":"member","category":"303","imdb":""},{"id":"6302628","name":"Linux Mint \\\"Debian\\\" - Linux Mint Xfce [201104] [32-Bit] [ISO","info_hash":"D097C0636EE45D235AE839AD7F1019CDB542D335","leechers":"0","seeders":"0","num_files":"1","size":"1004662784","username":"geno7744","added":"1302202364","status":"member","category":"303","imdb":""},{"id":"6302634","name":"Linux Mint \\\"Debian\\\" - Linux Mint Xfce [201104] [64-Bit] [ISO","info_hash":"C2B15F18A60573D075D15D917F5708FF854A082F","leechers":"0","seeders":"0","num_files":"1","size":"988688384","username":"geno7744","added":"1302202595","status":"member","category":"303","imdb":""},{"id":"7456182","name":"Debian_6.0.5_i386_netinst.iso","info_hash":"AC10EB57213131A1113321C66B991EC125AAFE55","leechers":"0","seeders":"0","num_files":"1","size":"200278016","username":"futurerealm","added":"1342663638","status":"member","category":"303","imdb":""},{"id":"7740588","name":"debian-testing-amd64-xfce-CD-1.iso","info_hash":"BE7968DC49EBC2994EC4129A7D42350831C95BB5","leechers":"1","seeders":"0","num_files":"1","size":"675741696","username":"ghostsquad57","added":"1350607428","status":"member","category":"303","imdb":""},{"id":"8166079","name":"Debian Wheezy 7.0 RC1 - Netinstall Amd64 ISO","info_hash":"5ED1E9731051E027FB0F097FC1D034F153192D7C","leechers":"0","seeders":"0","num_files":"1","size":"231735296","username":"melaur","added":"1361333871","status":"member","category":"303","imdb":""},{"id":"8166080","name":"Debian Wheezy 7.0 RC1 - CD1 Amd64 ISO","info_hash":"B0517AA899E8D81DF8731BF926408A10F615DC75","leechers":"0","seeders":"0","num_files":"1","size":"661651456","username":"melaur","added":"1361333966","status":"member","category":"303","imdb":""},{"id":"8166081","name":"Debian Wheezy 7.0 RC1 - DVD1 Amd64 ISO","info_hash":"2875DA262892568665D580B2043E5C0D49BD409F","leechers":"0","seeders":"0","num_files":"1","size":"3994091520","username":"melaur","added":"1361334036","status":"member","category":"303","imdb":""},{"id":"8447624","name":"debian-live-7.0.0-amd64-kde-desktop.iso","info_hash":"D0D5EC725AF658B964D7E319E3FF57A5CF17E773","leechers":"0","seeders":"0","num_files":"1","size":"1268842496","username":"arakus","added":"1367924014","status":"member","category":"303","imdb":""},{"id":"8452217","name":"debian-7.0.0-i386-DVD-1.iso","info_hash":"81BFB500A1C0BE3F1FA9C24FC6668882DA2BE1E7","leechers":"0","seeders":"0","num_files":"1","size":"3998007296","username":"LabelReunion","added":"1368023496","status":"member","category":"303","imdb":""},{"id":"8476583","name":"debian-7.0.0-amd64-DVD-1.iso","info_hash":"96534331D2D75ACF14F8162770495BD5B05A17A9","leechers":"0","seeders":"0","num_files":"1","size":"3998007296","username":"Caeden","added":"1368620918","status":"member","category":"303","imdb":""},{"id":"8476585","name":"debian-7.0.0-amd64-DVD-2.iso","info_hash":"9B9F7FC23507F56B24DF2CA8EA2ADD6DBA1665E9","leechers":"0","seeders":"0","num_files":"1","size":"4696872960","username":"Caeden","added":"1368620988","status":"member","category":"303","imdb":""},{"id":"8476587","name":"debian-7.0.0-amd64-DVD-3.iso","info_hash":"F32DE312C4819C405CE0D6A75578053AF517B45E","leechers":"0","seeders":"0","num_files":"1","size":"4698955776","username":"Caeden","added":"1368621052","status":"member","category":"303","imdb":""},{"id":"8717112","name":"debian-live-7.0.0-amd64-kde-desktop+nonfree.iso","info_hash":"5A7D58A79AE9F48CB480148340FCEFFE3A9F69DF","leechers":"0","seeders":"0","num_files":"1","size":"1268842496","username":"Agoing","added":"1374341366","status":"member","category":"399","imdb":""},{"id":"9167094","name":"Debian wheezy 7.2.0, 64-bit installer iso for CD or USB flash dr","info_hash":"5DFCA00F9C030A0E8404435F6FF3E8CB90EFBDC3","leechers":"1","seeders":"0","num_files":"1","size":"232783872","username":"rduke15","added":"1384003532","status":"member","category":"303","imdb":""},{"id":"10605619","name":"debian 7.6.0 versão mais recente.iso ","info_hash":"BDF336E0BB7B2EDDC586EECAFB8551B310D78480","leechers":"0","seeders":"0","num_files":"1","size":"679477248","username":"Bruno-kun","added":"1405975347","status":"member","category":"399","imdb":""}]
diff --git a/tests/data/no_hits.html b/tests/data/no_hits.html
deleted file mode 100644
index a3a9156..0000000
--- a/tests/data/no_hits.html
+++ /dev/null
@@ -1,200 +0,0 @@
-
-
-
- The Pirate Bay - The galaxy's most resilient bittorrent site
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Search results: aaaaaaaaaaaaaaaaa No hits. Try adding an asterisk in you search phrase.
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/tests/data/no_hits.json b/tests/data/no_hits.json
new file mode 100644
index 0000000..759b863
--- /dev/null
+++ b/tests/data/no_hits.json
@@ -0,0 +1 @@
+[{"id":"0","name":"No results returned","info_hash":"0000000000000000000000000000000000000000","leechers":"0","seeders":"0","num_files":"0","size":"0","username":"","added":"0","status":"member","category":"0","imdb":"","total_found":"1"}]
diff --git a/tests/data/result.json b/tests/data/result.json
new file mode 100644
index 0000000..a6cd0e1
--- /dev/null
+++ b/tests/data/result.json
@@ -0,0 +1 @@
+[{"id": "6089103", "name": "Linux Mint Debian [201012] [ISO] [64-Bit] [geno7744] ", "info_hash": 80795982264005515156107154982012464690660356648, "leechers": 1, "seeders": 1, "num_files": "1", "size": "974.2 MiB", "username": "geno7744", "added": "1294464892", "status": "member", "category": 303, "imdb": "", "raw_size": 1021560832, "magnet": "magnet:?xt=urn:btih:0E270463CD1AD2D856EA89859829BC1E63E3B228&dn=Linux%20Mint%20Debian%20%5B201012%5D%20%5BISO%5D%20%5B64-Bit%5D%20%5Bgeno7744%5D%20", "raw_uploaded": 1294464892, "uploaded": "2011-01-08 05:34"}, {"id": "8442441", "name": "debian-7.0.0-i386-DVD-1.iso", "info_hash": 722580357008177778209183558297497340197820479935, "leechers": 0, "seeders": 1, "num_files": "1", "size": "3.7 GiB", "username": "mmxx_01", "added": "1367797189", "status": "member", "category": 303, "imdb": "", "raw_size": 3998007296, "magnet": "magnet:?xt=urn:btih:7E919FB33216E2BC6C31ADE3594A3866D260B9BF&dn=debian-7.0.0-i386-DVD-1.iso", "raw_uploaded": 1367797189, "uploaded": "2013-05-05 23:39"}, {"id": "3357155", "name": "debian_gnu_linux_31r0a_ia64_1_iso", "info_hash": 689059738704693667977745168177664313802663339526, "leechers": 1, "seeders": 0, "num_files": "2", "size": "4.3 GiB", "username": "gigli", "added": "1121550810", "status": "member", "category": 303, "imdb": "", "raw_size": 4640233753, "magnet": "magnet:?xt=urn:btih:78B281DF90ED64F8E6D13555C9108A54C9472606&dn=debian_gnu_linux_31r0a_ia64_1_iso", "raw_uploaded": 1121550810, "uploaded": "2005-07-16 21:53"}, {"id": "3441282", "name": "Debian 3.1r1 i386 binary No. 1 (of 14) ISO file", "info_hash": 921490056423831576913963695099571046100261500145, "leechers": 0, "seeders": 0, "num_files": "1", "size": "638.6 MiB", "username": "PixelHermit", "added": "1139313316", "status": "member", "category": 303, "imdb": "", "raw_size": 669585408, "magnet": "magnet:?xt=urn:btih:A1690B1129AE13E5F01457338F5D06223F8174F1&dn=Debian%203.1r1%20i386%20binary%20No.%201%20%28of%2014%29%20ISO%20file", "raw_uploaded": 1139313316, "uploaded": "2006-02-07 11:55"}, {"id": "3441283", "name": "Debian 3.1r1 i386 binary No. 2 (of 14) ISO file", "info_hash": 1017066668506766024805398047146111070264848022468, "leechers": 0, "seeders": 0, "num_files": "1", "size": "640.0 MiB", "username": "PixelHermit", "added": "1139313488", "status": "member", "category": 303, "imdb": "", "raw_size": 671037440, "magnet": "magnet:?xt=urn:btih:B226D8C4192EB6994FEAF0DBCA04C6126DC7FBC4&dn=Debian%203.1r1%20i386%20binary%20No.%202%20%28of%2014%29%20ISO%20file", "raw_uploaded": 1139313488, "uploaded": "2006-02-07 11:58"}, {"id": "3441284", "name": "Debian 3.1r1 i386 binary No. 3 (of 14) ISO file", "info_hash": 1341401429899326842086437561024998431776375829169, "leechers": 1, "seeders": 0, "num_files": "1", "size": "642.5 MiB", "username": "PixelHermit", "added": "1139313671", "status": "member", "category": 303, "imdb": "", "raw_size": 673679360, "magnet": "magnet:?xt=urn:btih:EAF6853B9438EB757D066BFDE0D13AD9F657F2B1&dn=Debian%203.1r1%20i386%20binary%20No.%203%20%28of%2014%29%20ISO%20file", "raw_uploaded": 1139313671, "uploaded": "2006-02-07 12:01"}, {"id": "3578845", "name": "debian-31r4-i386-netinst.iso", "info_hash": 1190326781527594986631304106552213100503470480731, "leechers": 0, "seeders": 0, "num_files": "1", "size": "112.2 MiB", "username": "keep_patrick", "added": "1166383842", "status": "member", "category": 303, "imdb": "", "raw_size": 117620736, "magnet": "magnet:?xt=urn:btih:D08019524B9FD86A48D31347BFC3714408B09D5B&dn=debian-31r4-i386-netinst.iso", "raw_uploaded": 1166383842, "uploaded": "2006-12-17 19:30"}, {"id": "3795842", "name": "Debian 4.0 ETCH.iso", "info_hash": 341174057895425211125490586805104666098960797816, "leechers": 0, "seeders": 0, "num_files": "1", "size": "3.6 GiB", "username": "lkonti", "added": "1188950149", "status": "member", "category": 303, "imdb": "", "raw_size": 3833810944, "magnet": "magnet:?xt=urn:btih:3BC2C61C420A62DE7442E44698E6AED05A499078&dn=Debian%204.0%20ETCH.iso", "raw_uploaded": 1188950149, "uploaded": "2007-09-04 23:55"}, {"id": "3860812", "name": "debian-live-etch-i386-standard.iso", "info_hash": 375240546390171180979179024422139356658954548844, "leechers": 0, "seeders": 0, "num_files": "1", "size": "81.2 MiB", "username": "superunix", "added": "1193471893", "status": "member", "category": 303, "imdb": "", "raw_size": 85168128, "magnet": "magnet:?xt=urn:btih:41BA5E3833E9D678DB41673183EC49AD32EA366C&dn=debian-live-etch-i386-standard.iso", "raw_uploaded": 1193471893, "uploaded": "2007-10-27 07:58"}, {"id": "3872700", "name": "debian-kde4beta4-live-cd-i386.iso", "info_hash": 255001694441119611713565405428907543400911906961, "leechers": 0, "seeders": 0, "num_files": "1", "size": "418.2 MiB", "username": "jknotzke", "added": "1194191588", "status": "member", "category": 303, "imdb": "", "raw_size": 438484992, "magnet": "magnet:?xt=urn:btih:2CAAABE42A03A3EFB28F33DE93C2FBB278EAA891&dn=debian-kde4beta4-live-cd-i386.iso", "raw_uploaded": 1194191588, "uploaded": "2007-11-04 15:53"}, {"id": "3947454", "name": "Debian 4.0 r1 Update.iso", "info_hash": 606994683147973413780468573362798954490278176700, "leechers": 0, "seeders": 0, "num_files": "1", "size": "1.3 GiB", "username": "shasi420", "added": "1198595636", "status": "member", "category": 303, "imdb": "", "raw_size": 1374822400, "magnet": "magnet:?xt=urn:btih:6A52953C8C8C6DAE89D867EB3DEB1C66AC43E7BC&dn=Debian%204.0%20r1%20Update.iso", "raw_uploaded": 1198595636, "uploaded": "2007-12-25 15:13"}, {"id": "4224808", "name": "debian-hamm-source-cs.iso", "info_hash": 61691326620951224084992079352569146745579057563, "leechers": 0, "seeders": 0, "num_files": "1", "size": "602.1 MiB", "username": "geier", "added": "1212764102", "status": "trusted", "category": 303, "imdb": "", "raw_size": 631373824, "magnet": "magnet:?xt=urn:btih:0ACE55B2D81A56E1139A2E7C070F54B2F82F319B&dn=debian-hamm-source-cs.iso", "raw_uploaded": 1212764102, "uploaded": "2008-06-06 14:55"}, {"id": "4244856", "name": "debian-40r3-i386-netinst.iso", "info_hash": 760184894299265408134398932196202327732386155174, "leechers": 0, "seeders": 0, "num_files": "1", "size": "159.6 MiB", "username": "rabenkind", "added": "1213728149", "status": "member", "category": 303, "imdb": "", "raw_size": 167313408, "magnet": "magnet:?xt=urn:btih:8527DE9E12B4196A1C443853C2DD349C8E264AA6&dn=debian-40r3-i386-netinst.iso", "raw_uploaded": 1213728149, "uploaded": "2008-06-17 18:42"}, {"id": "4512019", "name": "debian-testing-amd64-i386-powerpc-netinst.iso", "info_hash": 955311907772995664947964713591409505697817906655, "leechers": 0, "seeders": 0, "num_files": "1", "size": "470.1 MiB", "username": "AMDFreak78", "added": "1226817859", "status": "member", "category": 303, "imdb": "", "raw_size": 492965888, "magnet": "magnet:?xt=urn:btih:A755AAE126AAF9047B89D0075FF19F702B6B4DDF&dn=debian-testing-amd64-i386-powerpc-netinst.iso", "raw_uploaded": 1226817859, "uploaded": "2008-11-16 06:44"}, {"id": "4725085", "name": "debian-500-i386-businesscard.iso", "info_hash": 8919171032581421194728491876700289114433037625, "leechers": 0, "seeders": 0, "num_files": "1", "size": "35.4 MiB", "username": "dreamszz", "added": "1234708307", "status": "member", "category": 303, "imdb": "", "raw_size": 37136384, "magnet": "magnet:?xt=urn:btih:018FF30FE833FA680A6E35B73C1B5DD6F0EAA539&dn=debian-500-i386-businesscard.iso", "raw_uploaded": 1234708307, "uploaded": "2009-02-15 14:31"}, {"id": "4727053", "name": "debian-500-i386-netinst.iso", "info_hash": 1379765476966545756211988693588566713117000296726, "leechers": 0, "seeders": 0, "num_files": "1", "size": "149.9 MiB", "username": "dreamszz", "added": "1234806400", "status": "member", "category": 303, "imdb": "", "raw_size": 157204480, "magnet": "magnet:?xt=urn:btih:F1AED2E515A0BDF141549D440047AB97812B6916&dn=debian-500-i386-netinst.iso", "raw_uploaded": 1234806400, "uploaded": "2009-02-16 17:46"}, {"id": "4734148", "name": "debian-500-i386-DVD-1.iso", "info_hash": 1185018697868601890431751383066358717434374821721, "leechers": 1, "seeders": 0, "num_files": "1", "size": "4.4 GiB", "username": "KRZR", "added": "1235186603", "status": "member", "category": 303, "imdb": "", "raw_size": 4698322944, "magnet": "magnet:?xt=urn:btih:CF92138268871BC3BB964ED69DB1EC36C2CE9759&dn=debian-500-i386-DVD-1.iso", "raw_uploaded": 1235186603, "uploaded": "2009-02-21 03:23"}, {"id": "4774275", "name": "debian-40r6-i386-amd64-powerpc-source-DVD-1.iso", "info_hash": 983926822319029410114111508998868428479086743491, "leechers": 0, "seeders": 0, "num_files": "1", "size": "4.1 GiB", "username": "CriogenicFear", "added": "1237149753", "status": "member", "category": 303, "imdb": "", "raw_size": 4385832960, "magnet": "magnet:?xt=urn:btih:AC58CDFD2577CCE93F7040F88BB4B782B5244FC3&dn=debian-40r6-i386-amd64-powerpc-source-DVD-1.iso", "raw_uploaded": 1237149753, "uploaded": "2009-03-15 20:42"}, {"id": "5247569", "name": "Debian 5.03 on a Blu-Ray ISO. Downloaded only a few days ago.", "info_hash": 255192467999179059682926285371714956385092837374, "leechers": 0, "seeders": 0, "num_files": "1", "size": "18.7 GiB", "username": "cerre", "added": "1262216973", "status": "member", "category": 303, "imdb": "", "raw_size": 20066349056, "magnet": "magnet:?xt=urn:btih:2CB339DD437A0A05BC65DACFF89E8A68EF671FFE&dn=Debian%205.03%20on%20a%20Blu-Ray%20ISO.%20Downloaded%20only%20a%20few%20days%20ago.", "raw_uploaded": 1262216973, "uploaded": "2009-12-30 23:49"}, {"id": "5724168", "name": "clonezilla-live-1.2.5.38 iso files (Testing Debian-based)", "info_hash": 1289663648106087712521617503484063321980943317226, "leechers": 1, "seeders": 0, "num_files": "3", "size": "366.0 MiB", "username": "Anonymous", "added": "1280391570", "status": "member", "category": 699, "imdb": "", "raw_size": 383778816, "magnet": "magnet:?xt=urn:btih:E1E684A0061FB8C26A6FEF7A912347A7AB09B8EA&dn=clonezilla-live-1.2.5.38%20iso%20files%20%28Testing%20Debian-based%29", "raw_uploaded": 1280391570, "uploaded": "2010-07-29 08:19"}, {"id": "5772142", "name": "Debian-Linux-2010.iso", "info_hash": 978217665147846806722892460805650877619966775379, "leechers": 0, "seeders": 0, "num_files": "1", "size": "4.4 GiB", "username": "Maizer911", "added": "1282132638", "status": "member", "category": 303, "imdb": "", "raw_size": 4681738240, "magnet": "magnet:?xt=urn:btih:AB58CC1423C06E30343BDCA6856A533D63531053&dn=Debian-Linux-2010.iso", "raw_uploaded": 1282132638, "uploaded": "2010-08-18 11:57"}, {"id": "5817670", "name": "Linux Mint Debian [201009] [ISO] [geno7744]", "info_hash": 1144983771245866337184222353155138181654247734705, "leechers": 1, "seeders": 0, "num_files": "1", "size": "874.6 MiB", "username": "geno7744", "added": "1283915768", "status": "member", "category": 303, "imdb": "", "raw_size": 917123072, "magnet": "magnet:?xt=urn:btih:C88ED91734EFE4AB5DA1C1851F3F6CF472157DB1&dn=Linux%20Mint%20Debian%20%5B201009%5D%20%5BISO%5D%20%5Bgeno7744%5D", "raw_uploaded": 1283915768, "uploaded": "2010-09-08 03:16"}, {"id": "5948268", "name": "debian-506-i386-DVD-1.iso", "info_hash": 808046707434296142448012177344602162952657342887, "leechers": 0, "seeders": 0, "num_files": "1", "size": "4.4 GiB", "username": "NickShim", "added": "1289441340", "status": "member", "category": 699, "imdb": "", "raw_size": 4675819520, "magnet": "magnet:?xt=urn:btih:8D8A114979524D0A2F881E758F42BD14C10A91A7&dn=debian-506-i386-DVD-1.iso", "raw_uploaded": 1289441340, "uploaded": "2010-11-11 02:09"}, {"id": "6039270", "name": "debian-privacy-remix.iso", "info_hash": 1459649973739518810476349462010708323032997184957, "leechers": 0, "seeders": 0, "num_files": "1", "size": "1.4 GiB", "username": "Brage P", "added": "1292329538", "status": "member", "category": 303, "imdb": "", "raw_size": 1531969536, "magnet": "magnet:?xt=urn:btih:FFACF7F1C5922C70FEF99A887D94D8765BAE45BD&dn=debian-privacy-remix.iso", "raw_uploaded": 1292329538, "uploaded": "2010-12-14 12:25"}, {"id": "6089092", "name": "Linux Mint Debian [201101] [ISO] [32-Bit] [geno7744] ", "info_hash": 266044536086293871309947032924605765528595699099, "leechers": 0, "seeders": 0, "num_files": "1", "size": "986.0 MiB", "username": "geno7744", "added": "1294464609", "status": "member", "category": 303, "imdb": "", "raw_size": 1033945088, "magnet": "magnet:?xt=urn:btih:2E99D97F1768644A86A8E99BFD80C816490F959B&dn=Linux%20Mint%20Debian%20%5B201101%5D%20%5BISO%5D%20%5B32-Bit%5D%20%5Bgeno7744%5D%20", "raw_uploaded": 1294464609, "uploaded": "2011-01-08 05:30"}, {"id": "6235937", "name": "debian-live-6.0.0-i686-openbox-installer.iso", "info_hash": 516705313591328416372009021219231554066424175265, "leechers": 0, "seeders": 0, "num_files": "1", "size": "152.0 MiB", "username": "alretz", "added": "1299868684", "status": "member", "category": 303, "imdb": "", "raw_size": 159383552, "magnet": "magnet:?xt=urn:btih:5A81DE1AEA89A3CEA01FAF1BF24F5CE1922E9AA1&dn=debian-live-6.0.0-i686-openbox-installer.iso", "raw_uploaded": 1299868684, "uploaded": "2011-03-11 18:38"}, {"id": "6302628", "name": "Linux Mint \\\"Debian\\\" - Linux Mint Xfce [201104] [32-Bit] [ISO", "info_hash": 1190854252250590780560429763076669195625374602037, "leechers": 0, "seeders": 0, "num_files": "1", "size": "958.1 MiB", "username": "geno7744", "added": "1302202364", "status": "member", "category": 303, "imdb": "", "raw_size": 1004662784, "magnet": "magnet:?xt=urn:btih:D097C0636EE45D235AE839AD7F1019CDB542D335&dn=Linux%20Mint%20%5C%22Debian%5C%22%20-%20Linux%20Mint%20Xfce%20%5B201104%5D%20%5B32-Bit%5D%20%5BISO", "raw_uploaded": 1302202364, "uploaded": "2011-04-07 18:52"}, {"id": "6302634", "name": "Linux Mint \\\"Debian\\\" - Linux Mint Xfce [201104] [64-Bit] [ISO", "info_hash": 1111499725494585269100850072277199343426671872047, "leechers": 0, "seeders": 0, "num_files": "1", "size": "942.9 MiB", "username": "geno7744", "added": "1302202595", "status": "member", "category": 303, "imdb": "", "raw_size": 988688384, "magnet": "magnet:?xt=urn:btih:C2B15F18A60573D075D15D917F5708FF854A082F&dn=Linux%20Mint%20%5C%22Debian%5C%22%20-%20Linux%20Mint%20Xfce%20%5B201104%5D%20%5B64-Bit%5D%20%5BISO", "raw_uploaded": 1302202595, "uploaded": "2011-04-07 18:56"}, {"id": "7456182", "name": "Debian_6.0.5_i386_netinst.iso", "info_hash": 982323725540756726924071830373687458237552197205, "leechers": 0, "seeders": 0, "num_files": "1", "size": "191.0 MiB", "username": "futurerealm", "added": "1342663638", "status": "member", "category": 303, "imdb": "", "raw_size": 200278016, "magnet": "magnet:?xt=urn:btih:AC10EB57213131A1113321C66B991EC125AAFE55&dn=Debian_6.0.5_i386_netinst.iso", "raw_uploaded": 1342663638, "uploaded": "2012-07-19 02:07"}, {"id": "7740588", "name": "debian-testing-amd64-xfce-CD-1.iso", "info_hash": 1087415771263667121064339753920887629991960468405, "leechers": 1, "seeders": 0, "num_files": "1", "size": "644.4 MiB", "username": "ghostsquad57", "added": "1350607428", "status": "member", "category": 303, "imdb": "", "raw_size": 675741696, "magnet": "magnet:?xt=urn:btih:BE7968DC49EBC2994EC4129A7D42350831C95BB5&dn=debian-testing-amd64-xfce-CD-1.iso", "raw_uploaded": 1350607428, "uploaded": "2012-10-19 00:43"}, {"id": "8166079", "name": "Debian Wheezy 7.0 RC1 - Netinstall Amd64 ISO", "info_hash": 541326324520720881965336898533966032628960800124, "leechers": 0, "seeders": 0, "num_files": "1", "size": "221.0 MiB", "username": "melaur", "added": "1361333871", "status": "member", "category": 303, "imdb": "", "raw_size": 231735296, "magnet": "magnet:?xt=urn:btih:5ED1E9731051E027FB0F097FC1D034F153192D7C&dn=Debian%20Wheezy%207.0%20RC1%20-%20Netinstall%20Amd64%20ISO", "raw_uploaded": 1361333871, "uploaded": "2013-02-20 04:17"}, {"id": "8166080", "name": "Debian Wheezy 7.0 RC1 - CD1 Amd64 ISO", "info_hash": 1006599421096978933808505216878621295017941392501, "leechers": 0, "seeders": 0, "num_files": "1", "size": "631.0 MiB", "username": "melaur", "added": "1361333966", "status": "member", "category": 303, "imdb": "", "raw_size": 661651456, "magnet": "magnet:?xt=urn:btih:B0517AA899E8D81DF8731BF926408A10F615DC75&dn=Debian%20Wheezy%207.0%20RC1%20-%20CD1%20Amd64%20ISO", "raw_uploaded": 1361333966, "uploaded": "2013-02-20 04:19"}, {"id": "8166081", "name": "Debian Wheezy 7.0 RC1 - DVD1 Amd64 ISO", "info_hash": 230987821484173680714374515411410949945908084895, "leechers": 0, "seeders": 0, "num_files": "1", "size": "3.7 GiB", "username": "melaur", "added": "1361334036", "status": "member", "category": 303, "imdb": "", "raw_size": 3994091520, "magnet": "magnet:?xt=urn:btih:2875DA262892568665D580B2043E5C0D49BD409F&dn=Debian%20Wheezy%207.0%20RC1%20-%20DVD1%20Amd64%20ISO", "raw_uploaded": 1361334036, "uploaded": "2013-02-20 04:20"}, {"id": "8447624", "name": "debian-live-7.0.0-amd64-kde-desktop.iso", "info_hash": 1192240736471224992363141147771043610819590350707, "leechers": 0, "seeders": 0, "num_files": "1", "size": "1.2 GiB", "username": "arakus", "added": "1367924014", "status": "member", "category": 303, "imdb": "", "raw_size": 1268842496, "magnet": "magnet:?xt=urn:btih:D0D5EC725AF658B964D7E319E3FF57A5CF17E773&dn=debian-live-7.0.0-amd64-kde-desktop.iso", "raw_uploaded": 1367924014, "uploaded": "2013-05-07 10:53"}, {"id": "8452217", "name": "debian-7.0.0-i386-DVD-1.iso", "info_hash": 740735019307954783227960257388713188362285736423, "leechers": 0, "seeders": 0, "num_files": "1", "size": "3.7 GiB", "username": "LabelReunion", "added": "1368023496", "status": "member", "category": 303, "imdb": "", "raw_size": 3998007296, "magnet": "magnet:?xt=urn:btih:81BFB500A1C0BE3F1FA9C24FC6668882DA2BE1E7&dn=debian-7.0.0-i386-DVD-1.iso", "raw_uploaded": 1368023496, "uploaded": "2013-05-08 14:31"}, {"id": "8476583", "name": "debian-7.0.0-amd64-DVD-1.iso", "info_hash": 858205430952303442181229988565206976761100375977, "leechers": 0, "seeders": 0, "num_files": "1", "size": "3.7 GiB", "username": "Caeden", "added": "1368620918", "status": "member", "category": 303, "imdb": "", "raw_size": 3998007296, "magnet": "magnet:?xt=urn:btih:96534331D2D75ACF14F8162770495BD5B05A17A9&dn=debian-7.0.0-amd64-DVD-1.iso", "raw_uploaded": 1368620918, "uploaded": "2013-05-15 12:28"}, {"id": "8476585", "name": "debian-7.0.0-amd64-DVD-2.iso", "info_hash": 888450517309844419082815343848182492547694093801, "leechers": 0, "seeders": 0, "num_files": "1", "size": "4.4 GiB", "username": "Caeden", "added": "1368620988", "status": "member", "category": 303, "imdb": "", "raw_size": 4696872960, "magnet": "magnet:?xt=urn:btih:9B9F7FC23507F56B24DF2CA8EA2ADD6DBA1665E9&dn=debian-7.0.0-amd64-DVD-2.iso", "raw_uploaded": 1368620988, "uploaded": "2013-05-15 12:29"}, {"id": "8476587", "name": "debian-7.0.0-amd64-DVD-3.iso", "info_hash": 1388308071719317659206743703980188513029953401950, "leechers": 0, "seeders": 0, "num_files": "1", "size": "4.4 GiB", "username": "Caeden", "added": "1368621052", "status": "member", "category": 303, "imdb": "", "raw_size": 4698955776, "magnet": "magnet:?xt=urn:btih:F32DE312C4819C405CE0D6A75578053AF517B45E&dn=debian-7.0.0-amd64-DVD-3.iso", "raw_uploaded": 1368621052, "uploaded": "2013-05-15 12:30"}, {"id": "8717112", "name": "debian-live-7.0.0-amd64-kde-desktop+nonfree.iso", "info_hash": 516604485438195032346767058483128354960364759519, "leechers": 0, "seeders": 0, "num_files": "1", "size": "1.2 GiB", "username": "Agoing", "added": "1374341366", "status": "member", "category": 399, "imdb": "", "raw_size": 1268842496, "magnet": "magnet:?xt=urn:btih:5A7D58A79AE9F48CB480148340FCEFFE3A9F69DF&dn=debian-live-7.0.0-amd64-kde-desktop%2Bnonfree.iso", "raw_uploaded": 1374341366, "uploaded": "2013-07-20 17:29"}, {"id": "9167094", "name": "Debian wheezy 7.2.0, 64-bit installer iso for CD or USB flash dr", "info_hash": 536569872754006726446059760945501647386767769027, "leechers": 1, "seeders": 0, "num_files": "1", "size": "222.0 MiB", "username": "rduke15", "added": "1384003532", "status": "member", "category": 303, "imdb": "", "raw_size": 232783872, "magnet": "magnet:?xt=urn:btih:5DFCA00F9C030A0E8404435F6FF3E8CB90EFBDC3&dn=Debian%20wheezy%207.2.0%2C%2064-bit%20installer%20iso%20for%20CD%20or%20USB%20flash%20dr", "raw_uploaded": 1384003532, "uploaded": "2013-11-09 13:25"}, {"id": "10605619", "name": "debian 7.6.0 versão mais recente.iso ", "info_hash": 1084423117304844355133768512253358904316863677568, "leechers": 0, "seeders": 0, "num_files": "1", "size": "648.0 MiB", "username": "Bruno-kun", "added": "1405975347", "status": "member", "category": 399, "imdb": "", "raw_size": 679477248, "magnet": "magnet:?xt=urn:btih:BDF336E0BB7B2EDDC586EECAFB8551B310D78480&dn=debian%207.6.0%20vers%26atilde%3Bo%20mais%20recente.iso%20", "raw_uploaded": 1405975347, "uploaded": "2014-07-21 20:42"}]
\ No newline at end of file
diff --git a/tests/test_pirate.py b/tests/test_pirate.py
index dffe4ab..4c0cf8e 100755
--- a/tests/test_pirate.py
+++ b/tests/test_pirate.py
@@ -1,7 +1,6 @@
#!/usr/bin/env python3
import socket
import unittest
-import subprocess
from argparse import Namespace
from unittest import mock
from unittest.mock import patch, call, MagicMock
@@ -28,33 +27,42 @@ class TestPirate(unittest.TestCase):
@patch('subprocess.call')
def test_main(self, mock_call):
result = {
- 'magnet': 'dn=derp',
- 'seeds': '1',
+ 'name': 'derp',
+ 'magnet': 'magnet:?xt=urn:btih:deadbeef&dn=derp',
+ 'seeders': '1',
'leechers': '1',
- 'size': ('1', 'mb'),
+ 'size': '1 MB',
'uploaded': '1',
}
- with patch('pirate.torrent.remote', return_value=[result]) as mock_remote:
+ with patch('pirate.pirate.connect_mirror',
+ return_value=([result], '')):
config = pirate.pirate.parse_config_file('')
- args = pirate.pirate.combine_configs(config, pirate.pirate.parse_args(['-0', 'term', '-C', 'blah %s']))
+ args = pirate.pirate.combine_configs(
+ config,
+ pirate.pirate.parse_args(['-0', 'term', '-C', 'blah %s']))
pirate.pirate.pirate_main(args)
- mock_call.assert_called_once_with(['blah', 'dn=derp'])
+ mock_call.assert_called_once_with(
+ ['blah', 'magnet:?xt=urn:btih:deadbeef&dn=derp'])
@patch('pirate.pirate.builtins.input', return_value='0')
@patch('subprocess.call')
def test_main_choice(self, mock_call, mock_input):
result = {
- 'magnet': 'dn=derp',
- 'seeds': '1',
+ 'name': 'derp',
+ 'magnet': 'magnet:?xt=urn:btih:deadbeef&dn=derp',
+ 'seeders': '1',
'leechers': '1',
- 'size': ('1', 'mb'),
+ 'size': '1 MB',
'uploaded': '1',
}
- with patch('pirate.torrent.remote', return_value=[result]) as mock_remote:
+ with patch('pirate.pirate.connect_mirror',
+ return_value=([result], '')):
config = pirate.pirate.parse_config_file('')
- args = pirate.pirate.combine_configs(config, pirate.pirate.parse_args(['term', '-C', 'blah %s']))
+ args = pirate.pirate.combine_configs(
+ config, pirate.pirate.parse_args(['term', '-C', 'blah %s']))
pirate.pirate.pirate_main(args)
- mock_call.assert_called_once_with(['blah', 'dn=derp'])
+ mock_call.assert_called_once_with(
+ ['blah', 'magnet:?xt=urn:btih:deadbeef&dn=derp'])
def test_parse_torrent_command(self):
tests = [
@@ -70,11 +78,13 @@ class TestPirate(unittest.TestCase):
[['d 23, 1'], ('d', [23, 1])],
[['1d'], ('d', [1])],
[['1 ... d'], ('d', [1])],
- [['1-3 d'], ('d', [1,2,3])],
- [['1-3'], (None, [1,2,3])],
+ [['1-3 d'], ('d', [1, 2, 3])],
+ [['1-3'], (None, [1, 2, 3])],
]
for test in tests:
- self.assertEqual(pirate.pirate.parse_torrent_command(*test[0]), test[1])
+ self.assertEqual(
+ pirate.pirate.parse_torrent_command(*test[0]),
+ test[1])
def test_parse_config_file(self):
types = {
@@ -128,16 +138,30 @@ class TestPirate(unittest.TestCase):
('', ['-l'], {'action': 'list_categories'}),
('', ['--list_sorts'], {'action': 'list_sorts'}),
('', ['term'], {'action': 'search', 'source': 'tpb'}),
- ('', ['-L', 'filename', 'term'], {'action': 'search', 'source': 'local_tpb', 'database': 'filename'}),
- ('', ['term', '-S', 'dir'], {'action': 'search', 'save_directory': 'dir'}),
- ('', ['-E', 'localhost:1337'], {'transmission_command': ['transmission-remote', 'localhost:1337']}),
+ ('',
+ ['-L', 'filename', 'term'],
+ {'action': 'search', 'source': 'local_tpb',
+ 'database': 'filename'}),
+ ('',
+ ['term', '-S', 'dir'],
+ {'action': 'search', 'save_directory': 'dir'}),
+ ('',
+ ['-E', 'localhost:1337'],
+ {'transmission_command':
+ ['transmission-remote', 'localhost:1337']}),
('', ['term'], {'output': 'browser_open'}),
('', ['term', '-t'], {'output': 'transmission'}),
('', ['term', '--save-magnets'], {'output': 'save_magnet_files'}),
- ('', ['term', '--save-torrents'], {'output': 'save_torrent_files'}),
- ('', ['term', '-C', 'command'], {'output': 'open_command', 'open_command': 'command'}),
+ ('',
+ ['term', '-C', 'command'],
+ {'output': 'open_command', 'open_command': 'command'}),
('', ['internets'], {'action': 'search', 'search': ['internets']}),
- ('', ['internets lol', 'lel'], {'action': 'search', 'search': ['internets lol', 'lel']}),
+ ('',
+ ['term', '--save-torrents'],
+ {'output': 'save_torrent_files'}),
+ ('',
+ ['internets lol', 'lel'],
+ {'action': 'search', 'search': ['internets lol', 'lel']}),
]
for test in tests:
args = pirate.pirate.parse_args(test[1])
@@ -148,29 +172,36 @@ class TestPirate(unittest.TestCase):
self.assertEqual(test[2][option], value)
def test_search_mirrors(self):
- args = Namespace(pages=1, category=100, sort=10,
- action='browse', search=[],
- mirror=[pirate.data.default_mirror])
+ args = Namespace(
+ category=100, sort=10,
+ action='browse', search=[],
+ mirror=[pirate.data.default_mirror],
+ timeout=pirate.data.default_timeout)
+
class MockResponse():
- readlines = mock.MagicMock(return_value=[x.encode('utf-8') for x in ['', '', '', 'https://example.com']])
+ readlines = mock.MagicMock(
+ return_value=[
+ x.encode('utf-8') for x in
+ ['', '', '', 'https://example.com']])
info = mock.MagicMock()
getcode = mock.MagicMock(return_value=200)
response_obj = MockResponse()
+
+ returns = [None, ([], 'https://example.com')]
+
printer = MagicMock(Printer)
- with patch('urllib.request.urlopen', return_value=response_obj) as urlopen:
- with patch('pirate.torrent.remote', return_value=[]) as remote:
+ with patch('pirate.pirate.connect_mirror',
+ side_effect=returns) as connect:
+ with patch('urllib.request.urlopen', return_value=response_obj):
results, mirror = pirate.pirate.search_mirrors(printer, args)
- self.assertEqual(results, [])
- self.assertEqual(mirror, pirate.data.default_mirror)
- remote.assert_called_once_with(printer=printer, pages=1, category=100, sort=10, mode='browse', terms=[], mirror=pirate.data.default_mirror)
- with patch('pirate.torrent.remote', side_effect=[socket.timeout, []]) as remote:
- results, mirror = pirate.pirate.search_mirrors(printer, args)
- self.assertEqual(results, [])
- self.assertEqual(mirror, 'https://example.com')
- remote.assert_has_calls([
- call(printer=printer, pages=1, category=100, sort=10, mode='browse', terms=[], mirror=pirate.data.default_mirror),
- call(printer=printer, pages=1, category=100, sort=10, mode='browse', terms=[], mirror='https://example.com')
- ])
+
+ connect.assert_has_calls([
+ call(pirate.data.default_mirror, printer, args),
+ call('https://example.com', printer, args)])
+
+ self.assertEqual(results, [])
+ self.assertEqual(mirror, 'https://example.com')
+
if __name__ == '__main__':
unittest.main()
diff --git a/tests/test_print.py b/tests/test_print.py
index feeb59a..ab13920 100755
--- a/tests/test_print.py
+++ b/tests/test_print.py
@@ -1,8 +1,10 @@
#!/usr/bin/env python3
import os
import unittest
-from unittest.mock import patch, call, MagicMock
+import json
+import sys
+from unittest.mock import patch, call, MagicMock
from pirate.print import Printer
@@ -19,17 +21,21 @@ class TestPrint(unittest.TestCase):
mock = MockTable()
printer = Printer(False)
printer.print = MagicMock()
- with patch('veryprettytable.VeryPrettyTable', return_value=mock) as prettytable:
+ with patch('veryprettytable.VeryPrettyTable',
+ return_value=mock) as prettytable:
results = [{
- 'magnet': 'dn=name',
- 'seeds': 1,
+ 'name': 'name',
+ 'seeders': 1,
'leechers': 2,
- 'size': ['3','MiB'],
+ 'size': '3.0 MiB',
'uploaded': 'never'
}]
printer.search_results(results)
- prettytable.assert_called_once_with(['LINK', 'SEED', 'LEECH', 'RATIO', 'SIZE', 'UPLOAD', 'NAME'])
- mock.add_row.assert_has_calls([call([0, 1, 2, '0.5', '3.0 MiB', 'never', 'name'])])
+ prettytable.assert_called_once_with([
+ 'LINK', 'SEED', 'LEECH', 'RATIO',
+ 'SIZE', 'UPLOAD', 'NAME'])
+ mock.add_row.assert_has_calls([
+ call([0, 1, 2, '0.5', '3.0 MiB', 'never', 'name'])])
def test_print_results_local(self):
class MockTable:
@@ -38,29 +44,36 @@ class TestPrint(unittest.TestCase):
mock = MockTable()
printer = Printer(False)
printer.print = MagicMock()
- with patch('veryprettytable.VeryPrettyTable', return_value=mock) as prettytable:
+ with patch('veryprettytable.VeryPrettyTable',
+ return_value=mock) as prettytable:
results = [{
- 'magnet': 'dn=name',
+ 'name': 'name1',
'date': '1',
'size': '1',
- },{
- 'magnet': 'dn=name2',
+ }, {
+ 'name': 'name2',
'date': '2',
'size': '2',
}]
printer.search_results(results, local=True)
- prettytable.assert_called_once_with(['LINK', 'DATE', 'SIZE', 'NAME'])
- mock.add_row.assert_has_calls([call([0, '1', '1', 'name']), call([1, '2', '2', 'name2'])])
+ prettytable.assert_called_once_with(
+ ['LINK', 'DATE', 'SIZE', 'NAME'])
+ mock.add_row.assert_has_calls(
+ [call([0, '1', '1', 'name1']), call([1, '2', '2', 'name2'])])
def test_print_color(self):
printer = Printer(False)
with patch('pirate.print.builtins.print') as mock_print:
printer.print('abc', color='zebra_1')
- mock_print.assert_called_once_with('abc')
+ mock_print.assert_called_once_with(
+ 'abc',
+ file=sys.stderr)
printer = Printer(True)
with patch('pirate.print.builtins.print') as mock_print:
printer.print('abc', color='zebra_1')
- mock_print.assert_called_once_with('\x1b[34mabc', '\x1b[0m')
+ mock_print.assert_called_once_with(
+ '\x1b[34mabc', '\x1b[0m',
+ file=sys.stderr)
def test_print_results_local2(self):
class MockTable:
@@ -69,57 +82,77 @@ class TestPrint(unittest.TestCase):
mock = MockTable()
printer = Printer(True)
printer.print = MagicMock()
- with patch('veryprettytable.VeryPrettyTable', return_value=mock) as prettytable:
+ with patch('veryprettytable.VeryPrettyTable',
+ return_value=mock) as prettytable:
results = [{
- 'magnet': 'dn=name',
+ 'name': 'name1',
'date': '1',
'size': '1',
- },{
- 'magnet': 'dn=name2',
+ }, {
+ 'name': 'name2',
'date': '2',
'size': '2',
}]
printer.search_results(results, local=True)
- prettytable.assert_called_once_with(['LINK', 'DATE', 'SIZE', 'NAME'])
- mock.add_row.assert_has_calls([call([0, '1', '1', 'name']), call([1, '2', '2', 'name2'], fore_color='blue')])
+ prettytable.assert_called_once_with(
+ ['LINK', 'DATE', 'SIZE', 'NAME'])
+ mock.add_row.assert_has_calls([
+ call([0, '1', '1', 'name1']),
+ call([1, '2', '2', 'name2'], fore_color='blue')])
def test_print_descriptions(self):
printer = Printer(False)
printer.print = MagicMock()
+
class MockRequest():
add_header = MagicMock()
request_obj = MockRequest()
+
class MockResponse():
- read = MagicMock(return_value=''.encode('utf8'))
+ read = MagicMock(return_value=json.dumps(
+ {'name': 'cool torrent',
+ 'descr': 'A fake torrent.\n'}))
info = MagicMock()
response_obj = MockResponse()
- class MockOpener():
- open = MagicMock(return_value=response_obj)
- add_handler = MagicMock()
- opener_obj = MockOpener()
- with patch('urllib.request.Request', return_value=request_obj) as request:
- with patch('urllib.request.OpenerDirector', return_value=opener_obj) as opener:
- printer.descriptions([0], [{'id': '1', 'magnet': 'dn=name'}], 'example.com')
- printer.print.assert_has_calls([call('Description for "name":', color='zebra_1'),call('stuff [link](href)', color='zebra_0')])
+
+ with patch('urllib.request.Request', return_value=request_obj):
+ with patch('urllib.request.urlopen',
+ return_value=response_obj):
+ printer.descriptions([0], [{'id': '1', 'name': 'name'}],
+ 'example.com', 9)
+ printer.print.assert_has_calls([
+ call('Description for "name":', color='zebra_1'),
+ call('A fake torrent.\n', color='zebra_0')])
def test_print_file_lists(self):
printer = Printer(False)
printer.print = MagicMock()
+
class MockRequest():
add_header = MagicMock()
+ info = MagicMock()
request_obj = MockRequest()
+
class MockResponse():
- read = MagicMock(return_value='1. | filename |
'.encode('utf8'))
+ read = MagicMock(return_value=json.dumps(
+ [{'name': ['readme.txt'], 'size': [16]},
+ {'name': ['a.mkv'], 'size': [677739464]},
+ {'name': ['b.nfo'], 'size': [61]}]))
info = MagicMock()
response_obj = MockResponse()
- class MockOpener():
- open = MagicMock(return_value=response_obj)
- add_handler = MagicMock()
- opener_obj = MockOpener()
- with patch('urllib.request.Request', return_value=request_obj) as request:
- with patch('urllib.request.OpenerDirector', return_value=opener_obj) as opener:
- printer.file_lists([0], [{'id': '1', 'magnet': 'dn=name'}], 'example.com')
- printer.print.assert_has_calls([call('Files in "name":', color='zebra_1'),call(' 1. filename', color='zebra_0')])
+
+ with patch('urllib.request.Request',
+ return_value=request_obj):
+ with patch('urllib.request.urlopen',
+ return_value=response_obj):
+ printer.file_lists([0], [{'id': '1', 'name': 'name'}],
+ 'example.com', 9)
+ printer.print.assert_has_calls([
+ call('Files in name:', color='zebra_1'),
+ call(' 16 B readme.txt', color='zebra_0'),
+ call(' 646.3 MiB a.mkv', color='zebra_1'),
+ call(' 61 B b.nfo', color='zebra_0')])
+
if __name__ == '__main__':
unittest.main()
diff --git a/tests/test_torrent.py b/tests/test_torrent.py
index 9939e16..a146cc9 100755
--- a/tests/test_torrent.py
+++ b/tests/test_torrent.py
@@ -2,48 +2,43 @@
import unittest
from unittest import mock
from unittest.mock import patch, MagicMock
-import os
import io
import urllib
+import json
+import os
+import time
import pirate.torrent
import pirate.data
from pirate.print import Printer
from tests import util
+
class TestTorrent(unittest.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ # to make test deterministic
+ os.environ['TZ'] = 'Etc/UTC'
+ time.tzset()
+
def test_no_hits(self):
- res = util.read_data('no_hits.html')
- actual = pirate.torrent.parse_page(res)
expected = []
+ with util.open_data('no_hits.json') as res:
+ actual = pirate.torrent.parse_page(res)
self.assertEqual(actual, expected)
def test_blocked_mirror(self):
- res = util.read_data('blocked.html')
- with self.assertRaises(IOError):
- pirate.torrent.parse_page(res)
+ with util.open_data('blocked.html') as res:
+ with self.assertRaises(IOError):
+ pirate.torrent.parse_page(res)
def test_search_results(self):
- res = util.read_data('dan_bull_search.html')
- actual = pirate.torrent.parse_page(res)
- expected = [
- {'uploaded': '04-04\xa02014', 'seeds': '16', 'leechers': '1', 'id': '9890864', 'magnet': 'magnet:?xt=urn:btih:30df4f8b42b8fd77f5e5aa34abbffe97f5e81fbf&dn=Dan+Croll+%26bull%3B+Sweet+Disarray+%5B2014%5D+320&tr=udp%3A%2F%2Ftracker.openbittorrent.com%3A80&tr=udp%3A%2F%2Fopen.demonii.com%3A1337&tr=udp%3A%2F%2Ftracker.coppersurfer.tk%3A6969&tr=udp%3A%2F%2Fexodus.desync.com%3A6969', 'size': ['89.33', 'MiB']},
- {'uploaded': '03-02\xa02014', 'seeds': '4', 'leechers': '0', 'id': '9684858', 'magnet': 'magnet:?xt=urn:btih:7abd3eda600996b8e6fc9a61b83288e0c6ac0d83&dn=Dan+Bull+-+Massive+Collection&tr=udp%3A%2F%2Ftracker.openbittorrent.com%3A80&tr=udp%3A%2F%2Fopen.demonii.com%3A1337&tr=udp%3A%2F%2Ftracker.coppersurfer.tk%3A6969&tr=udp%3A%2F%2Fexodus.desync.com%3A6969', 'size': ['294', 'MiB']},
- {'uploaded': '01-19\xa02013', 'seeds': '2', 'leechers': '0', 'id': '8037968', 'magnet': 'magnet:?xt=urn:btih:8f8d68fd0a51237c89692c428ed8a8f64a969c70&dn=Dan+Bull+-+Generation+Gaming+-+2013&tr=udp%3A%2F%2Ftracker.openbittorrent.com%3A80&tr=udp%3A%2F%2Fopen.demonii.com%3A1337&tr=udp%3A%2F%2Ftracker.coppersurfer.tk%3A6969&tr=udp%3A%2F%2Fexodus.desync.com%3A6969', 'size': ['54.86', 'MiB']},
- {'uploaded': '01-21\xa02010', 'seeds': '1', 'leechers': '0', 'id': '5295449', 'magnet': 'magnet:?xt=urn:btih:3da6a0fdc1d67a768cb32597e926abdf3e1a2fdd&dn=Dan+Bull+Collection&tr=udp%3A%2F%2Ftracker.openbittorrent.com%3A80&tr=udp%3A%2F%2Fopen.demonii.com%3A1337&tr=udp%3A%2F%2Ftracker.coppersurfer.tk%3A6969&tr=udp%3A%2F%2Fexodus.desync.com%3A6969', 'size': ['236.78', 'MiB']},
- {'uploaded': '09-02\xa02014', 'seeds': '1', 'leechers': '0', 'id': '10954408', 'magnet': 'magnet:?xt=urn:btih:5cd371a235317319db7da52c64422f9c2ac75d77&dn=Dan+Bull+-+The+Garden+%7B2014-Album%7D&tr=udp%3A%2F%2Ftracker.openbittorrent.com%3A80&tr=udp%3A%2F%2Fopen.demonii.com%3A1337&tr=udp%3A%2F%2Ftracker.coppersurfer.tk%3A6969&tr=udp%3A%2F%2Fexodus.desync.com%3A6969', 'size': ['36.27', 'MiB']},
- {'uploaded': '09-27\xa02009', 'seeds': '0', 'leechers': '1', 'id': '5101630', 'magnet': 'magnet:?xt=urn:btih:4e14dbd077c920875be4c15971b23b609ad6716a&dn=Dan+Bull+-+Dear+Lily+%5Ban+open+letter+to+Lily+Allen%5D+-+2009%5BMP3+%40&tr=udp%3A%2F%2Ftracker.openbittorrent.com%3A80&tr=udp%3A%2F%2Fopen.demonii.com%3A1337&tr=udp%3A%2F%2Ftracker.coppersurfer.tk%3A6969&tr=udp%3A%2F%2Fexodus.desync.com%3A6969', 'size': ['5.51', 'MiB']},
- {'uploaded': '11-29\xa02009', 'seeds': '0', 'leechers': '0', 'id': '5185893', 'magnet': 'magnet:?xt=urn:btih:5d9319cf852f7462422cb1bffc37b65174645047&dn=Dan+Bull+-+Dear+Mandy+%5Ban+open+letter+to+Lord+Mandelson%5D&tr=udp%3A%2F%2Ftracker.openbittorrent.com%3A80&tr=udp%3A%2F%2Fopen.demonii.com%3A1337&tr=udp%3A%2F%2Ftracker.coppersurfer.tk%3A6969&tr=udp%3A%2F%2Fexodus.desync.com%3A6969', 'size': ['5.07', 'MiB']},
- {'uploaded': '11-10\xa02011', 'seeds': '0', 'leechers': '0', 'id': '6806996', 'magnet': 'magnet:?xt=urn:btih:1c54af57426f53fdef4bbf1a9dbddf32f7b4988a&dn=Dan+Bull+-+Dear+Lily+%28Lily+Allen%29+%28Song+about+filesharing%29&tr=udp%3A%2F%2Ftracker.openbittorrent.com%3A80&tr=udp%3A%2F%2Fopen.demonii.com%3A1337&tr=udp%3A%2F%2Ftracker.coppersurfer.tk%3A6969&tr=udp%3A%2F%2Fexodus.desync.com%3A6969', 'size': ['5.34', 'MiB']},
- {'uploaded': '12-20\xa02011', 'seeds': '0', 'leechers': '0', 'id': '6901871', 'magnet': 'magnet:?xt=urn:btih:942c5bf3e1e9bc263939e13cea6ad7bd5f62aa36&dn=Dan+Bull+-+SOPA+Cabana.mp3&tr=udp%3A%2F%2Ftracker.openbittorrent.com%3A80&tr=udp%3A%2F%2Fopen.demonii.com%3A1337&tr=udp%3A%2F%2Ftracker.coppersurfer.tk%3A6969&tr=udp%3A%2F%2Fexodus.desync.com%3A6969', 'size': ['4.8', 'MiB']},
- {'uploaded': '12-21\xa02011', 'seeds': '0', 'leechers': '1', 'id': '6902247', 'magnet': 'magnet:?xt=urn:btih:d376f68a31b0db652234e790ed7256ac5e32db57&dn=Dan+Bull+-+SOPA+Cabana&tr=udp%3A%2F%2Ftracker.openbittorrent.com%3A80&tr=udp%3A%2F%2Fopen.demonii.com%3A1337&tr=udp%3A%2F%2Ftracker.coppersurfer.tk%3A6969&tr=udp%3A%2F%2Fexodus.desync.com%3A6969', 'size': ['3.4', 'MiB']},
- {'uploaded': '12-21\xa02011', 'seeds': '0', 'leechers': '1', 'id': '6903548', 'magnet': 'magnet:?xt=urn:btih:28163770a532eb24b9e0865878288a9bbdb7a5e6&dn=Dan+Bull+-+SOPA+Cabana+%5BWORKING%5D&tr=udp%3A%2F%2Ftracker.openbittorrent.com%3A80&tr=udp%3A%2F%2Fopen.demonii.com%3A1337&tr=udp%3A%2F%2Ftracker.coppersurfer.tk%3A6969&tr=udp%3A%2F%2Fexodus.desync.com%3A6969', 'size': ['4.8', 'MiB']},
- {'uploaded': '03-09\xa02012', 'seeds': '0', 'leechers': '1', 'id': '7088979', 'magnet': 'magnet:?xt=urn:btih:779ab0f13a3fbb12ba68b27721491e4d143f26eb&dn=Dan+Bull+-+Bye+Bye+BPI+2012++%5BMP3%40192%5D%28oan%29&tr=udp%3A%2F%2Ftracker.openbittorrent.com%3A80&tr=udp%3A%2F%2Fopen.demonii.com%3A1337&tr=udp%3A%2F%2Ftracker.coppersurfer.tk%3A6969&tr=udp%3A%2F%2Fexodus.desync.com%3A6969', 'size': ['60.72', 'MiB']},
- {'uploaded': '10-24\xa02012', 'seeds': '0', 'leechers': '0', 'id': '7756344', 'magnet': 'magnet:?xt=urn:btih:2667e4795bd5c868dedcabcb52943f4bb7212bab&dn=Dan+Bull+-+Dishonored+%5BExplicit+ver.%5D+%28Single+2012%29&tr=udp%3A%2F%2Ftracker.openbittorrent.com%3A80&tr=udp%3A%2F%2Fopen.demonii.com%3A1337&tr=udp%3A%2F%2Ftracker.coppersurfer.tk%3A6969&tr=udp%3A%2F%2Fexodus.desync.com%3A6969', 'size': ['6.29', 'MiB']},
- {'uploaded': '11-10\xa02012', 'seeds': '0', 'leechers': '0', 'id': '7812951', 'magnet': 'magnet:?xt=urn:btih:16364f83c556ad0fd3bb57a4a7c890e7e8087414&dn=Halo+4+EPIC+Rap+By+Dan+Bull&tr=udp%3A%2F%2Ftracker.openbittorrent.com%3A80&tr=udp%3A%2F%2Fopen.demonii.com%3A1337&tr=udp%3A%2F%2Ftracker.coppersurfer.tk%3A6969&tr=udp%3A%2F%2Fexodus.desync.com%3A6969', 'size': ['6.41', 'MiB']},
- {'uploaded': '01-19\xa02013', 'seeds': '0', 'leechers': '1', 'id': '8037899', 'magnet': 'magnet:?xt=urn:btih:843b466d9fd1f0bee3a476573b272dc2d6d0ebae&dn=Dan+Bull+-+Generation+Gaming+-+2013&tr=udp%3A%2F%2Ftracker.openbittorrent.com%3A80&tr=udp%3A%2F%2Fopen.demonii.com%3A1337&tr=udp%3A%2F%2Ftracker.coppersurfer.tk%3A6969&tr=udp%3A%2F%2Fexodus.desync.com%3A6969', 'size': ['54.87', 'MiB']}
- ]
+ with util.open_data('result.json') as file:
+ expected = json.load(file)
+ with util.open_data('debian_iso.json') as res:
+ actual = pirate.torrent.parse_page(res)
+ json.dump(actual, open('result.json', 'w'))
self.assertEqual(actual, expected)
def test_parse_category(self):
@@ -60,83 +55,113 @@ class TestTorrent(unittest.TestCase):
def test_parse_sort(self):
sort = pirate.torrent.parse_sort(MagicMock(Printer), 'SeedersDsc')
- self.assertEqual(7, sort)
+ self.assertEqual(['seeders', True], sort)
+ sort = pirate.torrent.parse_sort(MagicMock(Printer), 'CategoryAsc')
+ self.assertEqual(['category', False], sort)
+ sort = pirate.torrent.parse_sort(MagicMock(Printer), 'DateAsc')
+ self.assertEqual(['raw_uploaded', False], sort)
sort = pirate.torrent.parse_sort(MagicMock(Printer), '7')
- self.assertEqual(7, sort)
+ self.assertEqual(['seeders', True], sort)
sort = pirate.torrent.parse_sort(MagicMock(Printer), 'asdf')
- self.assertEqual(99, sort)
+ self.assertEqual(['seeders', True], sort)
sort = pirate.torrent.parse_sort(MagicMock(Printer), '7000')
- self.assertEqual(99, sort)
+ self.assertEqual(['seeders', True], sort)
def test_request_path(self):
- # the args are (page, category, sort, mode, terms)
- tests = [
- ((0, 100, 10, 'browse', []), '/browse/100/0/10'),
- ((0, 0, 10, 'browse', []), '/browse/100/0/10'),
- ((0, 0, 10, 'recent', []), '/top/48hall'),
- ((0, 100, 10, 'recent', []), '/top/48h100'),
- ((0, 100, 10, 'top', []), '/top/100'),
- ((0, 0, 10, 'top', []), '/top/all'),
- ((0, 100, 10, 'search', ['abc']), '/search/abc/0/10/100'),
- ((0, 100, 10, 'search', ['abc', 'def']), '/search/abc+def/0/10/100'),
- ((0, 100, 10, 'search', [u'\u1234']), '/search/%E1%88%B4/0/10/100'),
- ((0, 100, 10, 'asdf', []), Exception),
+ # the args are (mode, category, terms)
+ succeed = [
+ (('recent', 1, 0, []), '/precompiled/data_top100_recent_1.json'),
+ (('recent', 2, 100, []), '/precompiled/data_top100_recent_2.json'),
+ (('top', 1, 0, []), '/precompiled/data_top100_all.json'),
+ (('top', 1, 100, []), '/precompiled/data_top100_100.json'),
+ (('search', 1, 100, ['abc']), '/q.php?q=abc&cat=100'),
+ (('search', 1, 100, ['abc', 'def']), '/q.php?q=abc%20def&cat=100'),
+ (('search', 1, 100, ['\u1234']), '/q.php?q=%E1%88%B4&cat=100'),
+ (('browse', 1, 100, []), '/q.php?q=category%3A100'),
]
- for test in tests:
- if test[1] != Exception:
- path = pirate.torrent.build_request_path(*test[0])
- self.assertEqual(test[1], path)
- else:
- with self.assertRaises(test[1]):
- pirate.torrent.build_request_path(test[0])
+ fail = [
+ (('browse', 1, 0, []), Exception),
+ (('asdf', 1, 100, []), Exception)
+ ]
+ for inp, out in succeed:
+ path = pirate.torrent.build_request_path(*inp)
+ self.assertEqual(out, path)
+ for inp, out, in fail:
+ with self.assertRaises(out):
+ pirate.torrent.build_request_path(*inp)
@patch('pirate.torrent.get_torrent')
def test_save_torrents(self, get_torrent):
- with patch('pirate.torrent.open', mock.mock_open(), create=True) as open_:
- magnet = 'magnet:?xt=urn:btih:335fcd3cfbecc85554616d73de888033c6c16d37&dn=Test+Drive+Unl\im/ited+%5BPC+Version%5D&tr=udp%3A%2F%2Ftracker.openbittorrent.com%3A80&tr=udp%3A%2F%2Fopen.demonii.com%3A1337&tr=udp%3A%2F%2Ftracker.coppersurfer.tk%3A6969&tr=udp%3A%2F%2Fexodus.desync.com%3A6969'
- pirate.torrent.save_torrents(MagicMock(Printer), [0], [{'magnet':magnet}], 'path')
- get_torrent.assert_called_once_with(293294978876299923284263767676068334936407502135)
- open_.assert_called_once_with('path/Test Drive Unl_im_ited [PC Version].torrent', 'wb')
+ with patch('pirate.torrent.open',
+ mock.mock_open(), create=True) as open_:
+ pirate.torrent.save_torrents(
+ MagicMock(Printer), [0],
+ [{'name': 'cool torrent',
+ 'info_hash': 3735928559,
+ 'magnet': 'magnet:?xt=urn:btih:deadbeef'}], 'path', 9)
+ get_torrent.assert_called_once_with(3735928559, 9)
+ open_.assert_called_once_with('path/cool torrent.torrent', 'wb')
- @patch('pirate.torrent.get_torrent', side_effect=urllib.error.HTTPError('', '', '', '', io.StringIO()))
+ @patch('pirate.torrent.get_torrent',
+ side_effect=urllib.error.HTTPError('', '', '', '', io.StringIO()))
def test_save_torrents_fail(self, get_torrent):
- magnet = 'magnet:?xt=urn:btih:335fcd3cfbecc85554616d73de888033c6c16d37&dn=Test+Drive+Unlimited+%5BPC+Version%5D&tr=udp%3A%2F%2Ftracker.openbittorrent.com%3A80&tr=udp%3A%2F%2Fopen.demonii.com%3A1337&tr=udp%3A%2F%2Ftracker.coppersurfer.tk%3A6969&tr=udp%3A%2F%2Fexodus.desync.com%3A6969'
- pirate.torrent.save_torrents(MagicMock(Printer), [0], [{'magnet':magnet}], 'path')
+ pirate.torrent.save_torrents(
+ MagicMock(Printer), [0],
+ [{'name': 'cool torrent',
+ 'info_hash': 3735928559,
+ 'magnet': 'magnet:?xt=urn:btih:deadbeef'}], 'path', 9)
def test_save_magnets(self):
- with patch('pirate.torrent.open', mock.mock_open(), create=True) as open_:
- magnet = 'magnet:?xt=urn:btih:335fcd3cfbecc85554616d73de888033c6c16d37&dn=Test+Drive+Unl\im/ited+%5BPC+Version%5D&tr=udp%3A%2F%2Ftracker.openbittorrent.com%3A80&tr=udp%3A%2F%2Fopen.demonii.com%3A1337&tr=udp%3A%2F%2Ftracker.coppersurfer.tk%3A6969&tr=udp%3A%2F%2Fexodus.desync.com%3A6969'
- pirate.torrent.save_magnets(MagicMock(Printer), [0], [{'magnet':magnet}], 'path')
- open_.assert_called_once_with('path/Test Drive Unl_im_ited [PC Version].magnet', 'w')
+ with patch('pirate.torrent.open',
+ mock.mock_open(), create=True) as open_:
+ pirate.torrent.save_magnets(
+ MagicMock(Printer), [0],
+ [{'name': 'cool torrent',
+ 'info_hash': 3735928559,
+ 'magnet': 'magnet:?xt=urn:btih:deadbeef'}], 'path')
+ open_.assert_called_once_with('path/cool torrent.magnet', 'w')
@patch('urllib.request.urlopen')
def test_get_torrent(self, urlopen):
class MockRequest():
add_header = mock.MagicMock()
request_obj = MockRequest()
- with patch('urllib.request.Request', return_value=request_obj) as request:
- pirate.torrent.get_torrent(100000000000000)
- request.assert_called_once_with('http://itorrents.org/torrent/5AF3107A4000.torrent', headers=pirate.data.default_headers)
- urlopen.assert_called_once_with(request_obj, timeout=pirate.data.default_timeout)
+ with patch('urllib.request.Request', return_value=request_obj) as req:
+ pirate.torrent.get_torrent(100000000000000, 9)
+ req.assert_called_once_with(
+ 'http://itorrents.org/torrent/5AF3107A4000.torrent',
+ headers=pirate.data.default_headers)
+ urlopen.assert_called_once_with(
+ request_obj,
+ timeout=9)
def test_remote(self):
class MockRequest():
add_header = mock.MagicMock()
- request_obj = MockRequest()
+ req_obj = MockRequest()
+
+ class MockInfo():
+ get_content_type = mock.MagicMock(return_value='application/json')
+ get = mock.MagicMock()
+
class MockResponse():
- read = mock.MagicMock(return_value=b'No hits. Try adding an asterisk in you search phrase.')
- info = mock.MagicMock()
- response_obj = MockResponse()
- class MockOpener():
- open = mock.MagicMock(return_value=response_obj)
- add_handler = mock.MagicMock()
- opener_obj = MockOpener()
- with patch('urllib.request.Request', return_value=request_obj) as request:
- with patch('urllib.request.OpenerDirector', return_value=opener_obj) as opener:
- res = pirate.torrent.remote(MagicMock(Printer), 1, 100, 10, 'browse', [], 'http://example.com')
- request.assert_called_once_with('http://example.com/browse/100/0/10', headers=pirate.data.default_headers)
- opener_obj.open.assert_called_once_with(request_obj, timeout=pirate.data.default_timeout)
- self.assertEqual(res, [])
+ read = mock.MagicMock(return_value=b'[]')
+ info = mock.MagicMock(return_value=MockInfo())
+ res_obj = MockResponse()
+
+ sort = pirate.torrent.parse_sort(MagicMock(Printer), 10)
+
+ with patch('urllib.request.Request', return_value=req_obj) as req:
+ with patch('urllib.request.urlopen', return_value=res_obj) as res:
+ results = pirate.torrent.remote(
+ MagicMock(Printer), 1, 100, sort, 'top',
+ [], 'http://example.com', 9)
+ req.assert_called_once_with(
+ 'http://example.com/precompiled/data_top100_100.json',
+ headers=pirate.data.default_headers)
+ res.assert_called_once_with(req_obj, timeout=9)
+ self.assertEqual(results, [])
+
if __name__ == '__main__':
unittest.main()
diff --git a/tests/util.py b/tests/util.py
index 26f0fbe..c5b3f21 100644
--- a/tests/util.py
+++ b/tests/util.py
@@ -3,6 +3,5 @@ import os
def data_path(name):
return os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data', name)
-def read_data(name):
- with open(data_path(name)) as f:
- return f.read()
+def open_data(name):
+ return open(data_path(name))