From 1f0470015829c534669838335a92351b3087e2cd Mon Sep 17 00:00:00 2001 From: Viktor Stanchev Date: Thu, 3 Sep 2015 22:18:38 -0700 Subject: [PATCH] simplify results array --- pirate/pirate.py | 28 ++++++++++++++-------------- pirate/print.py | 42 ++++++++++++++++++++++++++---------------- pirate/torrent.py | 27 ++++++++++++--------------- tests/test_torrent.py | 4 ++-- 4 files changed, 54 insertions(+), 47 deletions(-) diff --git a/pirate/pirate.py b/pirate/pirate.py index c9070d4..ab3fba8 100755 --- a/pirate/pirate.py +++ b/pirate/pirate.py @@ -171,11 +171,11 @@ def main(): path = args.database else: path = config.get('LocalDB', 'path') - mags = pirate.local.search(path, args.search) + results = pirate.local.search(path, args.search) sizes, uploaded = [], [] else: - mags, mirrors = [], {'https://thepiratebay.mn'} + results, mirrors = [], {'https://thepiratebay.mn'} try: req = request.Request('https://proxybay.co/list.txt', headers=pirate.data.default_headers) @@ -192,7 +192,7 @@ def main(): for mirror in mirrors: try: print('Trying', mirror, end='... \n') - mags, sizes, uploaded, ids = pirate.torrent.remote( + results = pirate.torrent.remote( pages=args.pages, category=pirate.torrent.parse_category(args.category), sort=pirate.torrent.parse_sort(args.sort), @@ -211,18 +211,18 @@ def main(): print('No available mirrors :(', color='WARN') return - if not mags: + if len(results) == 0: print('No results') return - pirate.print.search_results(mags, sizes, uploaded, local=args.database) + pirate.print.search_results(results, local=args.database) if args.first: print('Choosing first result') choices = [0] elif args.download_all: print('Downloading all results') - choices = range(len(mags)) + choices = range(len(results)) else: # New input loop to support different link options while True: @@ -284,16 +284,16 @@ def main(): print('Bye.', color='alt') return elif code == 'd': - pirate.print.descriptions(choices, mags, site, ids) + pirate.print.descriptions(choices, results, site) elif code == 'f': - pirate.print.file_lists(choices, mags, site, ids) + pirate.print.file_lists(choices, results, site) elif code == 'p': - pirate.print.search_results(mags, sizes, uploaded) + pirate.print.search_results(results) elif code == 'm': - pirate.torrent.save_magnets(choices, mags, config.get( + pirate.torrent.save_magnets(choices, results, config.get( 'Save', 'directory')) elif code == 't': - pirate.torrent.save_torrents(choices, mags, config.get( + pirate.torrent.save_torrents(choices, results, config.get( 'Save', 'directory')) elif not l: print('No links entered!', color='WARN') @@ -307,13 +307,13 @@ def main(): if args.save_magnets or config.getboolean('Save', 'magnets'): print('Saving selected magnets...') - pirate.torrent.save_magnets(choices, mags, config.get( + pirate.torrent.save_magnets(choices, results, config.get( 'Save', 'directory')) save_to_file = True if args.save_torrents or config.getboolean('Save', 'torrents'): print('Saving selected torrents...') - pirate.torrent.save_torrents(choices, mags, config.get( + pirate.torrent.save_torrents(choices, results, config.get( 'Save', 'directory')) save_to_file = True @@ -321,7 +321,7 @@ def main(): return for choice in choices: - url = mags[int(choice)][0] + url = results[int(choice)]['magnet'] if args.transmission or config.getboolean('Misc', 'transmission'): subprocess.call(transmission_command + ['--add', url]) diff --git a/pirate/print.py b/pirate/print.py index 9d3f4a0..7e19ba3 100644 --- a/pirate/print.py +++ b/pirate/print.py @@ -31,7 +31,7 @@ def print(*args, **kwargs): return builtins.print(*args, **kwargs) -def search_results(mags, sizes, uploaded, local=None): +def search_results(results, local=None): columns = int(os.popen('stty size', 'r').read().split()[1]) cur_color = 'zebra_0' @@ -45,21 +45,26 @@ def search_results(mags, sizes, uploaded, local=None): 'SIZE', 'UPLOAD', 'NAME', length=columns - 52), color='header') - for m, magnet in enumerate(mags): + for n, result in enumerate(results): # Alternate between colors cur_color = 'zebra_0' if cur_color == 'zebra_1' else 'zebra_1' - name = re.search(r'dn=([^\&]*)', magnet[0]) - torrent_name = parse.unquote(name.group(1)).replace('+', ' ') + name = re.search(r'dn=([^\&]*)', result['magnet']) + torrent_name = parse.unquote_plus(name.group(1)) if local: line = '{:5} {:{length}}' - content = [m, torrent_name[:columns]] + content = [n, torrent_name[:columns]] else: - no_seeders, no_leechers = map(int, magnet[1:]) - size, unit = (float(sizes[m][0]), - sizes[m][1]) if sizes else (0, '???') - date = uploaded[m] + no_seeders = int(result['seeds']) + no_leechers = int(result['leechers']) + if result['size'] != []: + size = float(result['size'][0]) + unit = result['size'][1] + else: + size = 0 + unit = '???' + date = result['uploaded'] # compute the S/L ratio (Higher is better) try: @@ -69,17 +74,17 @@ def search_results(mags, sizes, uploaded, local=None): line = ('{:4} {:5} {:5} {:5.1f} {:5.1f}' ' {:3} {:<11} {:{length}}') - content = [m, no_seeders, no_leechers, ratio, + content = [n, no_seeders, no_leechers, ratio, size, unit, date, torrent_name[:columns - 52]] # enhanced print output with justified columns print(line.format(*content, length=columns - 52), color=cur_color) -def descriptions(chosen_links, mags, site, identifiers): +def descriptions(chosen_links, results, site): for link in chosen_links: link = int(link) - path = '/torrent/%s/' % identifiers[link] + path = '/torrent/%s/' % results[link]['id'] req = request.Request(site + path, headers=pirate.data.default_headers) req.add_header('Accept-encoding', 'gzip') f = request.urlopen(req, timeout=pirate.data.default_timeout) @@ -88,7 +93,7 @@ def descriptions(chosen_links, mags, site, identifiers): f = gzip.GzipFile(fileobj=BytesIO(f.read())) res = f.read().decode('utf-8') - name = re.search(r'dn=([^\&]*)', mags[link][0]) + 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) @@ -101,10 +106,11 @@ def descriptions(chosen_links, mags, site, identifiers): print(desc, color='zebra_0') -def file_lists(chosen_links, mags, site, identifiers): +def file_lists(chosen_links, results, site): for link in chosen_links: + link = int(link) path = '/ajax_details_filelist.php' - query = '?id=' + identifiers[int(link)] + query = '?id=' + results[link]['id'] req = request.Request(site + path + query, headers=pirate.data.default_headers) req.add_header('Accept-encoding', 'gzip') @@ -113,10 +119,14 @@ def file_lists(chosen_links, mags, site, identifiers): 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: + print('File list not available.') + return files = re.findall(r'\s*([^<]+?)\s*\s*([^<]+?)\s*', res) - name = re.search(r'dn=([^\&]*)', mags[int(link)][0]) + name = re.search(r'dn=([^\&]*)', results[link]['magnet']) torrent_name = parse.unquote(name.group(1)).replace('+', ' ') print('Files in "%s":' % torrent_name, color='zebra_1') diff --git a/pirate/torrent.py b/pirate/torrent.py index eb62f21..ab70e8b 100644 --- a/pirate/torrent.py +++ b/pirate/torrent.py @@ -75,6 +75,7 @@ def build_request_path(page, category, sort, mode, terms): raise Exception('Unknown mode.') +# this returns a list of dictionaries def parse_page(html): d = pq(html) @@ -97,7 +98,7 @@ def parse_page(html): d('table#searchResult tr>td:nth-child(3)'))) leechers = list(map(lambda l: pq(l).text(), d('table#searchResult tr>td:nth-child(4)'))) - identifiers = list(map(lambda l: pq(l).attr('href').split('/')[2], + ids = list(map(lambda l: pq(l).attr('href').split('/')[2], d('table#searchResult .detLink'))) sizes = [] @@ -108,14 +109,13 @@ def parse_page(html): sizes.append(re.findall(r'(?<=Size )[0-9.]+\s[KMGT]*[i ]*B', text)[0].split()) uploaded.append(re.findall(r'(?<=Uploaded ).+(?=\, Size)', text)[0]) - return list(zip(magnets,seeds,leechers)), sizes, uploaded, identifiers + titles = ('magnet', 'seeds', 'leechers', 'size', 'uploaded', 'id') + rows = list(zip(magnets, seeds, leechers, sizes, uploaded, ids)) + return [dict(zip(titles,row)) for row in rows] def remote(pages, category, sort, mode, terms, mirror): res_l = [] - sizes = [] - uploaded = [] - identifiers = [] if pages < 1: raise ValueError('Please provide an integer greater than 0 ' @@ -134,18 +134,13 @@ def remote(pages, category, sort, mode, terms, mirror): f = gzip.GzipFile(fileobj=BytesIO(f.read())) res = f.read().decode('utf-8') - page_res_l, page_sizes, page_uploaded, page_identifiers = parse_page(res) - res_l += page_res_l - sizes += page_sizes - uploaded += page_uploaded - identifiers += page_identifiers + res_l += parse_page(res) except KeyboardInterrupt: print('\nCancelled.') sys.exit(0) - # return the sizes in a separate list - return res_l, sizes, uploaded, identifiers + return res_l def get_torrent(info_hash): @@ -161,9 +156,10 @@ def get_torrent(info_hash): return torrent.read() -def save_torrents(chosen_links, mags, folder): +def save_torrents(chosen_links, results, folder): for link in chosen_links: - magnet = mags[int(link)][0] + link = int(link) + 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) @@ -180,7 +176,8 @@ def save_torrents(chosen_links, mags, folder): def save_magnets(chosen_links, mags, folder): for link in chosen_links: - magnet = mags[int(link)][0] + link = int(link) + 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) diff --git a/tests/test_torrent.py b/tests/test_torrent.py index 06c62fd..6ca7c5c 100755 --- a/tests/test_torrent.py +++ b/tests/test_torrent.py @@ -11,7 +11,7 @@ class TestTorrent(unittest.TestCase): def test_no_hits(self): res = util.read_data('no_hits.html') actual = pirate.torrent.parse_page(res) - expected = ([], [], [], []) + expected = [] self.assertEqual(actual, expected) def test_blocked_mirror(self): @@ -22,7 +22,7 @@ class TestTorrent(unittest.TestCase): def test_search_results(self): res = util.read_data('dan_bull_search.html') actual = pirate.torrent.parse_page(res) - expected = ([('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', '16', '1'), ('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', '4', '0'), ('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', '2', '0'), ('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', '1', '0'), ('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', '1', '0'), ('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', '0', '1'), ('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', '0', '0'), ('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', '0', '0'), ('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', '0', '0'), ('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', '0', '1'), ('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', '0', '1'), ('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', '0', '1'), ('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', '0', '0'), ('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', '0', '0'), ('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', '0', '1')], [['89.33', 'MiB'], ['294', 'MiB'], ['54.86', 'MiB'], ['236.78', 'MiB'], ['36.27', 'MiB'], ['5.51', 'MiB'], ['5.07', 'MiB'], ['5.34', 'MiB'], ['4.8', 'MiB'], ['3.4', 'MiB'], ['4.8', 'MiB'], ['60.72', 'MiB'], ['6.29', 'MiB'], ['6.41', 'MiB'], ['54.87', 'MiB']],['04-04\xa02014', '03-02\xa02014', '01-19\xa02013', '01-21\xa02010', '09-02\xa02014', '09-27\xa02009', '11-29\xa02009', '11-10\xa02011', '12-20\xa02011', '12-21\xa02011', '12-21\xa02011', '03-09\xa02012', '10-24\xa02012', '11-10\xa02012', '01-19\xa02013'], ['9890864', '9684858', '8037968', '5295449', '10954408', '5101630', '5185893', '6806996', '6901871', '6902247', '6903548', '7088979', '7756344', '7812951', '8037899']) + 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']}] self.assertEqual(actual, expected) if __name__ == '__main__':