diff --git a/delugeClient/__main__.py b/delugeClient/__main__.py index a7137d5..7edd0fe 100644 --- a/delugeClient/__main__.py +++ b/delugeClient/__main__.py @@ -1,40 +1,11 @@ #!/usr/bin/env python3.6 - -"""Custom delugeRPC client -Usage: - deluge_cli add MAGNET [DIR] [--json | --debug | --info | --warning | --error] - deluge_cli search QUERY [--json] - deluge_cli get ID [--json | --debug | --warning | --error] - deluge_cli ls [--downloading | --seeding | --paused | --json] - deluge_cli toggle TORRENT - deluge_cli progress [--json] - deluge_cli rm ID [--destroy] [--debug | --warning | --error] - deluge_cli (-h | --help) - deluge_cli --version - -Arguments: - MAGNET Magnet link to add - DIR Directory to save to - ID A torrent hash - QUERY Query search string - - -Options: - -h --help Show this screen - --version Show version - --print Print response from commands - --json Print response as JSON - --debug Print all debug log - --warning Print only logged warnings - --error Print error messages (Error/Warning) -""" import os import sys import signal import logging -from docopt import docopt +import typer from pprint import pprint from deluge import Deluge @@ -46,94 +17,35 @@ ch = logging.StreamHandler() ch.setLevel(logging.ERROR) logger.addHandler(ch) -logger.addFilter(ColorizeFilter()) +logger.addFilter(ColorizeFilter()) + +app = typer.Typer() +deluge = Deluge() def signal_handler(signal, frame): """ Handle exit by Keyboardinterrupt """ + del deluge + logger.info('\nGood bye!') sys.exit(0) -def main(): - """ - Main function, parse the input - """ +def handleKeyboardInterrupt(): signal.signal(signal.SIGINT, signal_handler) - arguments = docopt(__doc__, version=__version__) - - # Set logging level for streamHandler - if arguments['--debug']: - ch.setLevel(logging.DEBUG) - elif arguments['--info']: - ch.setLevel(logging.INFO) - elif arguments['--warning']: - ch.setLevel(logging.WARNING) - elif arguments['--error']: - ch.setLevel(logging.ERROR) - - logger.debug(arguments) - - # Get config settings - deluge = Deluge() - - _id = arguments['ID'] - magnet = arguments['MAGNET'] - query = arguments['QUERY'] - _filter = [ a[2:] for a in ['--downloading', '--seeding', '--paused'] if arguments[a] ] - - response = None - - if arguments['add']: - response = deluge.add(magnet) - - if response is not None: - msg = 'Successfully added torrent with id: {}'.format(response) - logger.info(msg) - else: - logger.warning('Add response returned empty: {}'.format(response)) - - elif arguments['search']: - logger.debug('Search cmd selected for query: {}'.format(query)) - response = deluge.search(query) - if response is not None or response != '[]': - logger.info('Search found {} torrents'.format(len(response))) - else: - logger.info('Empty response for search query.') - - elif arguments['progress']: - logger.debug('Progress cmd selected.') - response = deluge.progress() - - elif arguments['get']: - logger.debug('Get cmd selected for id: {}'.format(_id)) - response = deluge.get(_id) - - elif arguments['ls']: - logger.debug('List cmd selected') - response = deluge.get_all(_filter=_filter) - - elif arguments['toggle']: - logger.debug('Toggling id: {}'.format(_id)) - deluge.togglePaused(_id) - - elif arguments['rm']: - destroy = arguments['--destroy'] - logger.debug('Remove by id: {}.'.format(_id)) - - if destroy: - logger.info('Destroy set, removing files') - - if not _id: - logger.error("Unable to remove. No id supplied.") - return - - deluge.remove(_id, destroy) - +def printResponse(response, json=False): try: - if arguments['--json']: - print('[{}]'.format(','.join([t.toJSON() for t in response]))) + if json: + if isinstance(response, list): + print('[{}]'.format(','.join([t.toJSON() for t in response]))) + else: + print(response.toJSON()) + + elif isinstance(response, list): + for el in response: + print(el) + elif response: print(response) @@ -141,7 +53,85 @@ def main(): logger.error('Unexpected error while trying to print') raise error - sys.exit(0) +@app.command() +def add(magnet: str): + ''' + Add magnet torrent + ''' + logger.debug('Add command selected') + logger.debug(magnet) + response = deluge.add(magnet) + printResponse(response) + +@app.command() +def ls(json: bool = typer.Option(False, help="Print as json")): + ''' + List all torrents + ''' + logger.debug('List command selected') + response = deluge.get_all() + printResponse(response, json) + +@app.command() +def get(id: str, json: bool = typer.Option(False, help="Print as json")): + ''' + Get torrent by id or hash + ''' + logger.debug('Get command selected for id {}'.format(id)) + response = deluge.get(id) + printResponse(response, json) + +@app.command() +def toggle(id: str): + ''' + Toggle torrent download state + ''' + logger.debug('Toggle command selected for id {}'.format(id)) + response = deluge.toggle(id) + printResponse(response) + +@app.command() +def search(query: str, json: bool = typer.Option(False, help="Print as json")): + ''' + Search for string segment in torrent name + ''' + logger.debug('Search command selected with query: {}'.format(query)) + response = deluge.search(query) + printResponse(response, json) + +@app.command() +def remove(id: str, destroy: bool = typer.Option(False, help="Remove torrent data")): + ''' + Remove torrent by id or hash + ''' + logger.debug('Remove command selected for id: {} with destroy: {}'.format(id, destroy)) + response = deluge.remove(id, destroy) + printResponse(response) + +@app.command() +def version(): + ''' + Print package version + ''' + print(__version__) + +@app.callback() +def defaultOptions(debug: bool = typer.Option(False, '--debug', help='Set log level to debug'), info: bool = typer.Option(False, '--info', help='Set log level to info'), warning: bool = typer.Option(False, '--warning', help='Set log level to warning'), error: bool = typer.Option(False, '--error', help='Set log level to error')): + ch.setLevel(logging.WARNING) + + if error == True: + ch.setLevel(logging.ERROR) + elif warning == True: + ch.setLevel(logging.WARNING) + elif info == True: + ch.setLevel(logging.INFO) + elif debug == True: + ch.setLevel(logging.DEBUG) + +def main(): + app() + del deluge if __name__ == '__main__': - main() \ No newline at end of file + handleKeyboardInterrupt() + main() diff --git a/delugeClient/__version__.py b/delugeClient/__version__.py index 020ed73..0404d81 100644 --- a/delugeClient/__version__.py +++ b/delugeClient/__version__.py @@ -1 +1 @@ -__version__ = '0.2.2' +__version__ = '0.3.0' diff --git a/delugeClient/deluge.py b/delugeClient/deluge.py index b932a2e..dfe6d70 100644 --- a/delugeClient/deluge.py +++ b/delugeClient/deluge.py @@ -81,7 +81,7 @@ class Deluge(object): magnet = self.getMagnetFromFile(url) response = self.client.call('core.add_torrent_magnet', magnet, {}) - return responseToString(response.decode('utf-8')) + return responseToString(response) def get_all(self, _filter=None): if (type(_filter) is list and len(_filter)): @@ -101,7 +101,7 @@ class Deluge(object): torrentNamesMatchingQuery = [] if len(allTorrents): for torrent in allTorrents: - if query in torrent.name: + if query in torrent.name.lower(): torrentNamesMatchingQuery.append(torrent) allTorrents = torrentNamesMatchingQuery @@ -113,14 +113,19 @@ class Deluge(object): def get(self, id): response = self.client.call('core.get_torrent_status', id, {}) + if response == {}: + logger.warning('No torrent with id: {}'.format(id)) + return None + return Torrent.fromDeluge(response) - def togglePaused(self, id): + def toggle(self, id): torrent = self.get(id) if (torrent.paused): response = self.client.call('core.resume_torrent', [id]) else: response = self.client.call('core.pause_torrent', [id]) + return responseToString(response) def removeByName(self, name, destroy=False): @@ -160,23 +165,12 @@ class Deluge(object): filteredTorrents.append(value_template) return filteredTorrents - def progress(self): - attributes = ['progress', 'eta', 'state', 'finished'] - all_torrents = self.get_all() - - torrents = [] - for i, attribute in enumerate(attributes): - if i < 1: - torrents = self.filterOnValue(all_torrents, attribute) - continue - torrents = [dict(e, **v) for e,v in zip(torrents, self.filterOnValue(all_torrents, attribute))] - - return torrents - def __del__(self): - if hasattr(self, 'tunnel'): + self.client.disconnect() + + if hasattr(self, 'tunnel') and self.tunnel.is_active: logger.debug('Closing ssh tunnel') - self.tunnel.stop() + self.tunnel.stop(True) def getMagnetFromFile(self, url): logger.info('File url found, fetching magnet.') diff --git a/delugeClient/torrent.py b/delugeClient/torrent.py index e7e41fd..c9ac71b 100644 --- a/delugeClient/torrent.py +++ b/delugeClient/torrent.py @@ -44,5 +44,5 @@ class Torrent(object): return json.dumps(torrentDict) def __str__(self): - return "Name: {}, Progress: {}%, ETA: {}, State: {}, Paused: {}".format( - self.name, self.progress, self.eta, self.state, self.paused) \ No newline at end of file + return "{} Progress: {}% ETA: {} State: {} Paused: {}".format( + self.name[:59].ljust(60), self.progress.rjust(5), self.eta.rjust(11), self.state.ljust(12), self.paused) \ No newline at end of file diff --git a/setup.py b/setup.py index 6744077..5e09c6d 100644 --- a/setup.py +++ b/setup.py @@ -25,9 +25,9 @@ setup( install_requires=[ 'colored', 'deluge-client', - 'docopt', 'requests', 'sshtunnel', + 'typer', 'websockets' ], classifiers=[