1import locale
2import argparse
3import os
4import sys
5
6from asciinema import __version__
7import asciinema.config as config
8from asciinema.commands.auth import AuthCommand
9from asciinema.commands.record import RecordCommand
10from asciinema.commands.play import PlayCommand
11from asciinema.commands.cat import CatCommand
12from asciinema.commands.upload import UploadCommand
13from asciinema.api import Api
14
15
16def positive_float(value):
17    value = float(value)
18    if value <= 0.0:
19        raise argparse.ArgumentTypeError("must be positive")
20
21    return value
22
23
24def rec_command(args, config):
25    api = Api(config.api_url, os.environ.get("USER"), config.install_id)
26    return RecordCommand(api, args)
27
28
29def play_command(args, config):
30    return PlayCommand(args.filename, args.idle_time_limit, args.speed)
31
32
33def cat_command(args, config):
34    return CatCommand(args.filename)
35
36
37def upload_command(args, config):
38    api = Api(config.api_url, os.environ.get("USER"), config.install_id)
39    return UploadCommand(api, args.filename)
40
41
42def auth_command(args, config):
43    api = Api(config.api_url, os.environ.get("USER"), config.install_id)
44    return AuthCommand(api)
45
46
47def maybe_str(v):
48    if v is not None:
49        return str(v)
50
51
52def main():
53    if locale.nl_langinfo(locale.CODESET).upper() not in ['US-ASCII', 'UTF-8']:
54        print("asciinema needs an ASCII or UTF-8 character encoding to run. Check the output of `locale` command.")
55        sys.exit(1)
56
57    try:
58        cfg = config.load()
59    except config.ConfigError as e:
60        sys.stderr.write(str(e) + '\n')
61        sys.exit(1)
62
63    # create the top-level parser
64    parser = argparse.ArgumentParser(
65        description="Record and share your terminal sessions, the right way.",
66        epilog="""example usage:
67  Record terminal and upload it to asciinema.org:
68    \x1b[1masciinema rec\x1b[0m
69  Record terminal to local file:
70    \x1b[1masciinema rec demo.cast\x1b[0m
71  Record terminal and upload it to asciinema.org, specifying title:
72    \x1b[1masciinema rec -t "My git tutorial"\x1b[0m
73  Record terminal to local file, limiting idle time to max 2.5 sec:
74    \x1b[1masciinema rec -i 2.5 demo.cast\x1b[0m
75  Replay terminal recording from local file:
76    \x1b[1masciinema play demo.cast\x1b[0m
77  Replay terminal recording hosted on asciinema.org:
78    \x1b[1masciinema play https://asciinema.org/a/difqlgx86ym6emrmd8u62yqu8\x1b[0m
79  Print full output of recorded session:
80    \x1b[1masciinema cat demo.cast\x1b[0m
81
82For help on a specific command run:
83  \x1b[1masciinema <command> -h\x1b[0m""",
84        formatter_class=argparse.RawDescriptionHelpFormatter
85    )
86    parser.add_argument('--version', action='version', version='asciinema %s' % __version__)
87
88    subparsers = parser.add_subparsers()
89
90    # create the parser for the "rec" command
91    parser_rec = subparsers.add_parser('rec', help='Record terminal session')
92    parser_rec.add_argument('--stdin', help='enable stdin recording, disabled by default', action='store_true', default=cfg.record_stdin)
93    parser_rec.add_argument('--append', help='append to existing recording', action='store_true', default=False)
94    parser_rec.add_argument('--raw', help='save only raw stdout output', action='store_true', default=False)
95    parser_rec.add_argument('--overwrite', help='overwrite the file if it already exists', action='store_true', default=False)
96    parser_rec.add_argument('-c', '--command', help='command to record, defaults to $SHELL', default=cfg.record_command)
97    parser_rec.add_argument('-e', '--env', help='list of environment variables to capture, defaults to ' + config.DEFAULT_RECORD_ENV, default=cfg.record_env)
98    parser_rec.add_argument('-t', '--title', help='title of the asciicast')
99    parser_rec.add_argument('-i', '--idle-time-limit', help='limit recorded idle time to given number of seconds', type=positive_float, default=maybe_str(cfg.record_idle_time_limit))
100    parser_rec.add_argument('-y', '--yes', help='answer "yes" to all prompts (e.g. upload confirmation)', action='store_true', default=cfg.record_yes)
101    parser_rec.add_argument('-q', '--quiet', help='be quiet, suppress all notices/warnings (implies -y)', action='store_true', default=cfg.record_quiet)
102    parser_rec.add_argument('filename', nargs='?', default='', help='filename/path to save the recording to')
103    parser_rec.set_defaults(func=rec_command)
104
105    # create the parser for the "play" command
106    parser_play = subparsers.add_parser('play', help='Replay terminal session')
107    parser_play.add_argument('-i', '--idle-time-limit', help='limit idle time during playback to given number of seconds', type=positive_float, default=maybe_str(cfg.play_idle_time_limit))
108    parser_play.add_argument('-s', '--speed', help='playback speedup (can be fractional)', type=positive_float, default=cfg.play_speed)
109    parser_play.add_argument('filename', help='local path, http/ipfs URL or "-" (read from stdin)')
110    parser_play.set_defaults(func=play_command)
111
112    # create the parser for the "cat" command
113    parser_cat = subparsers.add_parser('cat', help='Print full output of terminal session')
114    parser_cat.add_argument('filename', help='local path, http/ipfs URL or "-" (read from stdin)')
115    parser_cat.set_defaults(func=cat_command)
116
117    # create the parser for the "upload" command
118    parser_upload = subparsers.add_parser('upload', help='Upload locally saved terminal session to asciinema.org')
119    parser_upload.add_argument('filename', help='filename or path of local recording')
120    parser_upload.set_defaults(func=upload_command)
121
122    # create the parser for the "auth" command
123    parser_auth = subparsers.add_parser('auth', help='Manage recordings on asciinema.org account')
124    parser_auth.set_defaults(func=auth_command)
125
126    # parse the args and call whatever function was selected
127    args = parser.parse_args()
128
129    if hasattr(args, 'func'):
130        command = args.func(args, cfg)
131        code = command.execute()
132        sys.exit(code)
133    else:
134        parser.print_help()
135        sys.exit(1)
136
137
138if __name__ == '__main__':
139    main()
140