Commit a66e6c58401047a1fbe43f2014b0af72a8e8ad95
1 parent
104cb330
Exists in
master
Adiciona módulo de verificação do arquivo .blend e módulo de geração do vídeo
Showing
6 changed files
with
237 additions
and
25 deletions
Show diff stats
.gitignore
@@ -0,0 +1,32 @@ | @@ -0,0 +1,32 @@ | ||
1 | +# -*- coding: UTF-8 -*- | ||
2 | + | ||
3 | +import bpy | ||
4 | +import sys | ||
5 | +import json | ||
6 | + | ||
7 | +def main(): | ||
8 | + if (len(sys.argv) == 8): | ||
9 | + json_obj = json.loads(sys.argv[7]) | ||
10 | + try: | ||
11 | + action_name = json_obj["action_name"] | ||
12 | + action_fake_is_valid = json_obj["action_fake_is_valid"] | ||
13 | + min_frame_count = json_obj["min_frame_count"] | ||
14 | + except: | ||
15 | + sys.exit(4) | ||
16 | + action = None | ||
17 | + for i in bpy.data.actions: | ||
18 | + if (i.name.upper() == action_name): | ||
19 | + action = i | ||
20 | + break | ||
21 | + if (action == None): | ||
22 | + sys.exit(5) | ||
23 | + if not (action_fake_is_valid): | ||
24 | + if (action.use_fake_user): | ||
25 | + sys.exit(6) | ||
26 | + if ((bpy.context.scene.frame_end - bpy.context.scene.frame_start) < min_frame_count): | ||
27 | + sys.exit(7) | ||
28 | + else: | ||
29 | + sys.exit(8) | ||
30 | + | ||
31 | +if __name__ == "__main__": | ||
32 | + main() |
@@ -0,0 +1,76 @@ | @@ -0,0 +1,76 @@ | ||
1 | +# -*- coding: UTF-8 -*- | ||
2 | + | ||
3 | +import bpy | ||
4 | +import json | ||
5 | +import os | ||
6 | +import sys | ||
7 | +import shutil | ||
8 | + | ||
9 | +def configure_output(): | ||
10 | + bpy.context.scene.frame_start = 0 | ||
11 | + bpy.context.scene.frame_current = bpy.context.scene.frame_start | ||
12 | + bpy.context.scene.frame_end = bpy.context.scene.frame_start | ||
13 | + bpy.context.scene.render.resolution_x = 640 | ||
14 | + bpy.context.scene.render.resolution_y = 480 | ||
15 | + bpy.context.scene.render.resolution_percentage = 100 | ||
16 | + bpy.context.scene.render.image_settings.file_format = 'H264' | ||
17 | + bpy.context.scene.render.ffmpeg.format = 'MPEG4' | ||
18 | + bpy.context.scene.render.ffmpeg.codec = 'H264' | ||
19 | + bpy.context.scene.render.use_shadows = False | ||
20 | + bpy.context.scene.render.use_raytrace = False | ||
21 | + bpy.context.scene.render.use_envmaps = False | ||
22 | + bpy.context.scene.render.use_motion_blur = False | ||
23 | + bpy.context.scene.render.use_shadows = False | ||
24 | + return | ||
25 | + | ||
26 | +def file_rename(file_full_path): | ||
27 | + file_path = os.path.dirname(os.path.abspath(file_full_path)) | ||
28 | + filename = os.path.basename(os.path.splitext(file_full_path)[0]) | ||
29 | + extension = os.path.splitext(file_full_path)[1] | ||
30 | + filename_reversed = "" | ||
31 | + valid_char = False | ||
32 | + for char in reversed(filename): | ||
33 | + if (valid_char == True): | ||
34 | + filename_reversed += char | ||
35 | + if (char == "_"): | ||
36 | + valid_char = True | ||
37 | + try: | ||
38 | + filename_reversed = filename_reversed[::-1] | ||
39 | + new_filename = os.path.join(file_path, "%s%s" % (filename_reversed, extension)) | ||
40 | + shutil.move(file_full_path, new_filename) | ||
41 | + return new_filename | ||
42 | + except Exception: | ||
43 | + return "" | ||
44 | + | ||
45 | +def render_video(video_output = ""): | ||
46 | + getcwd = os.path.dirname(os.path.abspath(__file__)) | ||
47 | + base_path = os.path.join(getcwd, video_output) | ||
48 | + bpy.context.scene.render.filepath = base_path + "_" | ||
49 | + try: | ||
50 | + bpy.ops.render.render(animation = True, write_still = False, layer = "", scene = "") | ||
51 | + return str("%s%0.4i-%0.4i.mp4" % (bpy.context.scene.render.filepath, bpy.context.scene.frame_start, bpy.context.scene.frame_end)) | ||
52 | + except: | ||
53 | + return "" | ||
54 | + | ||
55 | +def main(): | ||
56 | + if (len(sys.argv) == 8): | ||
57 | + json_obj = json.loads(sys.argv[7]) | ||
58 | + try: | ||
59 | + video_output = json_obj["video_output"] | ||
60 | + convert_to_webm = json_obj["convert_to_webm"] | ||
61 | + except: | ||
62 | + sys.exit(3) | ||
63 | + try: | ||
64 | + configure_output() | ||
65 | + renamed_video = file_rename(render_video(video_output)) | ||
66 | + base_path = os.path.splitext(renamed_video)[0] | ||
67 | + if (convert_to_webm): | ||
68 | + subprocess.call(["avconv", "-loglevel", "0", "-y", "-i", renamed_video, "-r", "24", "-vcodec", "libvpx", base_path + ".webm"]) | ||
69 | + subprocess.call(["rm", renamed_video]) | ||
70 | + except: | ||
71 | + sys.exit(4) | ||
72 | + else: | ||
73 | + sys.exit(5) | ||
74 | + | ||
75 | +if __name__ == "__main__": | ||
76 | + main() |
@@ -0,0 +1,91 @@ | @@ -0,0 +1,91 @@ | ||
1 | +# -*- coding: UTF-8 -*- | ||
2 | + | ||
3 | +import copy | ||
4 | +import json | ||
5 | +import math | ||
6 | +import os | ||
7 | +import sys | ||
8 | +import subprocess | ||
9 | + | ||
10 | +getcwd = os.path.dirname(os.path.abspath(__file__)) | ||
11 | +bpy_script_action = os.path.join(getcwd, "bpy_checkout.py") | ||
12 | +bpy_script_render = os.path.join(getcwd, "bpy_render.py") | ||
13 | + | ||
14 | +def file_exists(file_path): | ||
15 | + if ((os.path.isfile(file_path) == 1) and (os.path.exists(file_path) == 1)): | ||
16 | + return True | ||
17 | + else: | ||
18 | + return False | ||
19 | + | ||
20 | +def check_action(blend_file = "", action_name = "", action_fake_is_valid = True, min_frame_count = 10, hide_output = True): | ||
21 | + if (file_exists(blend_file)): | ||
22 | + if not (isinstance(blend_file, str) and isinstance(action_name, str)): | ||
23 | + return 1 | ||
24 | + if not (isinstance(action_fake_is_valid, bool) and isinstance(min_frame_count, int)): | ||
25 | + return 1 | ||
26 | + if (action_name == ""): | ||
27 | + action_name = os.path.splitext(blend_file)[0] | ||
28 | + try: | ||
29 | + json_object = json.JSONEncoder().encode( | ||
30 | + { | ||
31 | + "action_name": action_name.upper(), | ||
32 | + "action_fake_is_valid": action_fake_is_valid, | ||
33 | + "min_frame_count": min_frame_count | ||
34 | + } | ||
35 | + ) | ||
36 | + if (hide_output): | ||
37 | + dev_null = open(os.devnull, 'w') | ||
38 | + return subprocess.call(['blender', '-b', blend_file, '-noaudio', '-P', bpy_script_action, "--", json_object], stdout = dev_null, stderr = dev_null) | ||
39 | + else: | ||
40 | + return subprocess.call(['blender', '-b', blend_file, '-noaudio', '-P', bpy_script_action, "--", json_object]) | ||
41 | + except: | ||
42 | + return 2 | ||
43 | + else: | ||
44 | + return 3 | ||
45 | + | ||
46 | +def render_video(blend_file = "", video_output = "", convert_to_webm = True, hide_output = False): | ||
47 | + if (file_exists(blend_file)): | ||
48 | + if not (isinstance(blend_file, str) and isinstance(video_output, str)): | ||
49 | + return 1 | ||
50 | + try: | ||
51 | + json_object = json.JSONEncoder().encode( | ||
52 | + { | ||
53 | + "video_output": video_output.upper(), | ||
54 | + "convert_to_webm": convert_to_webm | ||
55 | + } | ||
56 | + ) | ||
57 | + if (hide_output): | ||
58 | + dev_null = open(os.devnull, 'w') | ||
59 | + return subprocess.call(['blender', '-b', blend_file, '-noaudio', '-P', bpy_script_render, "--", json_object], stdout = dev_null, stderr = dev_null) | ||
60 | + else: | ||
61 | + return subprocess.call(['blender', '-b', blend_file, '-noaudio', '-P', bpy_script_render, "--", json_object]) | ||
62 | + except: | ||
63 | + return 2 | ||
64 | + else: | ||
65 | + return 3 | ||
66 | + | ||
67 | +def main(): | ||
68 | + print("check_action return:", check_action("casa.blend")) | ||
69 | + # return codes | ||
70 | + # 0: [OK] Blend file and Action Name exists | ||
71 | + # 1: [ERROR] Args to check_action no match types | ||
72 | + # 2: [ERROR] Subprocess interrupt | ||
73 | + # 3: [ERROR] Blend file not exists | ||
74 | + # 4: [ERROR] JSON Key no match | ||
75 | + # 5: [ERROR] Action in blend file no exists | ||
76 | + # 6: [ERROR] Action is fake user | ||
77 | + # 7: [ERROR] Timeline Frame Count is less than 10 | ||
78 | + # 8: [ERROR] Args count no match | ||
79 | + | ||
80 | + print("render_video return:", render_video("casa.blend", "casa")) | ||
81 | + # return codes | ||
82 | + # 0: [OK] Video.mp4 generated | ||
83 | + # 1: [ERROR] Args to check_action no match types | ||
84 | + # 2: [ERROR] Subprocess interrupt | ||
85 | + # 3: [ERROR] JSON Key no match | ||
86 | + # 4: [ERROR] Except in process | ||
87 | + # 5: [ERROR] Args count no match | ||
88 | + return | ||
89 | + | ||
90 | +if __name__ == "__main__": | ||
91 | + main() |
corretor.py
@@ -3,46 +3,47 @@ from werkzeug import secure_filename | @@ -3,46 +3,47 @@ from werkzeug import secure_filename | ||
3 | import pbclient | 3 | import pbclient |
4 | import os | 4 | import os |
5 | import pyutil | 5 | import pyutil |
6 | +import checkout | ||
6 | 7 | ||
7 | class Corretor: | 8 | class Corretor: |
8 | - | 9 | + |
9 | def __init__(self, configuration, template_env): | 10 | def __init__(self, configuration, template_env): |
10 | self.config = configuration | 11 | self.config = configuration |
11 | self.env = template_env | 12 | self.env = template_env |
12 | self.__setup_pb_client() | 13 | self.__setup_pb_client() |
13 | self.__setup_upload_folder() | 14 | self.__setup_upload_folder() |
14 | - | 15 | + |
15 | def __setup_pb_client(self): | 16 | def __setup_pb_client(self): |
16 | - pbclient.set('endpoint', self.config['PYBOSSA_ENDPOINT']) | 17 | + pbclient.set('endpoint', self.config['PYBOSSA_ENDPOINT']) |
17 | pbclient.set('api_key', self.config['PYBOSSA_API_KEY']) | 18 | pbclient.set('api_key', self.config['PYBOSSA_API_KEY']) |
18 | - | 19 | + |
19 | def __setup_upload_folder(self): | 20 | def __setup_upload_folder(self): |
20 | upload_folder = self.config['UPLOAD_FOLDER'] | 21 | upload_folder = self.config['UPLOAD_FOLDER'] |
21 | if not os.path.exists(upload_folder): | 22 | if not os.path.exists(upload_folder): |
22 | os.makedirs(upload_folder) | 23 | os.makedirs(upload_folder) |
23 | - | 24 | + |
24 | def __find_project(self, app_short_name): | 25 | def __find_project(self, app_short_name): |
25 | projects = pbclient.find_project(short_name=app_short_name) | 26 | projects = pbclient.find_project(short_name=app_short_name) |
26 | return projects[0] if len(projects) > 0 else None | 27 | return projects[0] if len(projects) > 0 else None |
27 | - | 28 | + |
28 | def __setup_project(self, project): | 29 | def __setup_project(self, project): |
29 | self.__create_tasks(project) | 30 | self.__create_tasks(project) |
30 | self.__update_project_info(project) | 31 | self.__update_project_info(project) |
31 | - | 32 | + |
32 | def __create_tasks(self, project): | 33 | def __create_tasks(self, project): |
33 | test_signs = ["ENSINADO", "ENTANTO", "ENTENDIDO"] | 34 | test_signs = ["ENSINADO", "ENTANTO", "ENTENDIDO"] |
34 | for sign in test_signs: | 35 | for sign in test_signs: |
35 | task = dict(sign_name=sign, submission_date=pyutil.get_date_now()) | 36 | task = dict(sign_name=sign, submission_date=pyutil.get_date_now()) |
36 | pbclient.create_task(project.id, task) | 37 | pbclient.create_task(project.id, task) |
37 | - | ||
38 | - def __update_project_info(self, project): | 38 | + |
39 | + def __update_project_info(self, project): | ||
39 | template = self.env.get_template('template.html') | 40 | template = self.env.get_template('template.html') |
40 | project.info['task_presenter'] = template.render(server=self.config['HOST_ENDPOINT']) | 41 | project.info['task_presenter'] = template.render(server=self.config['HOST_ENDPOINT']) |
41 | project.info['thumbnail'] = self.config['HOST_ENDPOINT'] + "/img/thumbnail.png" | 42 | project.info['thumbnail'] = self.config['HOST_ENDPOINT'] + "/img/thumbnail.png" |
42 | project.info['sched'] = "incremental" | 43 | project.info['sched'] = "incremental" |
43 | project.allow_anonymous_contributors = False | 44 | project.allow_anonymous_contributors = False |
44 | pbclient.update_project(project) | 45 | pbclient.update_project(project) |
45 | - | 46 | + |
46 | def create_project(self): | 47 | def create_project(self): |
47 | app_short_name = self.config['PYBOSSA_APP_SHORT_NAME'] | 48 | app_short_name = self.config['PYBOSSA_APP_SHORT_NAME'] |
48 | project = self.__find_project(app_short_name) | 49 | project = self.__find_project(app_short_name) |
@@ -54,23 +55,23 @@ class Corretor: | @@ -54,23 +55,23 @@ class Corretor: | ||
54 | if (project): | 55 | if (project): |
55 | self.__setup_project(project) | 56 | self.__setup_project(project) |
56 | result_msg = "The project " + app_short_name + " was created." | 57 | result_msg = "The project " + app_short_name + " was created." |
57 | - else: | 58 | + else: |
58 | result_msg = "The project " + app_short_name + " couldn't be created. Check the server log for details." | 59 | result_msg = "The project " + app_short_name + " couldn't be created. Check the server log for details." |
59 | pyutil.log(result_msg) | 60 | pyutil.log(result_msg) |
60 | - return result_msg | 61 | + return result_msg |
61 | 62 | ||
62 | - def update_project(self): | 63 | + def update_project(self): |
63 | app_short_name = self.config['PYBOSSA_APP_SHORT_NAME'] | 64 | app_short_name = self.config['PYBOSSA_APP_SHORT_NAME'] |
64 | project = self.__find_project(app_short_name) | 65 | project = self.__find_project(app_short_name) |
65 | self.__update_project_info(project) | 66 | self.__update_project_info(project) |
66 | result_msg = "The project " + app_short_name + " was updated." | 67 | result_msg = "The project " + app_short_name + " was updated." |
67 | pyutil.log(result_msg) | 68 | pyutil.log(result_msg) |
68 | return result_msg | 69 | return result_msg |
69 | - | 70 | + |
70 | def __allowed_file(self, filename): | 71 | def __allowed_file(self, filename): |
71 | allowed_extensions = set(['blend']) | 72 | allowed_extensions = set(['blend']) |
72 | return '.' in filename and filename.rsplit('.', 1)[1] in allowed_extensions | 73 | return '.' in filename and filename.rsplit('.', 1)[1] in allowed_extensions |
73 | - | 74 | + |
74 | def upload_file(self): | 75 | def upload_file(self): |
75 | upload_session_id = request.form['upload_session_id'] | 76 | upload_session_id = request.form['upload_session_id'] |
76 | sign_name = request.form['sign_name'] | 77 | sign_name = request.form['sign_name'] |
@@ -88,10 +89,13 @@ class Corretor: | @@ -88,10 +89,13 @@ class Corretor: | ||
88 | os.makedirs(upload_dir) | 89 | os.makedirs(upload_dir) |
89 | uploaded_file = os.path.join(upload_dir, filename) | 90 | uploaded_file = os.path.join(upload_dir, filename) |
90 | file.save(uploaded_file) | 91 | file.save(uploaded_file) |
91 | - renamed_file = os.path.join(upload_dir, secure_filename(sign_name + "_CORRIGIDO.blend")) | 92 | + renamed_file = os.path.join(upload_dir, secure_filename(sign_name + ".blend")) |
92 | os.rename(uploaded_file, renamed_file) | 93 | os.rename(uploaded_file, renamed_file) |
93 | - # validar o .blend | ||
94 | - code = 200 | ||
95 | - result_msg = "File " + filename + " was uploaded." | 94 | + if (checkout.check_action(renamed_file, sign_name) == 0): |
95 | + result_msg = "File " + filename + " was uploaded." | ||
96 | + code = 200 | ||
97 | + else: | ||
98 | + result_msg = "File " + filename + " has not expected structure of blend file." | ||
99 | + code = 400 | ||
96 | pyutil.log(result_msg) | 100 | pyutil.log(result_msg) |
97 | return make_response(result_msg, code) | 101 | return make_response(result_msg, code) |
view/template.html
@@ -147,13 +147,20 @@ | @@ -147,13 +147,20 @@ | ||
147 | $("#finish-button").addClass("enabled-button"); | 147 | $("#finish-button").addClass("enabled-button"); |
148 | $("#finish-button").off("click").on("click", function() { | 148 | $("#finish-button").off("click").on("click", function() { |
149 | // endpoint - /finish_task | 149 | // endpoint - /finish_task |
150 | - // enviar mensagem via POST para renderizar video - upload/<session_hash>/<task.info.sign_name>_CORRIGIDO.blend | 150 | + // enviar mensagem via POST para renderizar video - upload/<session_hash>/<task.info.sign_name>.blend |
151 | enableLoading(); | 151 | enableLoading(); |
152 | - setTimeout(function() { | ||
153 | - disableLoading(); | ||
154 | - status = status_dict[$("#finish-button").text().trim()]; | ||
155 | - saveAnswer(task, deferred, status); | ||
156 | - }, 3000); | 152 | + $.post("{{ server }}/finish_task", |
153 | + { | ||
154 | + "upload_session_id": upload_session_id, | ||
155 | + "sign_name": task.info.sign_name | ||
156 | + }, | ||
157 | + function(response) { | ||
158 | + console.log(response); | ||
159 | + disableLoading(); | ||
160 | + status = status_dict[$("#finish-button").text().trim()]; | ||
161 | + saveAnswer(task, deferred, status); | ||
162 | + } | ||
163 | + ); | ||
157 | }); | 164 | }); |
158 | } | 165 | } |
159 | 166 |