1
0
mirror of https://github.com/vikstrous/pirate-get synced 2025-01-10 10:04:21 +01:00
pirate-get/pirate/pirate.py

395 lines
14 KiB
Python
Raw Normal View History

2015-08-30 03:28:43 +02:00
import re
import os
import argparse
import subprocess
import configparser
2015-08-30 04:00:42 +02:00
import socket
2015-08-30 03:28:43 +02:00
import urllib.request as request
import urllib.error
2015-09-15 05:21:13 +02:00
import sys
2015-09-20 07:27:05 +02:00
from collections import OrderedDict
2015-09-15 05:21:13 +02:00
2015-08-30 03:28:43 +02:00
import webbrowser
import pirate.data
import pirate.torrent
import pirate.local
import pirate.print
from os.path import expanduser, expandvars
from pirate.print import print
2015-09-15 05:21:13 +02:00
def parse_config_file(text):
2015-08-30 03:28:43 +02:00
config = configparser.ConfigParser()
# default options
config.add_section('Save')
config.set('Save', 'magnets', 'false')
config.set('Save', 'torrents', 'false')
config.set('Save', 'directory', os.getcwd())
config.add_section('LocalDB')
config.set('LocalDB', 'enabled', 'false')
config.set('LocalDB', 'path', expanduser('~/downloads/pirate-get/db'))
2015-08-30 04:00:42 +02:00
config.add_section('Misc')
2015-09-15 05:21:13 +02:00
# TODO: try to use https://docs.python.org/3/library/configparser.html#configparser.BasicInterpolation for interpolating in the command
2015-08-30 03:28:43 +02:00
config.set('Misc', 'openCommand', '')
config.set('Misc', 'transmission', 'false')
config.set('Misc', 'colors', 'true')
2015-09-15 05:21:13 +02:00
config.read_string(text)
2015-08-30 03:28:43 +02:00
# expand env variables
directory = expanduser(expandvars(config.get('Save', 'Directory')))
path = expanduser(expandvars(config.get('LocalDB', 'path')))
config.set('Save', 'Directory', directory)
config.set('LocalDB', 'path', path)
return config
2015-09-17 08:15:27 +02:00
2015-09-15 05:21:13 +02:00
def load_config():
# user-defined config files
main = expandvars('$XDG_CONFIG_HOME/pirate-get')
alt = expanduser('~/.config/pirate-get')
# read config file
if os.path.isfile(main):
with open(main) as f:
return parse_config_file(f.read())
if os.path.isfile(alt):
with open(alt) as f:
return parse_config_file(f.read())
return parse_config_file("")
2015-08-30 03:28:43 +02:00
def parse_cmd(cmd, url):
cmd_args_regex = r'''(('[^']*'|"[^"]*"|(\\\s|[^\s])+)+ *)'''
ret = re.findall(cmd_args_regex, cmd)
ret = [i[0].strip().replace('%s', url) for i in ret]
ret_no_quotes = []
for item in ret:
2015-08-30 04:00:42 +02:00
if ((item[0] == "'" and item[-1] == "'") or
(item[0] == '"' and item[-1] == '"')):
2015-08-30 03:28:43 +02:00
ret_no_quotes.append(item[1:-1])
else:
ret_no_quotes.append(item)
return ret_no_quotes
2015-09-04 08:00:40 +02:00
def parse_torrent_command(l):
# Very permissive handling
# Check for any occurances or d, f, p, t, m, or q
cmd_code_match = re.search(r'([hdfpmtq])', l,
flags=re.IGNORECASE)
if cmd_code_match:
code = cmd_code_match.group(0).lower()
else:
code = None
# Clean up command codes
# Substitute multiple consecutive spaces/commas for single
# comma remove anything that isn't an integer or comma.
# Turn into list
l = re.sub(r'^[hdfp, ]*|[hdfp, ]*$', '', l)
l = re.sub('[ ,]+', ',', l)
l = re.sub('[^0-9,-]', '', l)
parsed_input = l.split(',')
# expand ranges
choices = []
# loop will generate a list of lists
for elem in parsed_input:
left, sep, right = elem.partition('-')
if right:
choices.append(list(range(int(left), int(right) + 1)))
elif left != '':
choices.append([int(left)])
# flatten list
choices = sum(choices, [])
# the current code stores the choices as strings
# instead of ints. not sure if necessary
choices = [elem for elem in choices]
return code, choices
2015-09-15 05:21:13 +02:00
def parse_args(args_in):
2015-08-30 03:28:43 +02:00
parser = argparse.ArgumentParser(
2015-08-30 04:00:42 +02:00
description='finds and downloads torrents from the Pirate Bay')
2015-08-30 03:28:43 +02:00
parser.add_argument('-b', dest='browse',
action='store_true',
help='display in Browse mode')
parser.add_argument('search', metavar='search',
nargs='*', help='term to search for')
parser.add_argument('-c', dest='category', metavar='category',
help='specify a category to search', default='All')
parser.add_argument('-s', dest='sort', metavar='sort',
help='specify a sort option', default='SeedersDsc')
parser.add_argument('-R', dest='recent', action='store_true',
help='torrents uploaded in the last 48hours.'
2015-08-30 04:00:42 +02:00
'*ignored in searches*')
2015-08-30 03:28:43 +02:00
parser.add_argument('-l', dest='list_categories',
action='store_true',
help='list categories')
parser.add_argument('--list_sorts', dest='list_sorts',
action='store_true',
help='list Sortable Types')
parser.add_argument('-L', '--local', dest='database',
help='an xml file containing the Pirate Bay database')
2015-09-04 05:25:24 +02:00
parser.add_argument('-p', dest='pages', default=1, type=int,
2015-08-30 03:28:43 +02:00
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')
parser.add_argument('-a', '--download-all',
action='store_true',
help='download all results')
parser.add_argument('-t', '--transmission',
action='store_true',
help='open magnets with transmission-remote')
parser.add_argument('-P', '--port', dest='port',
help='transmission-remote rpc port. default is 9091')
parser.add_argument('-C', '--custom', dest='command',
help='open magnets with a custom command'
' (%%s will be replaced with the url)')
parser.add_argument('-M', '--save-magnets',
action='store_true',
help='save magnets links as files')
parser.add_argument('-T', '--save-torrents',
action='store_true',
help='save torrent files')
parser.add_argument('-S', '--save-directory',
type=str, metavar='DIRECTORY',
help='directory where to save downloaded files'
' (if none is given $PWD will be used)')
parser.add_argument('--disable-colors', dest='color',
action='store_false',
help='disable colored output')
2015-09-15 05:21:13 +02:00
args = parser.parse_args(args_in)
2015-08-30 03:28:43 +02:00
2015-09-17 08:15:27 +02:00
return args
def combine_configs(config, args):
# figure out the action - browse, search, top, etc.
if args.browse:
2015-09-17 08:15:27 +02:00
args.action = 'browse'
elif args.recent:
2015-09-17 08:15:27 +02:00
args.action = 'recent'
elif args.list_categories:
args.action = 'list_categories'
elif args.list_sorts:
args.action = 'list_sorts'
elif len(args.search) == 0:
args.action = 'top'
else:
2015-09-17 08:15:27 +02:00
args.action = 'search'
2015-09-15 05:21:13 +02:00
2015-09-17 08:15:27 +02:00
args.source = 'tpb'
if args.database or config.getboolean('LocalDB', 'enabled'):
args.source = 'local_tpb'
2015-09-15 05:21:13 +02:00
2015-09-17 08:15:27 +02:00
if not args.database:
args.database = config.get('LocalDB', 'path')
2015-09-15 05:21:13 +02:00
2015-09-17 08:15:27 +02:00
if not args.color or not config.getboolean('Misc', 'colors'):
# TODO: consider how this can be moved to the args
2015-08-30 03:28:43 +02:00
pirate.data.colored_output = False
2015-09-17 08:15:27 +02:00
if not args.save_directory:
args.save_directory = config.get('Save', 'directory')
2015-08-30 03:28:43 +02:00
2015-09-17 08:15:27 +02:00
args.transmission_command = ['transmission-remote']
2015-08-30 03:28:43 +02:00
if args.port:
2015-09-17 08:15:27 +02:00
args.transmission_command.append(args.port)
2015-08-30 03:28:43 +02:00
2015-09-17 08:15:27 +02:00
args.output = 'browser_open'
2015-08-30 03:28:43 +02:00
if args.transmission or config.getboolean('Misc', 'transmission'):
2015-09-17 08:15:27 +02:00
args.output = 'transmission'
elif args.save_magnets or config.getboolean('Save', 'magnets'):
args.output = 'save_magnet_files'
elif args.save_torrents or config.getboolean('Save', 'torrents'):
args.output = 'save_torrent_files'
elif args.command or config.get('Misc', 'openCommand'):
args.output = 'open_command'
args.open_command = args.command
if not args.open_command:
args.open_command = config.get('Misc', 'openCommand')
return args
2015-09-20 07:27:05 +02:00
def search_mirrors(pages, category, sort, action, search):
mirror_sources = [None, 'https://proxybay.co/list.txt']
for mirror_source in mirror_sources:
mirrors = OrderedDict()
if mirror_source is None:
mirrors['https://thepiratebay.mn'] = None
2015-09-17 08:15:27 +02:00
else:
try:
req = request.Request(mirror_source,
headers=pirate.data.default_headers)
f = request.urlopen(req, timeout=pirate.data.default_timeout)
except IOError:
print('Could not fetch additional mirrors', color='WARN')
else:
if f.getcode() != 200:
raise IOError('The proxy bay responded with an error.')
for mirror in [i.decode('utf-8').strip() for i in f.readlines()][3:]:
mirrors[mirror] = None
for mirror in pirate.data.blacklist:
if mirror in mirrors:
del mirrors[mirror]
for mirror in mirrors.keys():
try:
print('Trying', mirror, end='... \n')
results = pirate.torrent.remote(
pages=pages,
category=pirate.torrent.parse_category(category),
sort=pirate.torrent.parse_sort(sort),
mode=action,
terms=search,
mirror=mirror
)
except (urllib.error.URLError, socket.timeout,
IOError, ValueError):
print('Failed', color='WARN')
else:
print('Ok', color='alt')
return results, mirror
2015-09-17 08:15:27 +02:00
else:
print('No available mirrors :(', color='WARN')
return [], None
def main():
args = combine_configs(load_config(), parse_args(sys.argv[1:]))
# check it transmission is running
if args.transmission:
ret = subprocess.call(args.transmission_command + ['-l'],
2015-08-30 03:28:43 +02:00
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL)
if ret != 0:
print('Transmission is not running.')
2015-09-17 08:15:27 +02:00
sys.exit(1)
# non-torrent fetching actions
2015-08-30 03:28:43 +02:00
2015-09-17 08:15:27 +02:00
if args.action == 'list_categories':
2015-08-30 03:28:43 +02:00
cur_color = 'zebra_0'
for key, value in sorted(pirate.data.categories.items()):
cur_color = 'zebra_0' if cur_color == 'zebra_1' else 'zebra_1'
print(str(value), '\t', key, sep='', color=cur_color)
return
2015-09-17 08:15:27 +02:00
if args.action == 'list_sorts':
2015-08-30 03:28:43 +02:00
cur_color = 'zebra_0'
2015-08-30 04:00:06 +02:00
for key, value in sorted(pirate.data.sorts.items()):
2015-08-30 03:28:43 +02:00
cur_color = 'zebra_0' if cur_color == 'zebra_1' else 'zebra_1'
print(str(value), '\t', key, sep='', color=cur_color)
return
2015-09-17 08:15:27 +02:00
# fetch torrents
2015-08-30 03:28:43 +02:00
2015-09-17 08:15:27 +02:00
if args.source == 'local_tpb':
results = pirate.local.search(args.database, args.search)
elif args.source == 'tpb':
2015-09-20 07:27:05 +02:00
results, site = search_mirrors(args.pages, args.category, args.sort, args.action, args.search)
2015-08-30 03:28:43 +02:00
2015-09-04 07:18:38 +02:00
if len(results) == 0:
2015-08-30 03:28:43 +02:00
print('No results')
return
2015-09-17 08:15:27 +02:00
pirate.print.search_results(results, local=args.source == 'local_tpb')
2015-08-30 03:28:43 +02:00
2015-09-17 08:15:27 +02:00
# number of results to pick
2015-08-30 03:28:43 +02:00
if args.first:
print('Choosing first result')
choices = [0]
elif args.download_all:
print('Downloading all results')
2015-09-04 07:18:38 +02:00
choices = range(len(results))
2015-08-30 03:28:43 +02:00
else:
2015-09-17 08:15:27 +02:00
# interactive loop for per-torrent actions
2015-08-30 03:28:43 +02:00
while True:
print("\nSelect links (Type 'h' for more options"
", 'q' to quit)", end='\b', color='alt')
try:
2015-08-30 04:00:42 +02:00
l = input(': ')
2015-09-17 08:15:27 +02:00
except (KeyboardInterrupt, EOFError):
2015-08-30 03:28:43 +02:00
print('\nCancelled.')
return
try:
2015-09-04 08:00:40 +02:00
code, choices = parse_torrent_command(l)
2015-08-30 03:28:43 +02:00
# Act on option, if supplied
print('')
if code == 'h':
print('Options:',
'<links>: Download selected torrents',
'[m<links>]: Save magnets as files',
'[t<links>]: Save .torrent files',
'[d<links>]: Get descriptions',
'[f<links>]: Get files',
'[p] Print search results',
'[q] Quit', sep='\n')
elif code == 'q':
print('Bye.', color='alt')
return
elif code == 'd':
2015-09-04 07:18:38 +02:00
pirate.print.descriptions(choices, results, site)
2015-08-30 03:28:43 +02:00
elif code == 'f':
2015-09-04 07:18:38 +02:00
pirate.print.file_lists(choices, results, site)
2015-08-30 03:28:43 +02:00
elif code == 'p':
2015-09-04 07:18:38 +02:00
pirate.print.search_results(results)
2015-08-30 03:28:43 +02:00
elif code == 'm':
2015-09-17 08:15:27 +02:00
pirate.torrent.save_magnets(choices, results, args.save_directory)
2015-08-30 03:28:43 +02:00
elif code == 't':
2015-09-17 08:15:27 +02:00
pirate.torrent.save_torrents(choices, results, args.save_directory)
2015-08-30 03:28:43 +02:00
elif not l:
print('No links entered!', color='WARN')
else:
break
except Exception as e:
print('Exception:', e, color='ERROR')
2015-09-17 08:15:27 +02:00
return
2015-08-30 03:28:43 +02:00
2015-09-17 08:15:27 +02:00
# output
2015-08-30 03:28:43 +02:00
2015-09-17 08:15:27 +02:00
if args.output == 'save_magnet_files':
2015-08-30 03:28:43 +02:00
print('Saving selected magnets...')
2015-09-17 08:15:27 +02:00
pirate.torrent.save_magnets(choices, results, args.save_directory)
return
2015-08-30 03:28:43 +02:00
2015-09-17 08:15:27 +02:00
if args.output == 'save_torrent_files':
2015-08-30 03:28:43 +02:00
print('Saving selected torrents...')
2015-09-17 08:15:27 +02:00
pirate.torrent.save_torrents(choices, results, args.save_directory)
2015-08-30 03:28:43 +02:00
return
for choice in choices:
2015-09-17 08:15:27 +02:00
url = results[choice]['magnet']
2015-08-30 03:28:43 +02:00
2015-09-17 08:15:27 +02:00
if args.output == 'transmission':
subprocess.call(args.transmission_command + ['--add', url])
elif args.output == 'open_command':
subprocess.call(parse_cmd(args.open_command, url))
elif args.output == 'browser_open':
2015-08-30 03:28:43 +02:00
webbrowser.open(url)
2015-09-17 08:15:27 +02:00
if args.output == 'transmission':
subprocess.call(args.transmission_command + ['-l'])
2015-08-30 03:28:43 +02:00
if __name__ == '__main__':
main()