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__':
|