mirror of
https://github.com/asciinema/asciinema.git
synced 2025-12-16 11:48:13 +01:00
Class and module refactor
Conflicts: bin/asciiio
This commit is contained in:
committed by
Marcin Kulik
parent
477d36ea57
commit
57c55d3c16
2
Makefile
2
Makefile
@@ -5,7 +5,7 @@ bin/asciiio: tmp/asciiio.zip
|
||||
cat tmp/asciiio.zip >> bin/asciiio
|
||||
chmod +x bin/asciiio
|
||||
|
||||
tmp/asciiio.zip: src/__main__.py src/constants.py src/asciicast.py src/pty_recorder.py src/timed_file.py src/uploader.py src/config.py src/options.py src/help_text.py src/cli.py
|
||||
tmp/asciiio.zip: src/__main__.py src/asciicasts.py src/recorders.py src/timed_file.py src/uploader.py src/config.py src/options.py src/cli.py src/asciicast_recorder.py
|
||||
mkdir -p tmp
|
||||
rm -rf tmp/asciiio.zip
|
||||
cd src && zip ../tmp/asciiio.zip *.py
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from cli import CLI
|
||||
import cli
|
||||
|
||||
def main():
|
||||
CLI().run()
|
||||
cli.run()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import subprocess
|
||||
import json
|
||||
import shutil
|
||||
|
||||
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, 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 = options.command
|
||||
self.title = options.title
|
||||
self.record_input = options.record_input
|
||||
self.always_yes = options.always_yes
|
||||
self.duration = None
|
||||
|
||||
def create(self):
|
||||
self._record()
|
||||
if self.confirm_upload():
|
||||
return self._upload()
|
||||
else:
|
||||
self._delete()
|
||||
|
||||
def confirm_upload(self):
|
||||
if self.always_yes:
|
||||
return True
|
||||
|
||||
sys.stdout.write("~ Do you want to upload it? [Y/n] ")
|
||||
answer = sys.stdin.readline().strip()
|
||||
return answer == 'y' or answer == 'Y' or answer == ''
|
||||
|
||||
def _record(self):
|
||||
os.makedirs(self.path)
|
||||
self.recording_start = time.time()
|
||||
command = self.command or os.environ['SHELL'].split()
|
||||
PtyRecorder(self.path, command, self.record_input).run()
|
||||
self.duration = time.time() - self.recording_start
|
||||
self._save_metadata()
|
||||
|
||||
def _save_metadata(self):
|
||||
info_file = open(self.path + '/meta.json', 'wb')
|
||||
|
||||
# RFC 2822
|
||||
recorded_at = time.strftime("%a, %d %b %Y %H:%M:%S +0000",
|
||||
time.gmtime(self.recording_start))
|
||||
|
||||
command = self.command and ' '.join(self.command)
|
||||
uname = self._get_cmd_output(['uname', '-srvp'])
|
||||
username = os.environ['USER']
|
||||
shell = os.environ['SHELL']
|
||||
term = os.environ['TERM']
|
||||
lines = int(self._get_cmd_output(['tput', 'lines']))
|
||||
columns = int(self._get_cmd_output(['tput', 'cols']))
|
||||
|
||||
data = {
|
||||
'username' : username,
|
||||
'user_token' : self.user_token,
|
||||
'duration' : self.duration,
|
||||
'recorded_at': recorded_at,
|
||||
'title' : self.title,
|
||||
'command' : command,
|
||||
'shell' : shell,
|
||||
'uname' : uname,
|
||||
'term' : {
|
||||
'type' : term,
|
||||
'lines' : lines,
|
||||
'columns': columns
|
||||
}
|
||||
}
|
||||
|
||||
json_string = json.dumps(data, sort_keys=True, indent=4)
|
||||
info_file.write(json_string + '\n')
|
||||
info_file.close()
|
||||
|
||||
def _get_cmd_output(self, args):
|
||||
process = subprocess.Popen(args, stdout=subprocess.PIPE)
|
||||
return process.communicate()[0].strip()
|
||||
|
||||
def _upload(self):
|
||||
url = Uploader(self.config, self.path).upload()
|
||||
if url:
|
||||
print url
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def _delete(self):
|
||||
shutil.rmtree(self.path)
|
||||
95
src/asciicast_recorder.py
Normal file
95
src/asciicast_recorder.py
Normal file
@@ -0,0 +1,95 @@
|
||||
import os
|
||||
import time
|
||||
import subprocess
|
||||
|
||||
from asciicasts import Asciicast
|
||||
from recorders import ProcessRecorder
|
||||
|
||||
|
||||
# def record(command, queue_dir_path):
|
||||
# # id = int(time.time())
|
||||
# # path = "%s/%i" % (queue_dir_path, id)
|
||||
# # asciicast = Asciicast(path)
|
||||
|
||||
# if sys.stdin.isatty():
|
||||
# record_process(command, asciicast.stdout_file)
|
||||
# else:
|
||||
# record_stdin(asciicast.stdout_file)
|
||||
|
||||
|
||||
# def record_stdin(stdout_file, stdin=sys.stdin):
|
||||
|
||||
|
||||
# def record_process(command, stdout_file, stdin_file=None):
|
||||
|
||||
|
||||
def record(queue_dir_path, user_token, options):
|
||||
# id = int(time.time())
|
||||
# path = "%s/%i" % (queue_dir_path, id)
|
||||
# asciicast = Asciicast(path)
|
||||
|
||||
self.command = options.command
|
||||
self.record_input = options.record_input
|
||||
self.always_yes = options.always_yes
|
||||
|
||||
self.recording_start_time = None
|
||||
self.duration = None
|
||||
|
||||
self.record_command()
|
||||
self.save_metadata(user_token=user_token, title=options.title)
|
||||
|
||||
return asciicast
|
||||
|
||||
def record_command(self):
|
||||
self.recording_start_time = time.time()
|
||||
cmd = self.command or os.environ['SHELL'].split()
|
||||
|
||||
stdin_file = None
|
||||
if self.record_input:
|
||||
stdin_file = self.asciicast.stdin_file
|
||||
|
||||
self.asciicast.open_files()
|
||||
|
||||
recorder = ProcessRecorder(cmd, self.asciicast.stdout_file, stdin_file)
|
||||
recorder.run()
|
||||
|
||||
now = time.time()
|
||||
self.duration = now - self.recording_start_time
|
||||
|
||||
self.asciicast.close_files()
|
||||
|
||||
def save_metadata(self):
|
||||
# RFC 2822
|
||||
recorded_at = time.strftime("%a, %d %b %Y %H:%M:%S +0000",
|
||||
time.gmtime(self.recording_start_time))
|
||||
|
||||
command = self.command and ' '.join(self.command)
|
||||
uname = get_command_output(['uname', '-srvp'])
|
||||
username = os.environ['USER']
|
||||
shell = os.environ['SHELL']
|
||||
term = os.environ['TERM']
|
||||
lines = int(get_command_output(['tput', 'lines']))
|
||||
columns = int(get_command_output(['tput', 'cols']))
|
||||
|
||||
data = {
|
||||
'username' : username,
|
||||
'user_token' : self.user_token,
|
||||
'duration' : self.duration,
|
||||
'recorded_at': recorded_at,
|
||||
'title' : self.title,
|
||||
'command' : command,
|
||||
'shell' : shell,
|
||||
'uname' : uname,
|
||||
'term' : {
|
||||
'type' : term,
|
||||
'lines' : lines,
|
||||
'columns': columns
|
||||
}
|
||||
}
|
||||
|
||||
self.asciicast.save_metadata(data)
|
||||
|
||||
|
||||
def get_command_output(args):
|
||||
process = subprocess.Popen(args, stdout=subprocess.PIPE)
|
||||
return process.communicate()[0].strip()
|
||||
59
src/asciicasts.py
Normal file
59
src/asciicasts.py
Normal file
@@ -0,0 +1,59 @@
|
||||
import os
|
||||
import glob
|
||||
import shutil
|
||||
import json
|
||||
|
||||
from timed_file import TimedFile
|
||||
|
||||
|
||||
def pending(dir):
|
||||
filenames = glob.glob(dir + '/*/*.time')
|
||||
return [Asciicast(os.path.dirname(f)) for f in filenames]
|
||||
|
||||
|
||||
class Asciicast(object):
|
||||
|
||||
def __init__(self, dir_path):
|
||||
self.dir_path = dir_path
|
||||
|
||||
if not os.path.isdir(self.dir_path):
|
||||
os.makedirs(self.dir_path)
|
||||
|
||||
self.stdout_file = TimedFile(self.dir_path + '/stdout')
|
||||
self.stdin_file = TimedFile(self.dir_path + '/stdin')
|
||||
|
||||
@property
|
||||
def metadata_filename(self):
|
||||
return self.dir_path + '/meta.json'
|
||||
|
||||
@property
|
||||
def stdout_data_filename(self):
|
||||
return self.stdout_file.data_filename
|
||||
|
||||
@property
|
||||
def stdout_timing_filename(self):
|
||||
return self.stdout_file.timing_filename
|
||||
|
||||
@property
|
||||
def stdin_data_filename(self):
|
||||
return self.stdin_file.data_filename
|
||||
|
||||
@property
|
||||
def stdin_timing_filename(self):
|
||||
return self.stdin_file.timing_filename
|
||||
|
||||
def open_files(self):
|
||||
self.stdout_file.start_timing()
|
||||
self.stdin_file.start_timing()
|
||||
|
||||
def close_files(self):
|
||||
self.stdout_file.close()
|
||||
self.stdin_file.close()
|
||||
|
||||
def save_metadata(self, data):
|
||||
json_string = json.dumps(data, sort_keys=True, indent=4)
|
||||
with open(self.metadata_filename, 'wb') as f:
|
||||
f.write(json_string + '\n')
|
||||
|
||||
def destroy(self):
|
||||
shutil.rmtree(self.dir_path)
|
||||
176
src/cli.py
176
src/cli.py
@@ -1,66 +1,144 @@
|
||||
import os
|
||||
import sys
|
||||
import glob
|
||||
|
||||
import help_text
|
||||
import asciicasts
|
||||
from config import Config
|
||||
from options import Options
|
||||
from constants import SCRIPT_NAME
|
||||
from asciicast import AsciiCast
|
||||
from uploader import Uploader
|
||||
import asciicast_recorder
|
||||
|
||||
class CLI:
|
||||
'''Parses command-line options and xxxxxxxxxxxxxxxxx.'''
|
||||
|
||||
def run(self):
|
||||
self.config = Config()
|
||||
self.options = Options(sys.argv)
|
||||
SCRIPT_NAME = os.path.basename(sys.argv[0])
|
||||
|
||||
action = self.options.action
|
||||
config = Config()
|
||||
options = Options(sys.argv)
|
||||
|
||||
if action == 'rec':
|
||||
self.check_pending()
|
||||
self.record()
|
||||
elif action == 'upload':
|
||||
self.upload_pending()
|
||||
elif action == 'auth':
|
||||
self.auth()
|
||||
elif action == 'help':
|
||||
self.help()
|
||||
elif action == 'version':
|
||||
self.version()
|
||||
else:
|
||||
print('Unknown action: %s' % action)
|
||||
print('Run "%s --help" for list of available options' % SCRIPT_NAME)
|
||||
|
||||
def record(self):
|
||||
if not AsciiCast(self.config, self.options).create():
|
||||
sys.exit(1)
|
||||
def run():
|
||||
action = options.action
|
||||
|
||||
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
|
||||
if action == 'rec':
|
||||
record()
|
||||
elif action == 'upload':
|
||||
upload_all_pending()
|
||||
elif action == 'auth':
|
||||
authenticate()
|
||||
elif action == 'help':
|
||||
print_help()
|
||||
elif action == 'version':
|
||||
print_version()
|
||||
else:
|
||||
handle_unknown_action(action)
|
||||
|
||||
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
|
||||
# Actions
|
||||
|
||||
def pending_list(self):
|
||||
return [os.path.dirname(p) for p in glob.glob(AsciiCast.QUEUE_DIR + '/*/*.time')]
|
||||
def record():
|
||||
# check_pending()
|
||||
|
||||
def version(self):
|
||||
print 'asciiio 1.0.1'
|
||||
# id = int(time.time())
|
||||
# path = "%s/%i" % (queue_dir_path, id)
|
||||
# asciicast = Asciicast(path)
|
||||
|
||||
def help(self):
|
||||
print help_text.TEXT
|
||||
asciicast = Asciicast()
|
||||
asciicast.command = options.command
|
||||
asciicast.title = options.title
|
||||
|
||||
start_time = time.time()
|
||||
|
||||
if sys.stdin.isatty():
|
||||
record_process(command, asciicast.stdout_file)
|
||||
else:
|
||||
record_stdin(asciicast.stdout_file)
|
||||
|
||||
end_time = time.time()
|
||||
|
||||
asciicast.recorded_at = start_time
|
||||
asciicast.duration = end_time - start_time
|
||||
|
||||
asciicast.save()
|
||||
|
||||
# asciicast = asciicast_recorder.record(
|
||||
# config.queue_dir_path, config.user_token, options
|
||||
# )
|
||||
# asciicast = recorder.record()
|
||||
|
||||
if is_upload_requested():
|
||||
print '~ Uploading...'
|
||||
upload_asciicast(asciicast)
|
||||
|
||||
|
||||
def upload_all_pending():
|
||||
print 'Uploading pending asciicasts...'
|
||||
for asciicast in pending_asciicasts():
|
||||
upload_asciicast(asciicast)
|
||||
|
||||
|
||||
def authenticate():
|
||||
url = '%s/connect/%s' % (config.api_url, config.user_token)
|
||||
print 'Open following URL in your browser to authenticate and/or ' \
|
||||
'claim recorded asciicasts:\n\n%s' % url
|
||||
|
||||
|
||||
def print_help():
|
||||
print HELP_TEXT
|
||||
|
||||
|
||||
def print_version():
|
||||
print 'asciiio 1.0.1'
|
||||
|
||||
|
||||
def handle_unknown_action(action):
|
||||
print('Unknown action: %s' % action)
|
||||
print('Run "%s --help" for list of available options' % SCRIPT_NAME)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
# Helpers
|
||||
|
||||
def check_pending():
|
||||
num = len(pending_asciicasts())
|
||||
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, config.queue_dir_path)
|
||||
|
||||
|
||||
def pending_asciicasts():
|
||||
return asciicasts.pending(config.queue_dir_path)
|
||||
|
||||
|
||||
def upload_asciicast(asciicast):
|
||||
uploader = Uploader(config.api_url)
|
||||
url = uploader.upload(asciicast)
|
||||
|
||||
if url:
|
||||
print url
|
||||
asciicast.destroy()
|
||||
|
||||
|
||||
def is_upload_requested():
|
||||
if options.always_yes:
|
||||
return True
|
||||
|
||||
sys.stdout.write("~ Do you want to upload it? [Y/n] ")
|
||||
answer = sys.stdin.readline().strip()
|
||||
return answer == 'y' or answer == 'Y' or answer == ''
|
||||
|
||||
|
||||
HELP_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
|
||||
|
||||
@@ -6,7 +6,7 @@ class Config:
|
||||
|
||||
def __init__(self):
|
||||
self.base_dir_path = os.path.expanduser("~/.ascii.io")
|
||||
self.config_file_path = '%s/config' % self.base_dir_path
|
||||
self.config_filename = '%s/config' % self.base_dir_path
|
||||
self.queue_dir_path = '%s/queue' % self.base_dir_path
|
||||
|
||||
self.create_base_dir()
|
||||
@@ -23,14 +23,15 @@ class Config:
|
||||
config.add_section('record')
|
||||
|
||||
try:
|
||||
config.read(self.config_file_path)
|
||||
config.read(self.config_filename)
|
||||
except ConfigParser.ParsingError:
|
||||
print('Config file %s contains syntax errors' %
|
||||
self.config_file_path)
|
||||
self.config_filename)
|
||||
sys.exit(2)
|
||||
|
||||
self.config = config
|
||||
|
||||
@property
|
||||
def api_url(self):
|
||||
try:
|
||||
api_url = self.config.get('api', 'url')
|
||||
@@ -41,6 +42,7 @@ class Config:
|
||||
|
||||
return api_url
|
||||
|
||||
@property
|
||||
def user_token(self):
|
||||
try:
|
||||
user_token = self.config.get('user', 'token')
|
||||
@@ -48,7 +50,7 @@ class Config:
|
||||
user_token = str(uuid.uuid1())
|
||||
self.config.set('user', 'token', user_token)
|
||||
|
||||
with open(self.config_file_path, 'wb') as configfile:
|
||||
self.config.write(configfile)
|
||||
with open(self.config_filename, 'wb') as f:
|
||||
self.config.write(f)
|
||||
|
||||
return user_token
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
SCRIPT_NAME = os.path.basename(sys.argv[0])
|
||||
@@ -1,17 +0,0 @@
|
||||
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
|
||||
@@ -1,3 +1,5 @@
|
||||
# TODO REFA
|
||||
|
||||
import os
|
||||
import pty
|
||||
import subprocess
|
||||
@@ -8,44 +10,70 @@ import fcntl
|
||||
import termios
|
||||
import select
|
||||
|
||||
from timed_file import TimedFile
|
||||
|
||||
class PtyRecorder(object):
|
||||
import time
|
||||
import select
|
||||
|
||||
|
||||
class StdinRecorder(object):
|
||||
|
||||
def __init__(self, stdout_file):
|
||||
self.stdout_file = stdout_file
|
||||
|
||||
def run(self):
|
||||
while 1:
|
||||
line = sys.stdin.readline()
|
||||
|
||||
if len(line) == 0:
|
||||
break
|
||||
|
||||
self.stdout_file.write(data)
|
||||
|
||||
# descriptor = 0
|
||||
|
||||
# while 1:
|
||||
# try:
|
||||
# rfds, wfds, xfds = select.select([descriptor], [], [])
|
||||
# except select.error, e:
|
||||
# if e[0] == 4: # Interrupted system call.
|
||||
# continue
|
||||
|
||||
# if descriptor in rfds:
|
||||
# data = os.read(descriptor, 1024)
|
||||
|
||||
# if len(data) == 0:
|
||||
# break
|
||||
|
||||
# # self._write_stdout(data)
|
||||
# self.stdout_file.write(data)
|
||||
# # print time.time()
|
||||
# # print len(data)
|
||||
|
||||
|
||||
class ProcessRecorder(object):
|
||||
'''Pseudo-terminal recorder.
|
||||
|
||||
Creates new pseudo-terminal for spawned process
|
||||
and saves stdin/stderr (and timing) to files.
|
||||
'''
|
||||
|
||||
def __init__(self, path, command, record_input):
|
||||
self.master_fd = None
|
||||
self.path = path
|
||||
def __init__(self, command, stdout_file, stdin_file=None):
|
||||
self.command = command
|
||||
self.record_input = record_input
|
||||
self.stdout_file = stdout_file
|
||||
self.stdin_file = stdin_file
|
||||
self.master_fd = None
|
||||
|
||||
def run(self):
|
||||
self._open_files()
|
||||
self.reset_terminal()
|
||||
self._write_stdout('~ Asciicast recording started. Hit ^D (that\'s Ctrl+D) or type "exit" to finish.\n\n')
|
||||
success = self._spawn()
|
||||
self.reset_terminal()
|
||||
self._write_stdout('~ Asciicast recording finished.\n')
|
||||
self._close_files()
|
||||
return success
|
||||
|
||||
def reset_terminal(self):
|
||||
subprocess.call(["reset"])
|
||||
|
||||
def _open_files(self):
|
||||
self.stdout_file = TimedFile(self.path + '/stdout')
|
||||
if self.record_input:
|
||||
self.stdin_file = TimedFile(self.path + '/stdin')
|
||||
|
||||
def _close_files(self):
|
||||
self.stdout_file.close()
|
||||
if self.record_input:
|
||||
self.stdin_file.close()
|
||||
|
||||
def _spawn(self):
|
||||
'''Create a spawned process.
|
||||
|
||||
@@ -138,7 +166,7 @@ class PtyRecorder(object):
|
||||
'''Handles new data on child process stdin.'''
|
||||
|
||||
self._write_master(data)
|
||||
if self.record_input:
|
||||
if self.stdin_file:
|
||||
self.stdin_file.write(data)
|
||||
|
||||
def _write_stdout(self, data):
|
||||
@@ -2,34 +2,44 @@ import time
|
||||
import StringIO
|
||||
import bz2
|
||||
|
||||
|
||||
class TimedFile(object):
|
||||
'''File wrapper that records write times in separate file.'''
|
||||
|
||||
def __init__(self, filename):
|
||||
self.filename = filename
|
||||
self.data_filename = filename
|
||||
self.timing_filename = filename + '.time'
|
||||
|
||||
self.data_file = StringIO.StringIO()
|
||||
self.time_file = StringIO.StringIO()
|
||||
self.mem_data_file = None
|
||||
self.mem_timing_file = None
|
||||
|
||||
self.old_time = time.time()
|
||||
self.start_timing()
|
||||
|
||||
def start_timing(self):
|
||||
self.prev_time = time.time()
|
||||
|
||||
def write(self, data):
|
||||
self.data_file.write(data)
|
||||
if not self.mem_data_file:
|
||||
self.mem_data_file = StringIO.StringIO()
|
||||
self.mem_timing_file = StringIO.StringIO()
|
||||
|
||||
now = time.time()
|
||||
delta = now - self.old_time
|
||||
self.time_file.write("%f %d\n" % (delta, len(data)))
|
||||
self.old_time = now
|
||||
delta = now - self.prev_time
|
||||
self.prev_time = now
|
||||
|
||||
self.mem_data_file.write(data)
|
||||
self.mem_timing_file.write("%f %d\n" % (delta, len(data)))
|
||||
|
||||
def close(self):
|
||||
mode = 'w'
|
||||
if not self.mem_data_file:
|
||||
return
|
||||
|
||||
bz2_data_file = bz2.BZ2File(self.filename, mode)
|
||||
bz2_data_file.write(self.data_file.getvalue())
|
||||
bz2_data_file = bz2.BZ2File(self.data_filename, 'w')
|
||||
bz2_data_file.write(self.mem_data_file.getvalue())
|
||||
bz2_data_file.close()
|
||||
self.mem_data_file.close()
|
||||
|
||||
bz2_time_file = bz2.BZ2File(self.filename + '.time', mode)
|
||||
bz2_time_file.write(self.time_file.getvalue())
|
||||
bz2_time_file.close()
|
||||
|
||||
self.data_file.close()
|
||||
self.time_file.close()
|
||||
bz2_timing_file = bz2.BZ2File(self.timing_filename, 'w')
|
||||
bz2_timing_file.write(self.mem_timing_file.getvalue())
|
||||
bz2_timing_file.close()
|
||||
self.mem_timing_file.close()
|
||||
|
||||
@@ -1,6 +1,22 @@
|
||||
import os
|
||||
import subprocess
|
||||
import shutil
|
||||
|
||||
|
||||
class CurlFormData(object):
|
||||
|
||||
def __init__(self, namespace=None):
|
||||
self.namespace = namespace
|
||||
self.files = {}
|
||||
|
||||
def add_file(self, name, filename):
|
||||
if self.namespace:
|
||||
name = '%s[%s]' % (self.namespace, name)
|
||||
|
||||
self.files[name] = '@' + filename
|
||||
|
||||
def form_file_args(self):
|
||||
return ' '.join(['-F %s="%s"' % (k, v) for k, v in self.files.iteritems()])
|
||||
|
||||
|
||||
class Uploader(object):
|
||||
'''Asciicast uploader.
|
||||
@@ -8,26 +24,22 @@ class Uploader(object):
|
||||
Uploads recorded script to website using HTTP based API.
|
||||
'''
|
||||
|
||||
def __init__(self, config, path):
|
||||
self.api_url = config.api_url()
|
||||
self.path = path
|
||||
def __init__(self, api_url):
|
||||
self.upload_url = '%s/api/asciicasts' % api_url
|
||||
|
||||
def upload(self):
|
||||
print '~ Uploading...'
|
||||
def upload(self, asciicast):
|
||||
form_data = CurlFormData('asciicast')
|
||||
|
||||
files = {
|
||||
'meta': 'meta.json',
|
||||
'stdout': 'stdout',
|
||||
'stdout_timing': 'stdout.time'
|
||||
}
|
||||
form_data.add_file('meta', asciicast.metadata_filename)
|
||||
|
||||
if os.path.exists(self.path + '/stdin'):
|
||||
files['stdin'] = 'stdin'
|
||||
files['stdin_timing'] = 'stdin.time'
|
||||
form_data.add_file('stdout', asciicast.stdout_data_filename)
|
||||
form_data.add_file('stdout_timing', asciicast.stdout_timing_filename)
|
||||
|
||||
fields = ["-F asciicast[%s]=@%s/%s" % (f, self.path, files[f]) for f in files]
|
||||
if os.path.exists(asciicast.stdin_data_filename):
|
||||
form_data.add_file('stdin', asciicast.stdin_data_filename)
|
||||
form_data.add_file('stdin_timing', asciicast.stdin_timing_filename)
|
||||
|
||||
cmd = "curl -sSf -o - %s %s" % (' '.join(fields), '%s/api/asciicasts' % self.api_url)
|
||||
cmd = "curl -sSf -o - %s %s" % (form_data.form_file_args(), self.upload_url)
|
||||
|
||||
process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
@@ -37,13 +49,5 @@ class Uploader(object):
|
||||
# print >> sys.stderr, stderr
|
||||
# sys.stderr.write(stderr)
|
||||
os.write(2, stderr)
|
||||
else:
|
||||
self._remove_files()
|
||||
|
||||
if stdout:
|
||||
return stdout
|
||||
else:
|
||||
return None
|
||||
|
||||
def _remove_files(self):
|
||||
shutil.rmtree(self.path)
|
||||
return stdout
|
||||
|
||||
Reference in New Issue
Block a user