No more upload queue

Conflicts:
	bin/asciiio
This commit is contained in:
Marcin Kulik
2012-11-05 01:44:54 +01:00
committed by Marcin Kulik
parent 57c55d3c16
commit 1b0d87c8df
8 changed files with 161 additions and 244 deletions

View File

@@ -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/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
tmp/asciiio.zip: src/__main__.py src/asciicast.py src/recorders.py src/timed_file.py src/uploader.py src/config.py src/options.py src/cli.py
mkdir -p tmp
rm -rf tmp/asciiio.zip
cd src && zip ../tmp/asciiio.zip *.py

92
src/asciicast.py Normal file
View File

@@ -0,0 +1,92 @@
import json
import os
import shutil
import subprocess
import tempfile
import time
from timed_file import TimedFile
class Asciicast(object):
def __init__(self):
self.dir_path = tempfile.mkdtemp()
self.command = None
self.title = None
self.shell = os.environ['SHELL']
self.term = os.environ['TERM']
self.username = os.environ['USER']
self.uname = get_command_output(['uname', '-srvp'])
self.recorded_at = time.time()
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 save(self):
self.save_streams()
self.save_metadata()
def save_streams(self):
self.stdout_file.close()
self.stdin_file.close()
def save_metadata(self):
lines = int(get_command_output(['tput', 'lines']))
columns = int(get_command_output(['tput', 'cols']))
# RFC 2822
recorded_at = time.strftime("%a, %d %b %Y %H:%M:%S +0000",
time.gmtime(self.recorded_at))
data = {
'username' : self.username,
'user_token' : self.user_token,
'duration' : self.duration,
'recorded_at': recorded_at,
'title' : self.title,
'command' : self.command,
'shell' : self.shell,
'uname' : self.uname,
'term' : {
'type' : self.term,
'lines' : lines,
'columns': columns
}
}
json_string = json.dumps(data, sort_keys=True, indent=4)
with open(self.metadata_filename, 'wb') as f:
f.write(json_string + '\n')
def remove(self):
shutil.rmtree(self.dir_path)
def get_command_output(args):
process = subprocess.Popen(args, stdout=subprocess.PIPE)
return process.communicate()[0].strip()

View File

@@ -1,95 +0,0 @@
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()

View File

@@ -1,59 +0,0 @@
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)

View File

@@ -1,11 +1,11 @@
import os
import sys
import asciicasts
from asciicast import Asciicast
from config import Config
from options import Options
from uploader import Uploader
import asciicast_recorder
import recorders
SCRIPT_NAME = os.path.basename(sys.argv[0])
@@ -19,8 +19,6 @@ def run():
if action == 'rec':
record()
elif action == 'upload':
upload_all_pending()
elif action == 'auth':
authenticate()
elif action == 'help':
@@ -34,44 +32,13 @@ def run():
# Actions
def record():
# check_pending()
asciicast = record_asciicast()
# id = int(time.time())
# path = "%s/%i" % (queue_dir_path, id)
# asciicast = Asciicast(path)
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():
if upload_requested():
print '~ Uploading...'
upload_asciicast(asciicast)
def upload_all_pending():
print 'Uploading pending asciicasts...'
for asciicast in pending_asciicasts():
upload_asciicast(asciicast)
asciicast.remove()
def authenticate():
@@ -96,17 +63,31 @@ def handle_unknown_action(action):
# 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 record_asciicast():
asciicast = Asciicast()
if sys.stdin.isatty():
if options.command:
command = options.command
is_shell = False
else:
command = os.environ['SHELL']
is_shell = True
def pending_asciicasts():
return asciicasts.pending(config.queue_dir_path)
stdin_file = asciicast.stdin_file if options.record_input else None
duration = recorders.record_process(command, is_shell,
asciicast.stdout_file, stdin_file)
else:
duration = recorders.record_stream(sys.stdin, asciicast.stdout_file)
asciicast.user_token = config.user_token
asciicast.command = options.command
asciicast.title = options.title
asciicast.duration = duration
asciicast.save()
return asciicast
def upload_asciicast(asciicast):
@@ -115,10 +96,9 @@ def upload_asciicast(asciicast):
if url:
print url
asciicast.destroy()
def is_upload_requested():
def upload_requested():
if options.always_yes:
return True
@@ -133,7 +113,6 @@ 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:

View File

@@ -7,7 +7,6 @@ class Config:
def __init__(self):
self.base_dir_path = os.path.expanduser("~/.ascii.io")
self.config_filename = '%s/config' % self.base_dir_path
self.queue_dir_path = '%s/queue' % self.base_dir_path
self.create_base_dir()
self.parse_config_file()

View File

@@ -32,7 +32,7 @@ class Options:
elif opt == '--version':
self.action = 'version'
elif opt == '-c':
self.command = arg.split()
self.command = arg
elif opt == '-t':
self.title = arg
elif opt == '-i':

View File

@@ -1,53 +1,48 @@
# TODO REFA
import os
import pty
import subprocess
import signal
import tty
import array
import fcntl
import os
import pty
import select
import select
import signal
import subprocess
import sys
import termios
import select
import time
import select
import tty
class StdinRecorder(object):
def record_stream(stream, stdout_file):
recorder = StreamRecorder(stream, stdout_file)
start_time = time.time()
recorder.run()
end_time = time.time()
return end_time - start_time
def __init__(self, stdout_file):
class StreamRecorder(object):
def __init__(self, stream, stdout_file):
self.stream = stream
self.stdout_file = stdout_file
def run(self):
while 1:
line = sys.stdin.readline()
line = self.stream.readline()
if len(line) == 0:
break
self.stdout_file.write(data)
self.stdout_file.write(line)
print line,
# 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)
def record_process(command, is_shell, stdout_file, stdin_file=None):
recorder = ProcessRecorder(command, is_shell, stdout_file, stdin_file)
start_time = time.time()
recorder.run()
end_time = time.time()
return end_time - start_time
class ProcessRecorder(object):
@@ -57,22 +52,28 @@ class ProcessRecorder(object):
and saves stdin/stderr (and timing) to files.
'''
def __init__(self, command, stdout_file, stdin_file=None):
self.command = command
def __init__(self, command, is_shell, stdout_file, stdin_file=None):
self.command = command.split(' ')
self.is_shell = is_shell
self.stdout_file = stdout_file
self.stdin_file = stdin_file
self.master_fd = None
def run(self):
self.reset_terminal()
if self.is_shell:
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')
return success
def reset_terminal(self):
subprocess.call(["reset"])
pass
# subprocess.call(["reset"])
def _spawn(self):
'''Create a spawned process.