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)

- -
- - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Type
Name (Order by: Uploaded, Size, ULed by, SE, LE)
View: Single / Double 
SELE
-
- Audio
- (Music) -
-
- -Magnet linkTrusted - Uploaded 04-04 2014, Size 89.33 MiB, ULed by Capajebo - 161
-
- Audio
- (Other) -
-
- -Magnet linkThis torrent has 1 comments. - Uploaded 03-02 2014, Size 294 MiB, ULed by Vakume - 40
-
- Audio
- (Music) -
-
- -Magnet link - Uploaded 01-19 2013, Size 54.86 MiB, ULed by blowingfish - 20
-
- Audio
- (Other) -
-
- -Magnet linkThis torrent has 11 comments. - Uploaded 01-21 2010, Size 236.78 MiB, ULed by SuperSaru - 10
-
- Audio
- (Music) -
-
- -Magnet link - Uploaded 09-02 2014, Size 36.27 MiB, ULed by Bazookus - 10
-
- Audio
- (Music) -
-
- -Magnet linkThis torrent has 1 comments.VIP - Uploaded 09-27 2009, Size 5.51 MiB, ULed by oneanight - 01
-
- Audio
- (Music) -
-
- -Magnet linkThis torrent has 1 comments. - Uploaded 11-29 2009, Size 5.07 MiB, ULed by epiclawl - 00
-
- Audio
- (Music) -
-
- -Magnet link - Uploaded 11-10 2011, Size 5.34 MiB, ULed by Imperator42 - 00
-
- Audio
- (Music) -
-
- -Magnet link - Uploaded 12-20 2011, Size 4.8 MiB, ULed by lerdie - 00
-
- Audio
- (Music) -
-
- -Magnet linkThis torrent has 1 comments. - Uploaded 12-21 2011, Size 3.4 MiB, ULed by mattdow - 01
-
- Audio
- (Music) -
-
- -Magnet linkThis torrent has 3 comments. - Uploaded 12-21 2011, Size 4.8 MiB, ULed by lerdie - 01
-
- Audio
- (Other) -
-
- -Magnet linkThis torrent has 1 comments.VIP - Uploaded 03-09 2012, Size 60.72 MiB, ULed by oneanight - 01
-
- Audio
- (Music) -
-
- -Magnet linkThis torrent has 1 comments. - Uploaded 10-24 2012, Size 6.29 MiB, ULed by PIRATE300 - 00
-
- Audio
- (Music) -
-
- -Magnet linkThis torrent has 1 comments. - Uploaded 11-10 2012, Size 6.41 MiB, ULed by AdpoX10 - 00
-
- Audio
- (Other) -
-
- -Magnet linkThis torrent has 2 comments. - Uploaded 01-19 2013, Size 54.87 MiB, ULed by blowingfish - 01
-
-
-
- - - - - - \ 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='
stuff link
'.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))