diff --git a/src/__main__.py b/src/__main__.py index 8d2826c..1554dc5 100644 --- a/src/__main__.py +++ b/src/__main__.py @@ -1,155 +1,9 @@ #!/usr/bin/env python -import os -import sys -import getopt -import httplib, urllib -import socket -import glob -import ConfigParser -import uuid - -from constants import BASE_DIR, SCRIPT_NAME -from asciicast import AsciiCast -from uploader import Uploader - -def check_pending(): - num = len(pending_list()) - if num > 0: - print 'Warning: %i recorded asciicasts weren\'t uploaded. ' \ - 'Run "%s upload" to upload them or delete them with "rm -rf %s/*".' \ - % (num, SCRIPT_NAME, AsciiCast.QUEUE_DIR) - - -def upload_pending(api_url): - print 'Uploading pending asciicasts...' - for path in pending_list(): - url = Uploader(api_url, path).upload() - if url: - print url - - -def auth(api_url, user_token): - url = '%s/connect/%s' % (api_url, user_token) - print 'Open following URL in your browser to authenticate and/or claim ' \ - 'recorded asciicasts:\n\n%s' % url - - -def pending_list(): - return [os.path.dirname(p) for p in glob.glob(AsciiCast.QUEUE_DIR + '/*/*.time')] - - -def usage(): - text = '''usage: %s [-h] [-i] [-y] [-c ] [-t ] [action] - -Asciicast recorder+uploader. - -Actions: - rec record asciicast (this is the default when no action given) - upload upload recorded (but not uploaded) asciicasts - auth authenticate and/or claim recorded asciicasts - -Optional arguments: - -c command run specified command instead of shell ($SHELL) - -t title specify title of recorded asciicast - -y don't prompt for confirmation - -h, --help show this help message and exit - --version show version information''' % SCRIPT_NAME - print text - - -def print_version(): - print 'asciiio 1.0' - +from cli import CLI def main(): - '''Parses command-line options and creates asciicast.''' - - try: - opts, args = getopt.getopt(sys.argv[1:], 'c:t:ihy', ['help', 'version']) - except getopt.error as msg: - print('%s: %s' % (sys.argv[0], msg)) - print('Run "%s --help" for list of available options' % sys.argv[0]) - sys.exit(2) - - action = 'rec' - - if len(args) > 1: - print('Too many arguments') - print('Run "%s --help" for list of available options' % sys.argv[0]) - sys.exit(2) - elif len(args) == 1: - action = args[0] - - config = ConfigParser.RawConfigParser() - config.add_section('user') - config.add_section('api') - config.add_section('record') - - cfg_file = os.path.expanduser('~/.ascii.io/config') - try: - config.read(cfg_file) - except ConfigParser.ParsingError: - print('Config file %s contains syntax errors' % cfg_file) - sys.exit(2) - - try: - user_token = config.get('user', 'token') - except ConfigParser.NoOptionError: - user_token = str(uuid.uuid1()) - config.set('user', 'token', user_token) - - try: - record_input = config.getboolean('record', 'input') - except ConfigParser.NoOptionError: - record_input = False - - try: - api_url = config.get('api', 'url') - except ConfigParser.NoOptionError: - api_url = 'http://ascii.io' - - if not os.path.isdir(BASE_DIR): - os.mkdir(BASE_DIR) - - if not os.path.exists(cfg_file): - with open(cfg_file, 'wb') as configfile: - config.write(configfile) - - api_url = os.environ.get('ASCII_IO_API_URL', api_url) - - command = None - title = None - always_yes = False - - for opt, arg in opts: - if opt in ('-h', '--help'): - usage() - sys.exit(0) - elif opt == '--version': - print_version() - sys.exit(0) - elif opt == '-c': - command = arg.split() - elif opt == '-t': - title = arg - elif opt == '-i': - record_input = True - elif opt == '-y': - always_yes = True - - if action == 'rec': - check_pending() - if not AsciiCast(api_url, user_token, command, title, record_input, always_yes).create(): - sys.exit(1) - elif action == 'upload': - upload_pending(api_url) - elif action == 'auth': - auth(api_url, user_token) - else: - print('Unknown action: %s' % action) - print('Run "%s --help" for list of available options' % sys.argv[0]) - + CLI().run() if __name__ == '__main__': main() diff --git a/src/asciicast.py b/src/asciicast.py index c62a899..7c6fdde 100644 --- a/src/asciicast.py +++ b/src/asciicast.py @@ -5,22 +5,23 @@ import subprocess import json import shutil -from constants import SCRIPT_NAME, BASE_DIR +from constants import BASE_DIR from pty_recorder import PtyRecorder from uploader import Uploader class AsciiCast(object): QUEUE_DIR = BASE_DIR + "/queue" - def __init__(self, api_url, user_token, command, title, record_input, always_yes): - self.api_url = api_url - self.user_token = user_token + def __init__(self, config, options): + self.config = config + self.api_url = config.api_url() + self.user_token = config.user_token() self.path = AsciiCast.QUEUE_DIR + "/%i" % int(time.time()) - self.command = command - self.title = title - self.record_input = record_input + self.command = options.command + self.title = options.title + self.record_input = options.record_input + self.always_yes = options.always_yes self.duration = None - self.always_yes = always_yes def create(self): self._record() @@ -85,7 +86,7 @@ class AsciiCast(object): return process.communicate()[0].strip() def _upload(self): - url = Uploader(self.api_url, self.path).upload() + url = Uploader(self.config, self.path).upload() if url: print url return True diff --git a/src/cli.py b/src/cli.py new file mode 100644 index 0000000..b02a18e --- /dev/null +++ b/src/cli.py @@ -0,0 +1,66 @@ +import os +import sys +import glob + +import help_text +from config import Config +from options import Options +from constants import SCRIPT_NAME +from asciicast import AsciiCast +from uploader import Uploader + +class CLI: + '''Parses command-line options and xxxxxxxxxxxxxxxxx.''' + + def run(self): + self.config = Config() + self.options = Options(sys.argv) + + action = self.options.action + + if action == 'rec': + self.check_pending() + self.record() + elif action == 'upload': + self.upload_pending() + elif action == 'auth': + self.auth() + elif action == 'help': + self.usage() + elif action == 'version': + self.print_version() + else: + print('Unknown action: %s' % action) + print('Run "%s --help" for list of available options' % sys.argv[0]) + + def record(self): + if not AsciiCast(self.config, self.options).create(): + sys.exit(1) + + def auth(self): + url = '%s/connect/%s' % (self.config.api_url(), self.config.user_token()) + print 'Open following URL in your browser to authenticate and/or claim ' \ + 'recorded asciicasts:\n\n%s' % url + + def check_pending(self): + num = len(self.pending_list()) + if num > 0: + print 'Warning: %i recorded asciicasts weren\'t uploaded. ' \ + 'Run "%s upload" to upload them or delete them with "rm -rf %s/*".' \ + % (num, SCRIPT_NAME, AsciiCast.QUEUE_DIR) + + def upload_pending(self): + print 'Uploading pending asciicasts...' + for path in self.pending_list(): + url = Uploader(self.config, path).upload() + if url: + print url + + def pending_list(self): + return [os.path.dirname(p) for p in glob.glob(AsciiCast.QUEUE_DIR + '/*/*.time')] + + def print_version(self): + print 'asciiio 1.0' + + def usage(self): + print help_text.TEXT diff --git a/src/config.py b/src/config.py new file mode 100644 index 0000000..5886648 --- /dev/null +++ b/src/config.py @@ -0,0 +1,46 @@ +import os +import ConfigParser +import uuid + +class Config: + + def __init__(self): + self.config_filename = os.path.expanduser('~/.ascii.io/config') + + config = ConfigParser.RawConfigParser() + config.add_section('user') + config.add_section('api') + config.add_section('record') + + try: + config.read(self.config_filename) + except ConfigParser.ParsingError: + print('Config file %s contains syntax errors' % self.config_filename) + sys.exit(2) + + self.config = config + + # if not os.path.isdir(BASE_DIR): + # os.mkdir(BASE_DIR) + + def api_url(self): + try: + api_url = self.config.get('api', 'url') + except ConfigParser.NoOptionError: + api_url = 'http://ascii.io' + + api_url = os.environ.get('ASCII_IO_API_URL', api_url) + + return api_url + + def user_token(self): + try: + user_token = self.config.get('user', 'token') + except ConfigParser.NoOptionError: + user_token = str(uuid.uuid1()) + self.config.set('user', 'token', user_token) + + with open(self.config_filename, 'wb') as configfile: + self.config.write(configfile) + + return user_token diff --git a/src/help_text.py b/src/help_text.py new file mode 100644 index 0000000..7a9469a --- /dev/null +++ b/src/help_text.py @@ -0,0 +1,17 @@ +from constants import SCRIPT_NAME + +TEXT = '''usage: %s [-h] [-i] [-y] [-c <command>] [-t <title>] [action] + +Asciicast recorder+uploader. + +Actions: + rec record asciicast (this is the default when no action given) + upload upload recorded (but not uploaded) asciicasts + auth authenticate and/or claim recorded asciicasts + +Optional arguments: + -c command run specified command instead of shell ($SHELL) + -t title specify title of recorded asciicast + -y don't prompt for confirmation + -h, --help show this help message and exit + --version show version information''' % SCRIPT_NAME diff --git a/src/options.py b/src/options.py new file mode 100644 index 0000000..cebce1d --- /dev/null +++ b/src/options.py @@ -0,0 +1,41 @@ +import sys +import getopt + +class Options: + + def __init__(self, argv): + self.action = None + self.command = None + self.title = None + self.record_input = False + self.always_yes = False + + try: + opts, args = getopt.getopt(argv[1:], 'c:t:ihy', ['help', 'version']) + except getopt.error as msg: + print('%s: %s' % (argv[0], msg)) + print('Run "%s --help" for list of available options' % argv[0]) + sys.exit(2) + + if len(args) == 0: + self.action = 'rec' + elif len(args) == 1: + self.action = args[0] + elif len(args) > 1: + print('Too many arguments') + print('Run "%s --help" for list of available options' % argv[0]) + sys.exit(2) + + for opt, arg in opts: + if opt in ('-h', '--help'): + self.action = 'help' + elif opt == '--version': + self.action = 'version' + elif opt == '-c': + self.command = arg.split() + elif opt == '-t': + self.title = arg + elif opt == '-i': + self.record_input = True + elif opt == '-y': + self.always_yes = True diff --git a/src/uploader.py b/src/uploader.py index 99cfcb4..0a64449 100644 --- a/src/uploader.py +++ b/src/uploader.py @@ -8,8 +8,8 @@ class Uploader(object): Uploads recorded script to website using HTTP based API. ''' - def __init__(self, api_url, path): - self.api_url = api_url + def __init__(self, config, path): + self.api_url = config.api_url() self.path = path def upload(self):