Attract: render thumbnail button

Still a bit rough, needs GUI response to show upload is happening.
This commit is contained in:
2016-11-03 14:47:54 +01:00
parent 603159f0d1
commit feb62ddae0

View File

@@ -36,17 +36,22 @@
# "support": "TESTING"
# }
import contextlib
import functools
import logging
if "bpy" in locals():
import importlib
importlib.reload(draw)
draw = importlib.reload(draw)
pillar = importlib.reload(pillar)
async_loop = importlib.reload(async_loop)
else:
from . import draw
from .. import pillar, async_loop
import bpy
import pillarsdk
from pillarsdk.nodes import Node
from pillarsdk.projects import Project
from pillarsdk import exceptions as sdk_exceptions
@@ -115,12 +120,14 @@ class ToolsPanel(Panel):
ro_sub.prop(strip, 'atc_notes', text='Notes')
if strip.atc_is_synced:
row = layout.row(align=True)
sub = layout.column(align=True)
row = sub.row(align=True)
row.operator('attract.submit_selected', text='Submit %s' % noun)
row.operator(AttractShotFetchUpdate.bl_idname,
text='', icon='FILE_REFRESH')
row.operator(ATTRACT_OT_shot_open_in_browser.bl_idname,
text='', icon='WORLD')
sub.operator(ATTRACT_OT_make_shot_thumbnail.bl_idname)
# Group more dangerous operations.
dangerous_sub = layout.column(align=True)
@@ -481,12 +488,131 @@ class ATTRACT_OT_open_meta_blendfile(AttractOperatorMixin, Operator):
if scene:
cmd.extend(['--python-expr',
'import bpy; bpy.context.screen.scene = bpy.data.scenes["%s"]' % scene])
'import bpy; bpy.context.screen.scene = bpy.data.scenes["%s"]' % scene])
cmd.extend(['--scene', scene])
subprocess.Popen(cmd)
@contextlib.contextmanager
def thumbnail_render_settings(context, thumbnail_width=512):
orig_res_x = context.scene.render.resolution_x
orig_res_y = context.scene.render.resolution_y
orig_percentage = context.scene.render.resolution_percentage
orig_file_format = context.scene.render.image_settings.file_format
orig_quality = context.scene.render.image_settings.quality
try:
# Update the render size to something thumbnaily.
factor = orig_res_y / orig_res_x
context.scene.render.resolution_x = thumbnail_width
context.scene.render.resolution_y = round(thumbnail_width * factor)
context.scene.render.resolution_percentage = 100
context.scene.render.image_settings.file_format = 'JPEG'
context.scene.render.image_settings.quality = 85
yield
finally:
# Return the render settings to normal.
context.scene.render.resolution_x = orig_res_x
context.scene.render.resolution_y = orig_res_y
context.scene.render.resolution_percentage = orig_percentage
context.scene.render.image_settings.file_format = orig_file_format
context.scene.render.image_settings.quality = orig_quality
class ATTRACT_OT_make_shot_thumbnail(AttractOperatorMixin,
async_loop.AsyncModalOperatorMixin,
Operator):
bl_idname = 'attract.make_shot_thumbnail'
bl_label = 'Render shot thumbnail'
bl_description = 'Renders the current frame, and uploads it as thumbnail for the shot'
stop_upon_exception = True
async def async_execute(self, context):
import tempfile
# Later: for strip in context.selected_sequences:
strip = active_strip(context)
atc_object_id = getattr(strip, 'atc_object_id', None)
if not atc_object_id:
self.report({'ERROR'}, 'Strip %s not set up for Attract' % strip.name)
self.quit()
return
with tempfile.NamedTemporaryFile() as tmpfile:
with thumbnail_render_settings(context):
bpy.ops.render.render()
file_id = await self.upload_via_tempdir(bpy.data.images['Render Result'],
'attract_shot_thumbnail.jpg')
if file_id is None:
self.quit()
return
# Update the shot to include this file as the picture.
node = pillarsdk.Node({'_id': atc_object_id})
await pillar.pillar_call(
node.patch,
{
'op': 'from-blender',
'$set': {
'picture': file_id,
}
})
self.report({'INFO'}, 'Thumbnail uploaded to Attract')
self.quit()
async def upload_via_tempdir(self, datablock, filename_on_cloud) -> pillarsdk.Node:
"""Saves the datablock to file, and uploads it to the cloud.
Saving is done to a temporary directory, which is removed afterwards.
Returns the node.
"""
import tempfile
import os.path
with tempfile.TemporaryDirectory() as tmpdir:
filepath = os.path.join(tmpdir, filename_on_cloud)
self.log.debug('Saving %s to %s', datablock, filepath)
datablock.save_render(filepath)
return await self.upload_file(filepath)
async def upload_file(self, filename: str, fileobj=None):
"""Uploads a file to the cloud, attached to the image sharing node.
Returns the node.
"""
from .. import blender
prefs = blender.preferences()
project = self.find_project(prefs.attract_project.project)
self.log.info('Uploading file %s', filename)
resp = await pillar.pillar_call(
pillarsdk.File.upload_to_project,
project['_id'],
'image/jpeg',
filename,
fileobj=fileobj)
self.log.debug('Returned data: %s', resp)
try:
file_id = resp['file_id']
except KeyError:
self.log.error('Upload did not succeed, response: %s', resp)
self.report({'ERROR'}, 'Unable to upload thumbnail to Attract: %s' % resp)
return None
self.log.info('Created file %s', file_id)
self.report({'INFO'}, 'File succesfully uploaded to the cloud!')
return file_id
def draw_strip_movie_meta(self, context):
strip = active_strip(context)
if not strip:
@@ -537,6 +663,7 @@ def register():
bpy.utils.register_class(AttractShotSubmitSelected)
bpy.utils.register_class(ATTRACT_OT_open_meta_blendfile)
bpy.utils.register_class(ATTRACT_OT_shot_open_in_browser)
bpy.utils.register_class(ATTRACT_OT_make_shot_thumbnail)
draw.callback_enable()