Compare commits

...

17 Commits

Author SHA1 Message Date
e777d67922 Bumped version to 1.4.4 to release some bug fixes.
This branch doesn't contain Flamenco or Attract, since the project
selection still needs work.
2017-02-21 10:51:23 +01:00
7edeff5ee1 Added script to bump versions in all the right places.
Must be called with major.minor.micro revision number (so 3 components).

Signed-off-by: Sybren A. Stüvel <sybren@stuvel.eu>
2017-02-21 10:49:02 +01:00
63b976cb44 Allow texture browser usage when the blend file is dirty.
Refuse to start if the file hasn't been saved. It's okay if
it's dirty, we just need to know where '//' points to.

Fixes T49203.
2016-09-06 12:30:48 +02:00
73a62da8da Fixed some issues with new db_user-returning credential check. 2016-08-30 16:57:21 +02:00
2c70ceb489 Solved issue T48992 2016-08-30 16:33:59 +02:00
38ccb54b50 Switch to new API URL. 2016-08-30 16:33:51 +02:00
1df113ca01 check_credentials() now returns the entire user, not just the ID. 2016-08-26 17:43:40 +02:00
887a9cc697 Allow async operators to automatically quit when they raise an exception.
Just set the class property `stop_upon_exception=True`.
2016-08-26 17:43:40 +02:00
143456ae1d Made AsyncModalOperatorMixin.invoke() start self.async_execute(context).
This was already common practice in all subclasses, and has now been
moved into the mixin.
2016-08-26 17:43:40 +02:00
f41ea8c5a3 Ignore __pycache__ dirs 2016-08-26 16:16:21 +02:00
7d90a92e24 Bumped version to 1.4.3 2016-08-23 14:41:33 +02:00
2388f800dc Fix T49080: Blender Cloud add-on error uploading screenshot
The screenshot filename contained colons, which isn't allowed on Windows.
2016-08-23 14:40:41 +02:00
38a3bcba71 Bumped version to 1.4.2, to re-distribute with B'ID addon 1.1.0 2016-08-04 14:39:00 +02:00
2cf400a74c Remove trailing slash from pillar_endpoint for BlenderID Addon 1.1.0
BlenderID Addon 1.1.0 uses endpoint URLs differently, so now directory-
like URLs have to end in a slash.
2016-08-04 12:46:42 +02:00
54ebb0bf5d Removed support: OFFICIAL, as that's reserved for Blender-bundled addons. 2016-08-04 11:21:59 +02:00
9e84d2a416 Only ignore blend files at the root dir 2016-07-29 11:05:51 +02:00
772e6b0b1b bundle.sh: warn when an addon can't be found. 2016-07-29 11:02:31 +02:00
11 changed files with 87 additions and 53 deletions

4
.gitignore vendored
View File

@@ -1,10 +1,10 @@
*.pyc
*.swp
*.blend
*.blend[1-9]
/*.blend*
blender_cloud/wheels/*.whl
/textures*/
/test_*.py
/dist/
/build/
/addon-bundle/*.zip
__pycache__

View File

@@ -22,6 +22,9 @@ cd $(dirname $(readlink -f $0))
BCLOUD=$(ls ../dist/blender_cloud-*.addon.zip | tail -n 1)
BID=$(ls ../../../blender-id-addon/dist/blender_id-*.addon.zip | tail -n 1)
[ -z "$BCLOUD" ] && echo "BCloud addon not found" >&2
[ -z "$BID" ] && echo "B'ID addon not found" >&2
cp -va $BCLOUD $BID .
BUNDLE=$(basename $BCLOUD)

View File

@@ -21,7 +21,7 @@
bl_info = {
'name': 'Blender Cloud',
'author': 'Sybren A. Stüvel and Francesco Siddi',
'version': (1, 4, 1),
'version': (1, 4, 4),
'blender': (2, 77, 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 '
@@ -29,7 +29,6 @@ bl_info = {
'wiki_url': 'http://wiki.blender.org/index.php/Extensions:2.6/Py/'
'Scripts/System/BlenderCloud',
'category': 'System',
'support': 'OFFICIAL'
}
import logging

View File

@@ -178,12 +178,28 @@ class AsyncModalOperatorMixin:
log = logging.getLogger('%s.AsyncModalOperatorMixin' % __name__)
_state = 'INITIALIZING'
stop_upon_exception = False
def invoke(self, context, event):
context.window_manager.modal_handler_add(self)
self.timer = context.window_manager.event_timer_add(1 / 15, context.window)
self.log.info('Starting')
self._new_async_task(self.async_execute(context))
return {'RUNNING_MODAL'}
async def async_execute(self, context):
"""Entry point of the asynchronous operator.
Implement in a subclass.
"""
return
def quit(self):
"""Signals the state machine to stop this operator from running."""
self._state = 'QUIT'
def execute(self, context):
return self.invoke(context, None)
@@ -195,6 +211,11 @@ class AsyncModalOperatorMixin:
if ex is not None:
self._state = 'EXCEPTION'
self.log.error('Exception while running task: %s', ex)
if self.stop_upon_exception:
self.quit()
self._finish(context)
return {'FINISHED'}
return {'RUNNING_MODAL'}
if self._state == 'QUIT':

View File

@@ -31,8 +31,8 @@ import rna_prop_ui
from . import pillar
PILLAR_SERVER_URL = 'https://cloudapi.blender.org/'
# PILLAR_SERVER_URL = 'http://localhost:5000/'
PILLAR_SERVER_URL = 'https://cloud.blender.org/api/'
# PILLAR_SERVER_URL = 'http://pillar:5001/api/'
ADDON_NAME = 'blender_cloud'
log = logging.getLogger(__name__)

View File

@@ -107,11 +107,7 @@ class PILLAR_OT_image_share(pillar.PillarOperatorMixin,
self.report({'ERROR'}, 'Datablock is dirty, save it first.')
return {'CANCELLED'}
async_loop.AsyncModalOperatorMixin.invoke(self, context, event)
self.log.info('Starting sharing')
self._new_async_task(self.async_execute(context))
return {'RUNNING_MODAL'}
return async_loop.AsyncModalOperatorMixin.invoke(self, context, event)
async def async_execute(self, context):
"""Entry point of the asynchronous operator."""
@@ -121,15 +117,15 @@ class PILLAR_OT_image_share(pillar.PillarOperatorMixin,
try:
# Refresh credentials
try:
self.user_id = await self.check_credentials(context,
REQUIRES_ROLES_FOR_IMAGE_SHARING)
db_user = await self.check_credentials(context, REQUIRES_ROLES_FOR_IMAGE_SHARING)
self.user_id = db_user['_id']
self.log.debug('Found user ID: %s', self.user_id)
except pillar.NotSubscribedToCloudError:
self.log.exception('User not subscribed to cloud.')
self.report({'ERROR'}, 'Please subscribe to the Blender Cloud.')
self._state = 'QUIT'
return
except pillar.CredentialsNotSyncedError:
except pillar.UserNotLoggedInError:
self.log.exception('Error checking/refreshing credentials.')
self.report({'ERROR'}, 'Please log in on Blender ID first.')
self._state = 'QUIT'
@@ -287,7 +283,7 @@ class PILLAR_OT_image_share(pillar.PillarOperatorMixin,
async def upload_screenshot(self, context) -> pillarsdk.Node:
"""Takes a screenshot, saves it to a temp file, and uploads it."""
self.name = datetime.datetime.now().strftime('Screenshot-%Y-%m-%d-%H:%M:%S.png')
self.name = datetime.datetime.now().strftime('Screenshot-%Y-%m-%d-%H%M%S.png')
self.report({'INFO'}, "Uploading %s '%s'" % (self.target.lower(), self.name))
with tempfile.TemporaryDirectory() as tmpdir:

View File

@@ -241,7 +241,7 @@ async def check_pillar_credentials(required_roles: set):
profile.save_json()
raise NotSubscribedToCloudError()
return pillar_user_id
return db_user
async def refresh_pillar_credentials(required_roles: set):
@@ -256,7 +256,7 @@ async def refresh_pillar_credentials(required_roles: set):
import blender_id
from . import blender
pillar_endpoint = blender.preferences().pillar_server.rstrip('/')
pillar_endpoint = blender.preferences().pillar_server
# Create a subclient token and send it to Pillar.
# May raise a blender_id.BlenderIdCommError
@@ -780,15 +780,16 @@ def is_cancelled(future: asyncio.Future) -> bool:
class PillarOperatorMixin:
async def check_credentials(self, context, required_roles) -> bool:
"""Checks credentials with Pillar, and if ok returns the user ID.
"""Checks credentials with Pillar, and if ok returns the user document from Pillar/MongoDB.
Returns None if the user cannot be found, or if the user is not a Cloud subscriber.
:raises UserNotLoggedInError: if the user is not logged in
:raises NotSubscribedToCloudError: if the user does not have any of the required roles
"""
# self.report({'INFO'}, 'Checking Blender Cloud credentials')
try:
user_id = await check_pillar_credentials(required_roles)
db_user = await check_pillar_credentials(required_roles)
except NotSubscribedToCloudError:
self._log_subscription_needed()
raise
@@ -796,20 +797,22 @@ class PillarOperatorMixin:
self.log.info('Credentials not synced, re-syncing automatically.')
else:
self.log.info('Credentials okay.')
return user_id
return db_user
try:
user_id = await refresh_pillar_credentials(required_roles)
db_user = await refresh_pillar_credentials(required_roles)
except NotSubscribedToCloudError:
self._log_subscription_needed()
raise
except CredentialsNotSyncedError:
self.log.info('Credentials not synced after refreshing, handling as not logged in.')
raise UserNotLoggedInError('Not logged in.')
except UserNotLoggedInError:
self.log.error('User not logged in on Blender ID.')
raise
else:
self.log.info('Credentials refreshed and ok.')
return user_id
return None
return db_user
def _log_subscription_needed(self):
self.log.warning(

View File

@@ -234,11 +234,7 @@ class PILLAR_OT_sync(pillar.PillarOperatorMixin,
self.bss_report({'ERROR'}, 'No Blender version to sync for was given.')
return {'CANCELLED'}
async_loop.AsyncModalOperatorMixin.invoke(self, context, event)
self.log.info('Starting synchronisation')
self._new_async_task(self.async_execute(context))
return {'RUNNING_MODAL'}
return async_loop.AsyncModalOperatorMixin.invoke(self, context, event)
def action_select(self, context):
"""Allows selection of the Blender version to use.
@@ -285,14 +281,15 @@ class PILLAR_OT_sync(pillar.PillarOperatorMixin,
try:
# Refresh credentials
try:
self.user_id = await self.check_credentials(context, REQUIRES_ROLES_FOR_SYNC)
db_user = await self.check_credentials(context, REQUIRES_ROLES_FOR_SYNC)
self.user_id = db_user['_id']
log.debug('Found user ID: %s', self.user_id)
except pillar.NotSubscribedToCloudError:
self.log.exception('User not subscribed to cloud.')
self.bss_report({'SUBSCRIBE'}, 'Please subscribe to the Blender Cloud.')
self._state = 'QUIT'
return
except pillar.CredentialsNotSyncedError:
except pillar.UserNotLoggedInError:
self.log.exception('Error checking/refreshing credentials.')
self.bss_report({'ERROR'}, 'Please log in on Blender ID first.')
self._state = 'QUIT'

View File

@@ -258,8 +258,9 @@ class BlenderCloudBrowser(pillar.PillarOperatorMixin,
scroll_offset_space_left = 0
def invoke(self, context, event):
# Refuse to start if the file hasn't been saved.
if context.blend_data.is_dirty:
# Refuse to start if the file hasn't been saved. It's okay if
# it's dirty, we just need to know where '//' points to.
if not os.path.exists(context.blend_data.filepath):
self.report({'ERROR'}, 'Please save your Blend file before using '
'the Blender Cloud addon.')
return {'CANCELLED'}
@@ -288,10 +289,7 @@ class BlenderCloudBrowser(pillar.PillarOperatorMixin,
self._scroll_reset()
context.window.cursor_modal_set('DEFAULT')
async_loop.AsyncModalOperatorMixin.invoke(self, context, event)
self._new_async_task(self.async_execute(context))
return {'RUNNING_MODAL'}
return async_loop.AsyncModalOperatorMixin.invoke(self, context, event)
def modal(self, context, event):
result = async_loop.AsyncModalOperatorMixin.modal(self, context, event)
@@ -366,13 +364,13 @@ class BlenderCloudBrowser(pillar.PillarOperatorMixin,
self.log.debug('Checking credentials')
try:
user_id = await self.check_credentials(context, REQUIRED_ROLES_FOR_TEXTURE_BROWSER)
db_user = await self.check_credentials(context, REQUIRED_ROLES_FOR_TEXTURE_BROWSER)
except pillar.NotSubscribedToCloudError:
self.log.info('User not subscribed to Blender Cloud.')
self._show_subscribe_screen()
return None
if user_id is None:
if db_user is None:
raise pillar.UserNotLoggedInError()
await self.async_download_previews()
@@ -850,13 +848,6 @@ class PILLAR_OT_switch_hdri(pillar.PillarOperatorMixin,
file_uuid = bpy.props.StringProperty(name='file_uuid',
description='File ID to download')
def invoke(self, context, event):
async_loop.AsyncModalOperatorMixin.invoke(self, context, event)
self.log.info('Starting')
self._new_async_task(self.async_execute(context))
return {'RUNNING_MODAL'}
async def async_execute(self, context):
"""Entry point of the asynchronous operator."""
@@ -864,19 +855,20 @@ class PILLAR_OT_switch_hdri(pillar.PillarOperatorMixin,
try:
try:
user_id = await self.check_credentials(context, REQUIRED_ROLES_FOR_TEXTURE_BROWSER)
db_user = await self.check_credentials(context, REQUIRED_ROLES_FOR_TEXTURE_BROWSER)
user_id = db_user['_id']
except pillar.NotSubscribedToCloudError:
self.log.exception('User not subscribed to cloud.')
self.report({'ERROR'}, 'Please subscribe to the Blender Cloud.')
self._state = 'QUIT'
return
except pillar.CredentialsNotSyncedError:
except pillar.UserNotLoggedInError:
self.log.exception('Error checking/refreshing credentials.')
self.report({'ERROR'}, 'Please log in on Blender ID first.')
self._state = 'QUIT'
return
if user_id is None:
if not user_id:
raise pillar.UserNotLoggedInError()
await self.download_and_replace(context)
@@ -977,6 +969,11 @@ def _hdri_download_panel(self, current_image):
props.file_uuid = current_image.hdri_variation
# Storage for variation labels, as the strings in EnumProperty items
# MUST be kept in Python memory.
variation_label_storage = {}
def hdri_variation_choices(self, context):
if context.area.type == 'IMAGE_EDITOR':
image = context.edit_image
@@ -988,8 +985,11 @@ def hdri_variation_choices(self, context):
if 'bcloud_node' not in image:
return []
choices = [(file_doc['file'], file_doc['resolution'], '')
for file_doc in image['bcloud_node']['properties']['files']]
choices = []
for file_doc in image['bcloud_node']['properties']['files']:
label = file_doc['resolution']
variation_label_storage[label] = label
choices.append((file_doc['file'], label, ''))
return choices

View File

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

15
update_version.sh Executable file
View File

@@ -0,0 +1,15 @@
#!/bin/bash
if [ -z "$1" ]; then
echo "Usage: $0 new-version" >&2
exit 1
fi
BL_INFO_VER=$(echo "$1" | sed 's/\./, /g')
sed "s/version='[^']*'/version='$1'/" -i setup.py
sed "s/'version': ([^)]*)/'version': ($BL_INFO_VER)/" -i blender_cloud/__init__.py
git diff
echo
echo "Don't forget to commit!"