git @ Cat's Eye Technologies kinoje / d123eff
Merge pull request #2 from catseye/develop-0.3 Develop 0.3 Chris Pressey authored 2 years ago GitHub committed 2 years ago
7 changed file(s) with 112 addition(s) and 59 deletion(s). Raw diff Collapse all Expand all
8686
8787 # Rope project settings
8888 .ropeproject
89
90 # Generated files
91 *.gif
92 *.mp4
0 Jinja2==2.10
1 PyYAML==3.12
2 tqdm==4.19.6
11 import os
22 import sys
33
4 from kinoje.utils import LoggingExecutor, load_config_file
4 from kinoje.utils import BaseProcessor, Executor, load_config_file, zrange
55
66
77 SUPPORTED_OUTPUT_FORMATS = ('.m4v', '.mp4', '.gif')
88
99
10 class Compiler(object):
11 def __init__(self, dirname, outfilename, config, exe):
10 class Compiler(BaseProcessor):
11 def __init__(self, config, dirname, outfilename, **kwargs):
12 super(Compiler, self).__init__(config, **kwargs)
1213 self.dirname = dirname
13 self.exe = exe
1414 self.outfilename = outfilename
15 self.config = config
1615 self.frame_fmt = "%08d.png"
16
17 @classmethod
18 def get_class_for(cls, filename):
19 (whatever, outext) = os.path.splitext(filename)
20 if outext not in SUPPORTED_OUTPUT_FORMATS:
21 raise ValueError("%s not a supported output format (%r)" % (outext, SUPPORTED_OUTPUT_FORMATS))
22 return {
23 '.gif': GifCompiler,
24 '.mp4': MpegCompiler,
25 '.m4v': MpegCompiler,
26 }[outext]
27
28 def compile(self, num_frames):
29 raise NotImplementedError
30
31 def compile_all(self):
32 tasks = [lambda: self.compile(self.config['num_frames'])]
33 for task in self.tqdm(tasks):
34 result = task()
35 return result
1736
1837
1938 class GifCompiler(Compiler):
2241 # TODO: show some warning if this is not an integer delay
2342 delay = int(100.0 / self.config['fps'])
2443
25 filenames = [os.path.join(self.dirname, self.frame_fmt % f) for f in xrange(0, num_frames)]
44 filenames = [os.path.join(self.dirname, self.frame_fmt % f) for f in zrange(0, num_frames)]
2645 if self.config.get('shorten_final_frame'):
2746 filespec = ' '.join(filenames[:-1] + ['-delay', str(delay / 2), filenames[-1]])
2847 else:
85104 config = load_config_file(options.configfile)
86105 config['shorten_final_frame'] = options.shorten_final_frame
87106
88 exe = LoggingExecutor('compiler.log')
89
90 compiler = {
91 '.gif': GifCompiler,
92 '.mp4': MpegCompiler,
93 '.m4v': MpegCompiler,
94 }[outext](options.framesdir, options.output, config, exe)
95
96 compiler.compile(config['num_frames'])
107 compiler = Compiler.get_class_for(options.output)(config, options.framesdir, options.output)
108 compiler.compile_all()
97109
98110 if options.view:
99111 compiler.view()
100
101 exe.close()
55
66 from jinja2 import Template
77
8 from kinoje.utils import LoggingExecutor, fmod, tween, load_config_file
8 from kinoje.utils import BaseProcessor, Executor, fmod, tween, load_config_file, items, zrange
99
1010
11 class Expander(object):
11 class Expander(BaseProcessor):
1212 """Takes a directory and a template (Jinja2) and expands the template a number of times,
1313 creating a number of filled-out text files in the directory."""
14 def __init__(self, dirname, template, config, exe):
14 def __init__(self, config, dirname, **kwargs):
15 super(Expander, self).__init__(config, **kwargs)
1516 self.dirname = dirname
16 self.template = template
17 self.config = config
18 self.exe = exe
19
17 self.template = Template(config['template'])
2018 self.fun_context = {}
21 for key, value in self.config.get('functions', {}).iteritems():
19 for key, value in items(self.config.get('functions', {})):
2220 self.fun_context[key] = eval("lambda x: " + value)
2321
2422 def fillout_template(self, frame, t):
3634 with open(output_filename, 'w') as f:
3735 f.write(self.template.render(context))
3836
37 def expand_all(self):
38 t = self.config['start']
39 t_step = self.config['t_step']
40 for frame in self.tqdm(zrange(self.config['num_frames'])):
41 self.fillout_template(frame, t)
42 t += t_step
43
3944
4045 def main():
4146 argparser = ArgumentParser()
5055 options = argparser.parse_args(sys.argv[1:])
5156
5257 config = load_config_file(options.configfile)
53 template = Template(config['template'])
5458
55 exe = LoggingExecutor('movie.log')
56
57 expander = Expander(options.instantsdir, template, config, exe)
58
59 t = config['start']
60 t_step = config['t_step']
61 for frame in xrange(config['num_frames']):
62 expander.fillout_template(frame, t)
63 t += t_step
64
65 exe.close()
59 expander = Expander(config, options.instantsdir)
60 expander.expand_all()
22 import os
33 import re
44 import sys
5 from tempfile import mkdtemp
5 from tempfile import mkdtemp, mkstemp
66
7 from kinoje.utils import Executor, load_config_file
7 try:
8 from tqdm import tqdm
9 except ImportError:
10 def tqdm(x, **kwargs): return x
811
12 from kinoje.expander import Expander
13 from kinoje.renderer import Renderer
14 from kinoje.compiler import Compiler, SUPPORTED_OUTPUT_FORMATS
915
10 SUPPORTED_OUTPUT_FORMATS = ('.m4v', '.mp4', '.gif')
16 from kinoje.utils import LoggingExecutor, load_config_file
1117
1218
1319 def main():
3339 else:
3440 output_filename = options.output
3541
36 exe = Executor()
42 CompilerClass = Compiler.get_class_for(output_filename)
43
44 config = load_config_file(options.configfile)
45
46 fd, log_filename = mkstemp()
47 exe = LoggingExecutor(log_filename)
3748
3849 instants_dir = mkdtemp()
3950 frames_dir = mkdtemp()
4051
41 exe.do_it("kinoje-expand {} {}".format(options.configfile, instants_dir))
42 exe.do_it("kinoje-render {} {} {}".format(options.configfile, instants_dir, frames_dir))
43 exe.do_it("kinoje-compile {} {} {} {}".format(options.configfile, frames_dir, output_filename, remainder))
52 print('expanding template to instants...')
53 expander = Expander(config, instants_dir, exe=exe, tqdm=tqdm)
54 expander.expand_all()
55
56 print('rendering instants to frames...')
57 renderer = Renderer(config, instants_dir, frames_dir, exe=exe, tqdm=tqdm)
58 renderer.render_all()
59
60 print('compiling frames to movie...')
61 compiler = CompilerClass(config, frames_dir, output_filename, exe=exe, tqdm=tqdm)
62 compiler.compile_all()
4463
4564 exe.close()
65 os.close(fd)
88 except ImportError:
99 from yaml import Loader
1010
11 from kinoje.utils import LoggingExecutor, load_config_file
11 from kinoje.utils import BaseProcessor, Executor, load_config_file
1212
1313
14 class Renderer(object):
14 class Renderer(BaseProcessor):
1515 """Takes a source directory filled with text files and a destination directory and
1616 creates one image file in the destination directory from each text file in the source."""
17 def __init__(self, config, src, dest, exe):
17 def __init__(self, config, src, dest, **kwargs):
18 super(Renderer, self).__init__(config, **kwargs)
19 self.src = src
20 self.dest = dest
1821 self.command = config['command']
1922 self.libdir = config['libdir']
20 self.src = src
21 self.dest = dest
22 self.exe = exe
2323 self.width = config['width']
2424 self.height = config['height']
2525
2626 def render_all(self):
27 for filename in sorted(os.listdir(self.src)):
27 for filename in self.tqdm(sorted(os.listdir(self.src))):
2828 full_srcname = os.path.join(self.src, filename)
2929 match = re.match(r'^.*?(\d+).*?$', filename)
3030 frame = int(match.group(1))
6060
6161 config = load_config_file(options.configfile)
6262
63 exe = LoggingExecutor('renderer.log')
64
65 renderer = Renderer(config, options.instantsdir, options.framesdir, exe)
63 renderer = Renderer(config, options.instantsdir, options.framesdir)
6664 renderer.render_all()
67
68 exe.close()
00 import os
11 import sys
22 from subprocess import check_call
3
4
5 class BaseProcessor(object):
6 def __init__(self, config, exe=None, tqdm=None):
7 self.config = config
8 self.exe = exe or Executor()
9 if not tqdm:
10 def tqdm(x, **kwargs): return x
11 self.tqdm = tqdm
12
13
14 def items(d):
15 try:
16 return d.iteritems()
17 except AttributeError:
18 return d.items()
19
20
21 def zrange(*args):
22 try:
23 return xrange(*args)
24 except NameError:
25 return range(*args)
326
427
528 def load_config_file(filename):
3861 def __init__(self, filename):
3962 self.filename = filename
4063 self.log = open(filename, 'w')
64 print("logging to {}".format(self.filename))
4165
4266 def do_it(self, cmd, **kwargs):
43 print cmd
67 self.log.write('>>> {}\n'.format(cmd))
68 self.log.flush()
4469 try:
4570 check_call(cmd, shell=True, stdout=self.log, stderr=self.log, **kwargs)
4671 except Exception as e:
4772 self.log.close()
48 print str(e)
73 print(str(e))
4974 check_call("tail %s" % self.filename, shell=True)
5075 sys.exit(1)
5176
5580
5681 class Executor(object):
5782 def do_it(self, cmd, **kwargs):
58 print cmd
83 print(cmd)
5984 check_call(cmd, shell=True, **kwargs)
6085
6186 def close(self):