1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 """
26 Saves the images and movie file from a clip.
27 """
28 import sys
29 from time import strftime
30 import os
31 import shutil
32 import glob
33 import pprint
34
35 from twisted.internet import reactor
36 from twisted.internet import defer
37
38
39 from toon import mencoder
40 from rats import sig
41 import pygame
42 from pygame.locals import *
43
44 PROGRESS_MKDIR = 0.05
45 PROGRESS_IMAGES = 0.85
46 PROGRESS_MENCODER = 0.05
47 PROGRESS_CLEANUP = 0.05
48
50 """
51 Any error that may occur during saving a clip.
52 """
53 pass
54
56 """
57 Saves a clip to images and a movie file if possible.
58 """
59
60
61
62 - def __init__(self, core, dir_path, file_prefix, clip_id):
63 """
64 :param core: The Toonloop application object.
65 :param dir_path: Path to the directory where to save the clip.
66 :param file_prefix: The beginning of the name of the files to save.
67 :param clip_id: The id of the clip to save.
68 """
69 self.clip_id = clip_id
70 self.core = core
71 self.file_prefix = file_prefix
72 self.dir_path = dir_path
73 self.current_index = 0
74 self.IMAGES_DIR = "images"
75 self._deferred = None
76 self.is_busy = False
77 self.signal_progress = sig.Signal()
78 self.signal_done = sig.Signal()
79
81 """
82 Does save the clip.
83 Return a deferred.
84 """
85 self.is_busy = True
86 if self._deferred is not None:
87 raise ClipSaverError("Do not call save twice on the same ClipSaver.")
88 self._deferred = defer.Deferred()
89 try:
90 if not os.path.exists(self.dir_path):
91 os.makedirs(self.dir_path)
92 print('mkdir %s' % (self.dir_path))
93 except OSError, e:
94 msg = 'Error creating directories' % (self.dir_path, e.message)
95 self._fail(msg)
96 print(msg)
97 else:
98 try:
99 data_subdir = os.path.join(self.dir_path, self.IMAGES_DIR)
100 if not os.path.exists(data_subdir):
101 os.makedirs(data_subdir)
102 print('mkdir %s' % (data_subdir))
103 except OSError, e:
104 msg = 'Error creating directories' % (data_subdir, e.message)
105 print(msg)
106 self._fail(msg)
107 else:
108 self.signal_progress(PROGRESS_MKDIR)
109 reactor.callLater(0.0, self._write_01_next_image)
110 return self._deferred
111
113 self.is_busy = False
114 self.signal_done(False)
115 self._deferred.errback(msg)
116
118 self.is_busy = False
119 self.signal_done(True)
120 self._deferred.callback(msg)
121
123 """
124 Saves each image using twisted in order not to freeze the app.
125 Uses the JPG extension.
126 """
127
128 num_images_in_clip = len(self.core.clips[self.clip_id].images)
129 if self.current_index < num_images_in_clip:
130 name = ("%s/%s_%5d.jpg" % (self.dir_path, self.file_prefix, self.current_index)).replace(' ', '0')
131 if self.core.config.verbose:
132 print("writing image %s" % (self.current_index))
133 pygame.image.save(self.core.clips[self.clip_id].images[self.current_index], name)
134 self.current_index += 1
135 self.signal_progress(PROGRESS_MKDIR + PROGRESS_IMAGES * self.current_index / float(num_images_in_clip))
136 reactor.callLater(0.0, self._write_01_next_image)
137 else:
138 reactor.callLater(0.0, self._write_02_images_done)
139
141 """
142 Converts the list of images in a motion-JPEG .mov video file.
143 """
144 if self.current_index > 0:
145 if self.core.config.verbose:
146 print("\nConverting to motion JPEG in Quicktime container.")
147 fps = 12
148
149
150 deferred = mencoder.jpeg_to_movie(self.file_prefix, self.dir_path, fps, self.core.config.verbose, self.core.config.image_width, self.core.config.image_height)
151 deferred.addCallback(self._write_03_movie_done)
152 deferred.addErrback(self._eb_mencoder)
153
154
155
157 msg = reason.getErrorMessage()
158 print(msg)
159 self._fail(msg)
160
162 """
163 Called when mencoder conversion is done.
164 MOV file.
165 """
166 if self.core.config.verbose:
167 print("Done converting %s/%s.mov" % (self.dir_path, self.file_prefix))
168
169 self.signal_progress(1.0)
170 reactor.callLater(1.0, self._write_04_delete_images)
171
173 """
174 deletes JPG images or moves them to the 'data' folder in the project folder.
175 renames MOV file.
176 """
177 files = glob.glob("%s/%s_*.jpg" % (self.dir_path, self.file_prefix))
178 for f in files:
179 if self.core.config.delete_jpeg:
180 try:
181 os.remove(f)
182 if self.core.config.verbose:
183 print('removed %s' % (f))
184 except OSError, e:
185 msg = "%s Error removing file %s" % (e.message, f)
186 print(msg)
187
188 else:
189 try:
190 dest = os.path.join(self.dir_path, self.IMAGES_DIR, os.path.basename(f))
191 shutil.move(f, dest)
192
193
194 except IOError, e:
195 msg = "%s Error moving file %s to %s" % (e.message, f, dest)
196 print(msg)
197
198
199 try:
200 src = "%s/%s.mov" % (self.dir_path, self.file_prefix)
201 dest = "%s/clip_%s.mov" % (self.dir_path, self.file_prefix)
202 shutil.move(src, dest)
203 if self.core.config.verbose:
204 print('renamed %s to %s' % (src, dest) )
205 print('DONE SAVING CLIP %s' % (self.clip_id))
206 except IOError, e:
207 msg = "%s Error moving file %s to %s" % (e.message, src, dest)
208 print(msg)
209 self._fail(msg)
210 else:
211 self.signal_progress(1.0)
212 self._succeed("Successfully saved images, converted movie and moved files for clip %s" % (self.clip_id))
213