Merge pull request #2 from catseye/develop-0.3
Develop 0.3
Chris Pressey authored 7 years ago
GitHub committed 7 years ago
1 | 1 | import os |
2 | 2 | import sys |
3 | 3 | |
4 | from kinoje.utils import LoggingExecutor, load_config_file | |
4 | from kinoje.utils import BaseProcessor, Executor, load_config_file, zrange | |
5 | 5 | |
6 | 6 | |
7 | 7 | SUPPORTED_OUTPUT_FORMATS = ('.m4v', '.mp4', '.gif') |
8 | 8 | |
9 | 9 | |
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) | |
12 | 13 | self.dirname = dirname |
13 | self.exe = exe | |
14 | 14 | self.outfilename = outfilename |
15 | self.config = config | |
16 | 15 | 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 | |
17 | 36 | |
18 | 37 | |
19 | 38 | class GifCompiler(Compiler): |
22 | 41 | # TODO: show some warning if this is not an integer delay |
23 | 42 | delay = int(100.0 / self.config['fps']) |
24 | 43 | |
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)] | |
26 | 45 | if self.config.get('shorten_final_frame'): |
27 | 46 | filespec = ' '.join(filenames[:-1] + ['-delay', str(delay / 2), filenames[-1]]) |
28 | 47 | else: |
85 | 104 | config = load_config_file(options.configfile) |
86 | 105 | config['shorten_final_frame'] = options.shorten_final_frame |
87 | 106 | |
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() | |
97 | 109 | |
98 | 110 | if options.view: |
99 | 111 | compiler.view() |
100 | ||
101 | exe.close() |
5 | 5 | |
6 | 6 | from jinja2 import Template |
7 | 7 | |
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 | |
9 | 9 | |
10 | 10 | |
11 | class Expander(object): | |
11 | class Expander(BaseProcessor): | |
12 | 12 | """Takes a directory and a template (Jinja2) and expands the template a number of times, |
13 | 13 | 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) | |
15 | 16 | self.dirname = dirname |
16 | self.template = template | |
17 | self.config = config | |
18 | self.exe = exe | |
19 | ||
17 | self.template = Template(config['template']) | |
20 | 18 | self.fun_context = {} |
21 | for key, value in self.config.get('functions', {}).iteritems(): | |
19 | for key, value in items(self.config.get('functions', {})): | |
22 | 20 | self.fun_context[key] = eval("lambda x: " + value) |
23 | 21 | |
24 | 22 | def fillout_template(self, frame, t): |
36 | 34 | with open(output_filename, 'w') as f: |
37 | 35 | f.write(self.template.render(context)) |
38 | 36 | |
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 | ||
39 | 44 | |
40 | 45 | def main(): |
41 | 46 | argparser = ArgumentParser() |
50 | 55 | options = argparser.parse_args(sys.argv[1:]) |
51 | 56 | |
52 | 57 | config = load_config_file(options.configfile) |
53 | template = Template(config['template']) | |
54 | 58 | |
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() |
2 | 2 | import os |
3 | 3 | import re |
4 | 4 | import sys |
5 | from tempfile import mkdtemp | |
5 | from tempfile import mkdtemp, mkstemp | |
6 | 6 | |
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 | |
8 | 11 | |
12 | from kinoje.expander import Expander | |
13 | from kinoje.renderer import Renderer | |
14 | from kinoje.compiler import Compiler, SUPPORTED_OUTPUT_FORMATS | |
9 | 15 | |
10 | SUPPORTED_OUTPUT_FORMATS = ('.m4v', '.mp4', '.gif') | |
16 | from kinoje.utils import LoggingExecutor, load_config_file | |
11 | 17 | |
12 | 18 | |
13 | 19 | def main(): |
33 | 39 | else: |
34 | 40 | output_filename = options.output |
35 | 41 | |
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) | |
37 | 48 | |
38 | 49 | instants_dir = mkdtemp() |
39 | 50 | frames_dir = mkdtemp() |
40 | 51 | |
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() | |
44 | 63 | |
45 | 64 | exe.close() |
65 | os.close(fd) |
8 | 8 | except ImportError: |
9 | 9 | from yaml import Loader |
10 | 10 | |
11 | from kinoje.utils import LoggingExecutor, load_config_file | |
11 | from kinoje.utils import BaseProcessor, Executor, load_config_file | |
12 | 12 | |
13 | 13 | |
14 | class Renderer(object): | |
14 | class Renderer(BaseProcessor): | |
15 | 15 | """Takes a source directory filled with text files and a destination directory and |
16 | 16 | 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 | |
18 | 21 | self.command = config['command'] |
19 | 22 | self.libdir = config['libdir'] |
20 | self.src = src | |
21 | self.dest = dest | |
22 | self.exe = exe | |
23 | 23 | self.width = config['width'] |
24 | 24 | self.height = config['height'] |
25 | 25 | |
26 | 26 | def render_all(self): |
27 | for filename in sorted(os.listdir(self.src)): | |
27 | for filename in self.tqdm(sorted(os.listdir(self.src))): | |
28 | 28 | full_srcname = os.path.join(self.src, filename) |
29 | 29 | match = re.match(r'^.*?(\d+).*?$', filename) |
30 | 30 | frame = int(match.group(1)) |
60 | 60 | |
61 | 61 | config = load_config_file(options.configfile) |
62 | 62 | |
63 | exe = LoggingExecutor('renderer.log') | |
64 | ||
65 | renderer = Renderer(config, options.instantsdir, options.framesdir, exe) | |
63 | renderer = Renderer(config, options.instantsdir, options.framesdir) | |
66 | 64 | renderer.render_all() |
67 | ||
68 | exe.close() |
0 | 0 | import os |
1 | 1 | import sys |
2 | 2 | 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) | |
3 | 26 | |
4 | 27 | |
5 | 28 | def load_config_file(filename): |
38 | 61 | def __init__(self, filename): |
39 | 62 | self.filename = filename |
40 | 63 | self.log = open(filename, 'w') |
64 | print("logging to {}".format(self.filename)) | |
41 | 65 | |
42 | 66 | def do_it(self, cmd, **kwargs): |
43 | print cmd | |
67 | self.log.write('>>> {}\n'.format(cmd)) | |
68 | self.log.flush() | |
44 | 69 | try: |
45 | 70 | check_call(cmd, shell=True, stdout=self.log, stderr=self.log, **kwargs) |
46 | 71 | except Exception as e: |
47 | 72 | self.log.close() |
48 | print str(e) | |
73 | print(str(e)) | |
49 | 74 | check_call("tail %s" % self.filename, shell=True) |
50 | 75 | sys.exit(1) |
51 | 76 | |
55 | 80 | |
56 | 81 | class Executor(object): |
57 | 82 | def do_it(self, cmd, **kwargs): |
58 | print cmd | |
83 | print(cmd) | |
59 | 84 | check_call(cmd, shell=True, **kwargs) |
60 | 85 | |
61 | 86 | def close(self): |