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 @@ |
| 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 @@ |
| 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 @@ |
| 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 | 3 | import pbclient |
| 4 | 4 | import os |
| 5 | 5 | import pyutil |
| 6 | +import checkout | |
| 6 | 7 | |
| 7 | 8 | class Corretor: |
| 8 | - | |
| 9 | + | |
| 9 | 10 | def __init__(self, configuration, template_env): |
| 10 | 11 | self.config = configuration |
| 11 | 12 | self.env = template_env |
| 12 | 13 | self.__setup_pb_client() |
| 13 | 14 | self.__setup_upload_folder() |
| 14 | - | |
| 15 | + | |
| 15 | 16 | def __setup_pb_client(self): |
| 16 | - pbclient.set('endpoint', self.config['PYBOSSA_ENDPOINT']) | |
| 17 | + pbclient.set('endpoint', self.config['PYBOSSA_ENDPOINT']) | |
| 17 | 18 | pbclient.set('api_key', self.config['PYBOSSA_API_KEY']) |
| 18 | - | |
| 19 | + | |
| 19 | 20 | def __setup_upload_folder(self): |
| 20 | 21 | upload_folder = self.config['UPLOAD_FOLDER'] |
| 21 | 22 | if not os.path.exists(upload_folder): |
| 22 | 23 | os.makedirs(upload_folder) |
| 23 | - | |
| 24 | + | |
| 24 | 25 | def __find_project(self, app_short_name): |
| 25 | 26 | projects = pbclient.find_project(short_name=app_short_name) |
| 26 | 27 | return projects[0] if len(projects) > 0 else None |
| 27 | - | |
| 28 | + | |
| 28 | 29 | def __setup_project(self, project): |
| 29 | 30 | self.__create_tasks(project) |
| 30 | 31 | self.__update_project_info(project) |
| 31 | - | |
| 32 | + | |
| 32 | 33 | def __create_tasks(self, project): |
| 33 | 34 | test_signs = ["ENSINADO", "ENTANTO", "ENTENDIDO"] |
| 34 | 35 | for sign in test_signs: |
| 35 | 36 | task = dict(sign_name=sign, submission_date=pyutil.get_date_now()) |
| 36 | 37 | pbclient.create_task(project.id, task) |
| 37 | - | |
| 38 | - def __update_project_info(self, project): | |
| 38 | + | |
| 39 | + def __update_project_info(self, project): | |
| 39 | 40 | template = self.env.get_template('template.html') |
| 40 | 41 | project.info['task_presenter'] = template.render(server=self.config['HOST_ENDPOINT']) |
| 41 | 42 | project.info['thumbnail'] = self.config['HOST_ENDPOINT'] + "/img/thumbnail.png" |
| 42 | 43 | project.info['sched'] = "incremental" |
| 43 | 44 | project.allow_anonymous_contributors = False |
| 44 | 45 | pbclient.update_project(project) |
| 45 | - | |
| 46 | + | |
| 46 | 47 | def create_project(self): |
| 47 | 48 | app_short_name = self.config['PYBOSSA_APP_SHORT_NAME'] |
| 48 | 49 | project = self.__find_project(app_short_name) |
| ... | ... | @@ -54,23 +55,23 @@ class Corretor: |
| 54 | 55 | if (project): |
| 55 | 56 | self.__setup_project(project) |
| 56 | 57 | result_msg = "The project " + app_short_name + " was created." |
| 57 | - else: | |
| 58 | + else: | |
| 58 | 59 | result_msg = "The project " + app_short_name + " couldn't be created. Check the server log for details." |
| 59 | 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 | 64 | app_short_name = self.config['PYBOSSA_APP_SHORT_NAME'] |
| 64 | 65 | project = self.__find_project(app_short_name) |
| 65 | 66 | self.__update_project_info(project) |
| 66 | 67 | result_msg = "The project " + app_short_name + " was updated." |
| 67 | 68 | pyutil.log(result_msg) |
| 68 | 69 | return result_msg |
| 69 | - | |
| 70 | + | |
| 70 | 71 | def __allowed_file(self, filename): |
| 71 | 72 | allowed_extensions = set(['blend']) |
| 72 | 73 | return '.' in filename and filename.rsplit('.', 1)[1] in allowed_extensions |
| 73 | - | |
| 74 | + | |
| 74 | 75 | def upload_file(self): |
| 75 | 76 | upload_session_id = request.form['upload_session_id'] |
| 76 | 77 | sign_name = request.form['sign_name'] |
| ... | ... | @@ -88,10 +89,13 @@ class Corretor: |
| 88 | 89 | os.makedirs(upload_dir) |
| 89 | 90 | uploaded_file = os.path.join(upload_dir, filename) |
| 90 | 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 | 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 | 100 | pyutil.log(result_msg) |
| 97 | 101 | return make_response(result_msg, code) | ... | ... |
view/template.html
| ... | ... | @@ -147,13 +147,20 @@ |
| 147 | 147 | $("#finish-button").addClass("enabled-button"); |
| 148 | 148 | $("#finish-button").off("click").on("click", function() { |
| 149 | 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 | 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 | ... | ... |