Commit | Line | Data |
---|---|---|
76918e52 AN |
1 | # GNU MediaGoblin -- federated, autonomous media hosting |
2 | # Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS. | |
3 | # | |
4 | # This program is free software: you can redistribute it and/or modify | |
5 | # it under the terms of the GNU Affero General Public License as published by | |
6 | # the Free Software Foundation, either version 3 of the License, or | |
7 | # (at your option) any later version. | |
8 | # | |
9 | # This program is distributed in the hope that it will be useful, | |
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | # GNU Affero General Public License for more details. | |
13 | # | |
14 | # You should have received a copy of the GNU Affero General Public License | |
15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
16 | ||
17 | import os | |
12a10467 | 18 | import json |
76918e52 | 19 | import logging |
12a10467 | 20 | import subprocess |
39c340f2 | 21 | import pkg_resources |
76918e52 AN |
22 | |
23 | from mediagoblin import mg_globals as mgg | |
45ab3e07 | 24 | from mediagoblin.decorators import get_workbench |
76918e52 AN |
25 | from mediagoblin.processing import create_pub_filepath, \ |
26 | FilenameBuilder | |
27 | ||
28 | from mediagoblin.media_types.stl import model_loader | |
29 | ||
30 | ||
31 | _log = logging.getLogger(__name__) | |
32 | SUPPORTED_FILETYPES = ['stl', 'obj'] | |
33 | ||
39c340f2 CAW |
34 | BLEND_FILE = pkg_resources.resource_filename( |
35 | 'mediagoblin.media_types.stl', | |
36 | os.path.join( | |
37 | 'assets', | |
38 | 'blender_render.blend')) | |
39 | BLEND_SCRIPT = pkg_resources.resource_filename( | |
40 | 'mediagoblin.media_types.stl', | |
41 | os.path.join( | |
42 | 'assets', | |
43 | 'blender_render.py')) | |
44 | ||
76918e52 AN |
45 | |
46 | def sniff_handler(media_file, **kw): | |
47 | if kw.get('media') is not None: | |
48 | name, ext = os.path.splitext(kw['media'].filename) | |
49 | clean_ext = ext[1:].lower() | |
50 | ||
51 | if clean_ext in SUPPORTED_FILETYPES: | |
52 | _log.info('Found file extension in supported filetypes') | |
53 | return True | |
54 | else: | |
55 | _log.debug('Media present, extension not found in {0}'.format( | |
56 | SUPPORTED_FILETYPES)) | |
57 | else: | |
58 | _log.warning('Need additional information (keyword argument \'media\')' | |
59 | ' to be able to handle sniffing') | |
60 | ||
61 | return False | |
62 | ||
63 | ||
12a10467 AN |
64 | def blender_render(config): |
65 | """ | |
66 | Called to prerender a model. | |
67 | """ | |
68 | arg_string = "blender -b blender_render.blend -F " | |
69 | arg_string +="JPEG -P blender_render.py" | |
70 | env = {"RENDER_SETUP" : json.dumps(config), "DISPLAY":":0"} | |
39c340f2 CAW |
71 | subprocess.call( |
72 | ["blender", | |
73 | "-b", BLEND_FILE, | |
74 | "-F", "JPEG", | |
75 | "-P", BLEND_SCRIPT], | |
76 | env=env) | |
12a10467 AN |
77 | |
78 | ||
45ab3e07 SS |
79 | @get_workbench |
80 | def process_stl(entry, workbench=None): | |
81 | """Code to process an stl or obj model. Will be run by celery. | |
82 | ||
83 | A Workbench() represents a local tempory dir. It is automatically | |
84 | cleaned up when this function exits. | |
76918e52 | 85 | """ |
76918e52 AN |
86 | queued_filepath = entry.queued_media_file |
87 | queued_filename = workbench.localized_file( | |
88 | mgg.queue_store, queued_filepath, 'source') | |
89 | name_builder = FilenameBuilder(queued_filename) | |
90 | ||
91 | ext = queued_filename.lower().strip()[-4:] | |
92 | if ext.startswith("."): | |
93 | ext = ext[1:] | |
94 | else: | |
95 | ext = None | |
96 | ||
97 | # Attempt to parse the model file and divine some useful | |
98 | # information about it. | |
99 | with open(queued_filename, 'rb') as model_file: | |
100 | model = model_loader.auto_detect(model_file, ext) | |
101 | ||
12a10467 AN |
102 | # generate preview images |
103 | greatest = [model.width, model.height, model.depth] | |
104 | greatest.sort() | |
105 | greatest = greatest[-1] | |
106 | ||
107 | def snap(name, camera, width=640, height=640, project="ORTHO"): | |
e7e43534 CAW |
108 | filename = name_builder.fill(name) |
109 | workbench_path = workbench.joinpath(filename) | |
12a10467 | 110 | shot = { |
39c340f2 CAW |
111 | "model_path": queued_filename, |
112 | "model_ext": ext, | |
113 | "camera_coord": camera, | |
114 | "camera_focus": model.average, | |
115 | "camera_clip": greatest*10, | |
116 | "greatest": greatest, | |
117 | "projection": project, | |
118 | "width": width, | |
119 | "height": height, | |
e7e43534 | 120 | "out_file": workbench_path, |
12a10467 | 121 | } |
12a10467 | 122 | blender_render(shot) |
e7e43534 CAW |
123 | |
124 | # make sure the image rendered to the workbench path | |
125 | assert os.path.exists(workbench_path) | |
126 | ||
127 | # copy it up! | |
128 | with open(workbench_path, 'rb') as rendered_file: | |
129 | public_path = create_pub_filepath(entry, filename) | |
130 | ||
131 | with mgg.public_store.get_file(public_path, "wb") as public_file: | |
132 | public_file.write(rendered_file.read()) | |
133 | ||
134 | return public_path | |
12a10467 AN |
135 | |
136 | thumb_path = snap( | |
137 | "{basename}.thumb.jpg", | |
138 | [0, greatest*-1.5, greatest], | |
139 | mgg.global_config['media:thumb']['max_width'], | |
140 | mgg.global_config['media:thumb']['max_height'], | |
141 | project="PERSP") | |
142 | ||
143 | perspective_path = snap( | |
144 | "{basename}.perspective.jpg", | |
145 | [0, greatest*-1.5, greatest], project="PERSP") | |
146 | ||
147 | topview_path = snap( | |
148 | "{basename}.top.jpg", | |
149 | [model.average[0], model.average[1], greatest*2]) | |
150 | ||
151 | frontview_path = snap( | |
152 | "{basename}.front.jpg", | |
153 | [model.average[0], greatest*-2, model.average[2]]) | |
154 | ||
155 | sideview_path = snap( | |
156 | "{basename}.side.jpg", | |
157 | [greatest*-2, model.average[1], model.average[2]]) | |
158 | ||
e7e43534 | 159 | ## Save the public file stuffs |
76918e52 AN |
160 | model_filepath = create_pub_filepath( |
161 | entry, name_builder.fill('{basename}{ext}')) | |
162 | ||
163 | with mgg.public_store.get_file(model_filepath, 'wb') as model_file: | |
164 | with open(queued_filename, 'rb') as queued_file: | |
165 | model_file.write(queued_file.read()) | |
166 | ||
76918e52 AN |
167 | # Remove queued media file from storage and database |
168 | mgg.queue_store.delete_file(queued_filepath) | |
169 | entry.queued_media_file = [] | |
45ab3e07 | 170 | |
76918e52 AN |
171 | # Insert media file information into database |
172 | media_files_dict = entry.setdefault('media_files', {}) | |
173 | media_files_dict[u'original'] = model_filepath | |
12a10467 AN |
174 | media_files_dict[u'thumb'] = thumb_path |
175 | media_files_dict[u'perspective'] = perspective_path | |
176 | media_files_dict[u'top'] = topview_path | |
177 | media_files_dict[u'side'] = sideview_path | |
178 | media_files_dict[u'front'] = frontview_path | |
76918e52 AN |
179 | |
180 | # Put model dimensions into the database | |
181 | dimensions = { | |
182 | "center_x" : model.average[0], | |
183 | "center_y" : model.average[1], | |
184 | "center_z" : model.average[2], | |
185 | "width" : model.width, | |
186 | "height" : model.height, | |
187 | "depth" : model.depth, | |
d25ed5dd | 188 | "file_type" : ext, |
76918e52 AN |
189 | } |
190 | entry.media_data_init(**dimensions) |