Compare commits

..

12 Commits

Author SHA1 Message Date
e32e75e3db Bumped version to 1.15 and marked as released in CHANGELOG 2019-12-12 10:42:08 +01:00
6fa5ab5481 Removed trailing period from property description
No functional changes.
2019-12-12 10:40:58 +01:00
379580de86 Don't create BAT pack when rendering file in job storage directory
When the to-be-rendered blend file is contained in the job storage
directory, it is now assumed that all files are already reachable by the
Flamenco Workers. This supports environments working directly on shared
storage.

This assumes that the paths are already correct for the Flamenco
Workers. No detection of missing files is done (as BAT doesn't run).
2019-10-25 13:34:34 +02:00
db30b3df76 Bumped version to 1.14 2019-10-10 10:39:37 +02:00
5de99baaef Updated changelog 2019-10-10 10:39:28 +02:00
2184b39d27 Bump Blender Asset Tracer (BAT) version from 1.1.1 → 1.2.1 2019-10-10 10:29:53 +02:00
23b1f7de7d Convert property definitions from assignment to annotations on Blender 2.80+
The properties are still declared in the Python 3.5 compatible assignment
notation, and a class decorator that converts those to class annotations
as preferred by Blender 2.80.
2019-10-10 10:29:36 +02:00
28f68c6fbf update_version.sh: Use Python 3 in example command
This makes it possible to run the command outside of a Python 3 virtualenv.
2019-06-21 14:31:54 +02:00
b00cb233cc Bumped version to 1.13.5 2019-06-21 14:30:03 +02:00
2142e9e7fc Attract fix for Blender 2.80 panel change
Commit 1e7c3a159fd2ca42fd5688be067008ef0d2c03df removed the 'Info' panel
(which is good), so we have to attach the metadata subpanel somewhere else.
2019-06-21 14:29:49 +02:00
1dea802932 Attract doesn't have to be active to use ATTRACT_OT_open_meta_blendfile
It is pretty much independent of Attract.
2019-06-21 14:29:07 +02:00
077bd1abdb Prevent KeyError when Flamenco Manager settings are unknown 2019-06-12 11:47:16 +02:00
13 changed files with 163 additions and 51 deletions

View File

@@ -1,5 +1,18 @@
# Blender Cloud changelog
## Version 1.15 (2019-12-12)
- Avoid creating BAT pack when the to-be-rendered file is already inside the job storage
directory. This assumes that the paths are already correct for the Flamenco Workers.
## Version 1.14 (2019-10-10)
- Upgraded BAT to 1.2 for missing smoke caches, compatibility with Blender 2.81, and some
Windows-specific fixes.
- Removed warnings on the terminal when running Blender 2.80+
## Version 1.13 (2019-04-18)
- Upgraded BAT to 1.1.1 for a compatibility fix with Blender 2.79

View File

@@ -21,7 +21,7 @@
bl_info = {
'name': 'Blender Cloud',
"author": "Sybren A. Stüvel, Francesco Siddi, Inês Almeida, Antony Riakiotakis",
'version': (1, 13, 4),
'version': (1, 15),
'blender': (2, 80, 0),
'location': 'Addon Preferences panel, and Ctrl+Shift+Alt+A anywhere for texture browser',
'description': 'Texture library browser and Blender Sync. Requires the Blender ID addon '

View File

@@ -47,6 +47,7 @@ if "bpy" in locals():
pillar = importlib.reload(pillar)
async_loop = importlib.reload(async_loop)
blender = importlib.reload(blender)
compatibility = importlib.reload(compatibility)
else:
import bpy
@@ -54,7 +55,7 @@ else:
from . import draw_27 as draw
else:
from . import draw
from .. import pillar, async_loop, blender
from .. import pillar, async_loop, blender, compatibility
import bpy
import pillarsdk
@@ -252,7 +253,7 @@ class ATTRACT_PT_tools(AttractPollMixin, Panel):
icon='RENDER_STILL')
# Group more dangerous operations.
dangerous_sub = layout.split(**blender.factor(0.6), align=True)
dangerous_sub = layout.split(**compatibility.factor(0.6), align=True)
dangerous_sub.operator('attract.strip_unlink',
text='Unlink %s' % noun,
icon='PANEL_CLOSE')
@@ -408,6 +409,7 @@ class ATTRACT_OT_shot_fetch_update(AttractOperatorMixin, Operator):
return {'FINISHED'}
@compatibility.convert_properties
class ATTRACT_OT_shot_relink(AttractOperatorMixin, Operator):
bl_idname = "attract.shot_relink"
bl_label = "Relink With Attract"
@@ -477,6 +479,7 @@ class ATTRACT_OT_shot_open_in_browser(AttractOperatorMixin, Operator):
return {'FINISHED'}
@compatibility.convert_properties
class ATTRACT_OT_shot_delete(AttractOperatorMixin, Operator):
bl_idname = 'attract.shot_delete'
bl_label = 'Delete Shot'
@@ -640,8 +643,7 @@ class ATTRACT_OT_open_meta_blendfile(AttractOperatorMixin, Operator):
@classmethod
def poll(cls, context):
return AttractOperatorMixin.poll(context) and \
bool(any(cls.filename_from_metadata(s) for s in context.selected_sequences))
return bool(any(cls.filename_from_metadata(s) for s in context.selected_sequences))
@staticmethod
def filename_from_metadata(strip):
@@ -916,6 +918,7 @@ class ATTRACT_OT_copy_id_to_clipboard(AttractOperatorMixin, Operator):
return {'FINISHED'}
@compatibility.convert_properties
class ATTRACT_OT_project_open_in_browser(Operator):
bl_idname = 'attract.project_open_in_browser'
bl_label = 'Open Project in Browser'
@@ -949,7 +952,7 @@ class ATTRACT_OT_project_open_in_browser(Operator):
class ATTRACT_PT_strip_metadata(bl_ui.space_sequencer.SequencerButtonsPanel, Panel):
bl_label = "Metadata"
bl_parent_id = "SEQUENCER_PT_info"
bl_parent_id = "SEQUENCER_PT_source"
bl_category = "Strip"
bl_options = {'DEFAULT_CLOSED'}

View File

@@ -30,7 +30,7 @@ from bpy.types import AddonPreferences, Operator, WindowManager, Scene, Property
from bpy.props import StringProperty, EnumProperty, PointerProperty, BoolProperty, IntProperty
import rna_prop_ui
from . import pillar, async_loop, flamenco, project_specific
from . import compatibility, pillar, async_loop, flamenco, project_specific
from .utils import pyside_cache, redraw
PILLAR_WEB_SERVER_URL = os.environ.get('BCLOUD_SERVER', 'https://cloud.blender.org/')
@@ -40,23 +40,6 @@ ADDON_NAME = 'blender_cloud'
log = logging.getLogger(__name__)
icons = None
if bpy.app.version < (2, 80):
SYNC_SELECT_VERSION_ICON = 'DOTSDOWN'
else:
SYNC_SELECT_VERSION_ICON = 'DOWNARROW_HLT'
@functools.lru_cache()
def factor(factor: float) -> dict:
"""Construct keyword argument for UILayout.split().
On Blender 2.8 this returns {'factor': factor}, and on earlier Blenders it returns
{'percentage': factor}.
"""
if bpy.app.version < (2, 80, 0):
return {'percentage': factor}
return {'factor': factor}
@pyside_cache('version')
def blender_syncable_versions(self, context):
@@ -69,6 +52,7 @@ def blender_syncable_versions(self, context):
return [(v, v, '') for v in versions]
@compatibility.convert_properties
class SyncStatusProperties(PropertyGroup):
status = EnumProperty(
items=[
@@ -156,6 +140,7 @@ def project_extensions(project_id) -> set:
return set(proj.get('enabled_for', ()))
@compatibility.convert_properties
class BlenderCloudProjectGroup(PropertyGroup):
status = EnumProperty(
items=[
@@ -185,6 +170,7 @@ class BlenderCloudProjectGroup(PropertyGroup):
project_specific.handle_project_update()
@compatibility.convert_properties
class BlenderCloudPreferences(AddonPreferences):
bl_idname = ADDON_NAME
@@ -257,7 +243,7 @@ class BlenderCloudPreferences(AddonPreferences):
name='Relative Paths Only',
description='When enabled, only assets that are referred to with a relative path are '
'packed, and assets referred to by an absolute path are excluded from the '
'BAT pack. When disabled, all assets are packed.',
'BAT pack. When disabled, all assets are packed',
default=False,
update=project_specific.store,
)
@@ -331,7 +317,7 @@ class BlenderCloudPreferences(AddonPreferences):
bss = context.window_manager.blender_sync_status
bsync_box = layout.box()
bsync_box.enabled = msg_icon != 'ERROR'
row = bsync_box.row().split(**factor(0.33))
row = bsync_box.row().split(**compatibility.factor(0.33))
row.label(text='Blender Sync with Blender Cloud', icon_value=icon('CLOUD'))
icon_for_level = {
@@ -374,7 +360,7 @@ class BlenderCloudPreferences(AddonPreferences):
layout.enabled = bss.status in {'NONE', 'IDLE'}
buttons = layout.column()
row_buttons = buttons.row().split(**factor(0.5))
row_buttons = buttons.row().split(**compatibility.factor(0.5))
row_push = row_buttons.row()
row_pull = row_buttons.row(align=True)
@@ -396,7 +382,7 @@ class BlenderCloudPreferences(AddonPreferences):
props.blender_version = bss.version
row_pull.operator('pillar.sync',
text='',
icon=SYNC_SELECT_VERSION_ICON).action = 'SELECT'
icon=compatibility.SYNC_SELECT_VERSION_ICON).action = 'SELECT'
else:
row_pull.label(text='Cloud Sync is running.')
@@ -444,7 +430,7 @@ class BlenderCloudPreferences(AddonPreferences):
header_row = flamenco_box.row(align=True)
header_row.label(text='Flamenco:', icon_value=icon('CLOUD'))
manager_split = flamenco_box.split(**factor(0.32), align=True)
manager_split = flamenco_box.split(**compatibility.factor(0.32), align=True)
manager_split.label(text='Manager:')
manager_box = manager_split.row(align=True)
@@ -461,7 +447,7 @@ class BlenderCloudPreferences(AddonPreferences):
else:
manager_box.label(text='Fetching available managers.')
path_split = flamenco_box.split(**factor(0.32), align=True)
path_split = flamenco_box.split(**compatibility.factor(0.32), align=True)
path_split.label(text='Job File Path:')
path_box = path_split.row(align=True)
path_box.prop(self, 'flamenco_job_file_path', text='')
@@ -469,7 +455,7 @@ class BlenderCloudPreferences(AddonPreferences):
props.path = self.flamenco_job_file_path
job_output_box = flamenco_box.column(align=True)
path_split = job_output_box.split(**factor(0.32), align=True)
path_split = job_output_box.split(**compatibility.factor(0.32), align=True)
path_split.label(text='Job Output Path:')
path_box = path_split.row(align=True)
path_box.prop(self, 'flamenco_job_output_path', text='')
@@ -477,7 +463,7 @@ class BlenderCloudPreferences(AddonPreferences):
props.path = self.flamenco_job_output_path
job_output_box.prop(self, 'flamenco_exclude_filter')
prop_split = job_output_box.split(**factor(0.32), align=True)
prop_split = job_output_box.split(**compatibility.factor(0.32), align=True)
prop_split.label(text='Strip Components:')
prop_split.prop(self, 'flamenco_job_output_strip_components', text='')
@@ -561,6 +547,7 @@ class PILLAR_OT_subscribe(Operator):
return {'FINISHED'}
@compatibility.convert_properties
class PILLAR_OT_project_open_in_browser(Operator):
bl_idname = 'pillar.project_open_in_browser'
bl_label = 'Open in Browser'

View File

@@ -0,0 +1,70 @@
"""Compatibility functions to support Blender 2.79 and 2.80+ in one code base."""
import functools
import bpy
if bpy.app.version < (2, 80):
SYNC_SELECT_VERSION_ICON = 'DOTSDOWN'
else:
SYNC_SELECT_VERSION_ICON = 'DOWNARROW_HLT'
# Get references to all property definition functions in bpy.props,
# so that they can be used to replace 'x = IntProperty()' to 'x: IntProperty()'
# dynamically when working on Blender 2.80+
__all_prop_funcs = {
getattr(bpy.props, propname)
for propname in dir(bpy.props)
if propname.endswith('Property')
}
def convert_properties(class_):
"""Class decorator to avoid warnings in Blender 2.80+
This decorator replaces property definitions like this:
someprop = bpy.props.IntProperty()
to annotations, as introduced in Blender 2.80:
someprop: bpy.props.IntProperty()
No-op if running on Blender 2.79 or older.
"""
if bpy.app.version < (2, 80):
return class_
if not hasattr(class_, '__annotations__'):
class_.__annotations__ = {}
attrs_to_delete = []
for name, value in class_.__dict__.items():
if not isinstance(value, tuple) or len(value) != 2:
continue
prop_func, kwargs = value
if prop_func not in __all_prop_funcs:
continue
# This is a property definition, replace it with annotation.
attrs_to_delete.append(name)
class_.__annotations__[name] = value
for attr_name in attrs_to_delete:
delattr(class_, attr_name)
return class_
@functools.lru_cache()
def factor(factor: float) -> dict:
"""Construct keyword argument for UILayout.split().
On Blender 2.8 this returns {'factor': factor}, and on earlier Blenders it returns
{'percentage': factor}.
"""
if bpy.app.version < (2, 80, 0):
return {'percentage': factor}
return {'factor': factor}

View File

@@ -34,12 +34,13 @@ if "bpy" in locals():
bat_interface = importlib.reload(bat_interface)
sdk = importlib.reload(sdk)
blender = importlib.reload(blender)
compatibility = importlib.reload(compatibility)
except NameError:
from . import bat_interface, sdk
from .. import blender
from .. import blender, compatibility
else:
from . import bat_interface, sdk
from .. import blender
from .. import blender, compatibility
import bpy
from bpy.types import AddonPreferences, Operator, WindowManager, Scene, PropertyGroup
@@ -138,6 +139,7 @@ def silently_quit_blender():
bpy.ops.wm.quit_blender()
@compatibility.convert_properties
class FlamencoManagerGroup(PropertyGroup):
manager = EnumProperty(
items=available_managers,
@@ -238,6 +240,24 @@ def guess_output_file_extension(output_format: str, scene) -> str:
return '.' + container.lower()
def is_file_inside_job_storage(prefs, current_file: Path) -> bool:
"""Check whether current blend file is inside the storage path.
:return: True when 'current_file' is inside the Flamenco
job storage directory already. In this case it won't be
BAT-packed, as it's assumed the job storage dir is
accessible by the workers already.
"""
flamenco_job_file_path = Path(prefs.flamenco_job_file_path).absolute().resolve()
current_file = current_file.absolute().resolve()
try:
current_file.relative_to(flamenco_job_file_path)
except ValueError:
return False
return True
@compatibility.convert_properties
class FLAMENCO_OT_render(async_loop.AsyncModalOperatorMixin,
pillar.AuthenticatedPillarOperatorMixin,
FlamencoPollMixin,
@@ -435,7 +455,7 @@ class FLAMENCO_OT_render(async_loop.AsyncModalOperatorMixin,
# Pop out some settings so that settings of irrelevant Managers are excluded.
flamenco_managers_settings = project_settings.pop('flamenco_managers_settings', {})
flamenco_manager_settings = flamenco_managers_settings.pop(manager_id)
flamenco_manager_settings = flamenco_managers_settings.pop(manager_id, '-unknown-')
info = {
'_meta': {'version': 2},
@@ -593,6 +613,11 @@ class FLAMENCO_OT_render(async_loop.AsyncModalOperatorMixin,
outfile = PurePath('{shaman}') / outfile
return None, outfile, missing_sources
if is_file_inside_job_storage(prefs, filepath):
# The blend file is contained in the job storage path, no need to copy anything.
# Since BAT doesn't run, we also don't know whether files are missing.
return filepath.parent, filepath, []
# Create a unique directory that is still more or less identifyable.
# This should work better than a random ID.
unique_dir = '%s-%s-%s' % (datetime.now().isoformat('-').replace(':', ''),
@@ -729,6 +754,7 @@ class FLAMENCO_OT_abort(Operator, FlamencoPollMixin):
return {'FINISHED'}
@compatibility.convert_properties
class FLAMENCO_OT_explore_file_path(FlamencoPollMixin,
Operator):
"""Opens the Flamenco job storage path in a file explorer.
@@ -796,6 +822,7 @@ class FLAMENCO_OT_disable_output_path_override(Operator):
return {'FINISHED'}
@compatibility.convert_properties
class FLAMENCO_OT_set_recommended_sample_cap(Operator):
bl_idname = 'flamenco.set_recommended_sample_cap'
bl_label = 'Set Recommended Maximum Sample Count'
@@ -962,7 +989,7 @@ class FLAMENCO_PT_render(bpy.types.Panel, FlamencoPollMixin):
prefs = preferences()
labeled_row = layout.split(**blender.factor(0.25), align=True)
labeled_row = layout.split(**compatibility.factor(0.25), align=True)
labeled_row.label(text='Manager:')
prop_btn_row = labeled_row.row(align=True)
@@ -980,7 +1007,7 @@ class FLAMENCO_PT_render(bpy.types.Panel, FlamencoPollMixin):
else:
prop_btn_row.label(text='Fetching available managers.')
labeled_row = layout.split(**blender.factor(0.25), align=True)
labeled_row = layout.split(**compatibility.factor(0.25), align=True)
labeled_row.label(text='Job Type:')
labeled_row.prop(context.scene, 'flamenco_render_job_type', text='')
@@ -1005,7 +1032,7 @@ class FLAMENCO_PT_render(bpy.types.Panel, FlamencoPollMixin):
sample_count = scene_sample_count(context.scene)
recommended_cap = sample_count // 4
split = box.split(**blender.factor(0.4))
split = box.split(**compatibility.factor(0.4))
split.label(text='Total Sample Count: %d' % sample_count)
props = split.operator('flamenco.set_recommended_sample_cap',
text='Recommended Max Samples per Task: %d' % recommended_cap)
@@ -1019,7 +1046,7 @@ class FLAMENCO_PT_render(bpy.types.Panel, FlamencoPollMixin):
else:
box.prop(context.scene, 'flamenco_render_fchunk_size')
labeled_row = layout.split(**blender.factor(0.25), align=True)
labeled_row = layout.split(**compatibility.factor(0.25), align=True)
labeled_row.label(text='Frame Range:')
prop_btn_row = labeled_row.row(align=True)
prop_btn_row.prop(context.scene, 'flamenco_render_frame_range', text='')
@@ -1030,7 +1057,7 @@ class FLAMENCO_PT_render(bpy.types.Panel, FlamencoPollMixin):
paths_layout = layout.column(align=True)
labeled_row = paths_layout.split(**blender.factor(0.25), align=True)
labeled_row = paths_layout.split(**compatibility.factor(0.25), align=True)
labeled_row.label(text='Storage:')
prop_btn_row = labeled_row.row(align=True)
prop_btn_row.label(text=prefs.flamenco_job_file_path)
@@ -1038,12 +1065,18 @@ class FLAMENCO_PT_render(bpy.types.Panel, FlamencoPollMixin):
text='', icon='DISK_DRIVE')
props.path = prefs.flamenco_job_file_path
if is_file_inside_job_storage(prefs, Path(context.blend_data.filepath)):
# File is contained in the job storage path, no need to copy anything.
paths_layout.label(text='Current file already in job storage path; '
'not going to create BAT pack.')
render_output = render_output_path(context)
if render_output is None:
paths_layout.label(text='Unable to render with Flamenco, outside of project directory.')
return
labeled_row = paths_layout.split(**blender.factor(0.25), align=True)
labeled_row = paths_layout.split(**compatibility.factor(0.25), align=True)
labeled_row.label(text='Output:')
prop_btn_row = labeled_row.row(align=True)
@@ -1062,7 +1095,7 @@ class FLAMENCO_PT_render(bpy.types.Panel, FlamencoPollMixin):
props.path = str(render_output.parent)
if context.scene.flamenco_do_override_output_path:
labeled_row = paths_layout.split(**blender.factor(0.25), align=True)
labeled_row = paths_layout.split(**compatibility.factor(0.25), align=True)
labeled_row.label(text='Effective Output Path:')
labeled_row.label(text=str(render_output))
@@ -1072,7 +1105,7 @@ class FLAMENCO_PT_render(bpy.types.Panel, FlamencoPollMixin):
flamenco_status = context.window_manager.flamenco_status
if flamenco_status in {'IDLE', 'ABORTED', 'DONE'}:
if prefs.flamenco_show_quit_after_submit_button:
ui = layout.split(**blender.factor(0.75), align=True)
ui = layout.split(**compatibility.factor(0.75), align=True)
else:
ui = layout
ui.operator(FLAMENCO_OT_render.bl_idname,

View File

@@ -25,7 +25,7 @@ import bpy
import pillarsdk
from pillarsdk import exceptions as sdk_exceptions
from .pillar import pillar_call
from . import async_loop, pillar, home_project, blender
from . import async_loop, compatibility, pillar, home_project, blender
REQUIRES_ROLES_FOR_IMAGE_SHARING = {'subscriber', 'demo'}
IMAGE_SHARING_GROUP_NODE_NAME = 'Image sharing'
@@ -53,6 +53,7 @@ async def find_image_sharing_group_id(home_project_id, user_id):
return share_group['_id']
@compatibility.convert_properties
class PILLAR_OT_image_share(pillar.PillarOperatorMixin,
async_loop.AsyncModalOperatorMixin,
bpy.types.Operator):

View File

@@ -35,7 +35,7 @@ import asyncio
import pillarsdk
from pillarsdk import exceptions as sdk_exceptions
from .pillar import pillar_call
from . import async_loop, blender, pillar, cache, blendfile, home_project
from . import async_loop, blender, compatibility, pillar, cache, blendfile, home_project
SETTINGS_FILES_TO_UPLOAD = ['userpref.blend', 'startup.blend']
@@ -196,6 +196,7 @@ async def available_blender_versions(home_project_id: str, user_id: str) -> list
# noinspection PyAttributeOutsideInit
@compatibility.convert_properties
class PILLAR_OT_sync(pillar.PillarOperatorMixin,
async_loop.AsyncModalOperatorMixin,
bpy.types.Operator):

View File

@@ -26,7 +26,7 @@ import bpy
import bgl
import pillarsdk
from .. import async_loop, pillar, cache, blender, utils
from .. import async_loop, compatibility, pillar, cache, blender, utils
from . import menu_item as menu_item_mod # so that we can have menu items called 'menu_item'
from . import nodes
@@ -648,6 +648,7 @@ class BlenderCloudBrowser(pillar.PillarOperatorMixin,
self.scroll_offset_target = self.scroll_offset = 0
@compatibility.convert_properties
class PILLAR_OT_switch_hdri(pillar.PillarOperatorMixin,
async_loop.AsyncModalOperatorMixin,
bpy.types.Operator):
@@ -776,7 +777,7 @@ def _hdri_download_panel(self, current_image):
current_image.name)
return
row = self.layout.row(align=True).split(**blender.factor(0.3))
row = self.layout.row(align=True).split(**compatibility.factor(0.3))
row.label(text='HDRi', icon_value=blender.icon('CLOUD'))
row.prop(current_image, 'hdri_variation', text='')

View File

@@ -94,7 +94,10 @@ def pyside_cache(propname):
result = wrapped(self, context)
return result
finally:
rna_type, rna_info = getattr(self.bl_rna, propname)
try:
rna_type, rna_info = self.bl_rna.__annotations__[propname]
except AttributeError:
rna_type, rna_info = getattr(self.bl_rna, propname)
rna_info['_cached_result'] = result
return wrapper
return decorator

View File

@@ -3,7 +3,7 @@
lockfile==0.12.2
pillarsdk==1.8.0
wheel==0.29.0
blender-asset-tracer==1.1.1
blender-asset-tracer==1.2.1
# Secondary requirements:
asn1crypto==0.24.0

View File

@@ -236,7 +236,7 @@ setup(
'wheels': BuildWheels},
name='blender_cloud',
description='The Blender Cloud addon allows browsing the Blender Cloud from Blender.',
version='1.13.4',
version='1.15',
author='Sybren A. Stüvel',
author_email='sybren@stuvel.eu',
packages=find_packages('.'),

View File

@@ -19,4 +19,4 @@ echo git commit -m \'Bumped version to $VERSION\' setup.py blender_cloud/__init_
echo git tag -a version-$VERSION -m \'Tagged version $VERSION\'
echo
echo "To build a distribution ZIP:"
echo python setup.py bdist
echo python3 setup.py bdist