Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion client/ayon_sitesync/addon.py
Original file line number Diff line number Diff line change
Expand Up @@ -1259,8 +1259,17 @@ def _transform_sites_from_settings(self, settings):

for whole_site_info in settings.get("sites", []):
site_name = whole_site_info["name"]
provider = whole_site_info.get("provider")
if not provider or provider not in whole_site_info:
# site saved in Settings without provider selected would
# break settings preparation for all projects
self.log.warning(
"Site '{}' does not have valid provider configured"
" ('{}'), skipping it.".format(site_name, provider)
)
continue
provider_specific = copy.deepcopy(
whole_site_info[whole_site_info["provider"]]
whole_site_info[provider]
)
configured_site = {
"enabled": True,
Expand Down Expand Up @@ -1812,6 +1821,9 @@ def get_representations_sites_sync_state(
Returns:
list[dict]: dicts follow RepresentationSiteStateModel
"""
if not representation_ids:
return []

endpoint = "{}/{}/state/representations".format(
self.endpoint_prefix, project_name
)
Expand Down
11 changes: 10 additions & 1 deletion client/ayon_sitesync/providers/dropbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,16 @@ def get_roots_config(self, anatomy=None):
Format is importing for usage of python's format ** approach
"""
# TODO implement multiple roots
return {"root": {"work": self.presets['root']}}
# Projects may define several roots (work/publish/cache/...) but the
# provider preset holds a single root value - map any requested root
# name onto it so path templates using other roots still resolve.
class _SingleRoot(dict):
def __missing__(self, key):
return self["work"]

root_map = _SingleRoot()
root_map["work"] = self.presets['root']
return {"root": root_map}

def resolve_path(self, path, root_config=None, anatomy=None):
"""
Expand Down
40 changes: 28 additions & 12 deletions client/ayon_sitesync/providers/gdrive.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,16 @@ def get_roots_config(self, anatomy=None):
"""
# GDrive roots cannot be locally overridden
# TODO implement multiple roots
return {"root": {"work": self.presets["root"]}}
# Projects may define several roots (work/publish/cache/...) but the
# provider preset holds a single root value - map any requested root
# name onto it so path templates using other roots still resolve.
class _SingleRoot(dict):
def __missing__(self, key):
return self["work"]

root_map = _SingleRoot()
root_map["work"] = self.presets["root"]
return {"root": root_map}

def get_tree(self):
"""
Expand Down Expand Up @@ -508,8 +517,16 @@ def list_files(self):

def folder_path_exists(self, file_path):
"""
Checks if path from 'file_path' exists. If so, return its
Checks if the folder path 'file_path' exists. If so, return its
folder id.

'file_path' is expected to be a folder path (no file name). The
folder is resolved purely against the known folder tree instead of
guessing folder-vs-file from the extension. Folder names may
legitimately contain dots (e.g. 'girish.pv', 'mani.j',
'bhavana.darsi'), which the previous ``os.path.splitext`` based
check misread as files - that made the dotted folder unresolvable,
so it was never created and files landed in the parent folder.
Args:
file_path (string): gdrive path with / as a separator
Returns:
Expand All @@ -518,15 +535,11 @@ def folder_path_exists(self, file_path):
if not file_path:
return False

root, ext = os.path.splitext(file_path)
if not ext:
file_path += "/"

dir_path = os.path.dirname(file_path)
dir_path = file_path.rstrip("/")

path = self.get_tree().get(dir_path, None)
if path:
return path["id"]
folder = self.get_tree().get(dir_path, None)
if folder:
return folder["id"]

return False

Expand All @@ -539,9 +552,12 @@ def file_path_exists(self, file_path):
Returns:
(dictionary|boolean) file metadata | False if not found
"""
folder_id = self.folder_path_exists(file_path)
# 'folder_path_exists' now resolves only real folder paths, so strip
# the file name here and look the file up inside its parent folder.
clean_path = file_path.rstrip("/")
folder_id = self.folder_path_exists(os.path.dirname(clean_path))
if folder_id:
return self.file_exists(os.path.basename(file_path), folder_id)
return self.file_exists(os.path.basename(clean_path), folder_id)
return False

def file_exists(self, file_name, folder_id):
Expand Down
11 changes: 10 additions & 1 deletion client/ayon_sitesync/providers/rclone.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,16 @@ def get_tree(self):

def get_roots_config(self, anatomy=None) -> dict:
"""Returns root values for path resolving."""
return {"root": {"work": self.presets.get("root", "/")}}
# Projects may define several roots (work/publish/cache/...) but the
# provider preset holds a single root value - map any requested root
# name onto it so path templates using other roots still resolve.
class _SingleRoot(dict):
def __missing__(self, key):
return self["work"]

root_map = _SingleRoot()
root_map["work"] = self.presets.get("root", "/")
return {"root": root_map}

# Helper methods
def _manage_web_config(self):
Expand Down
11 changes: 10 additions & 1 deletion client/ayon_sitesync/providers/sftp.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,16 @@ def get_roots_config(self, anatomy=None):
Format is importing for usage of python's format ** approach
"""
# TODO implement multiple roots
return {"root": {"work": self.presets["root"]}}
# Projects may define several roots (work/publish/cache/...) but the
# provider preset holds a single root value - map any requested root
# name onto it so path templates using other roots still resolve.
class _SingleRoot(dict):
def __missing__(self, key):
return self["work"]

root_map = _SingleRoot()
root_map["work"] = self.presets["root"]
return {"root": root_map}

def get_tree(self):
"""
Expand Down
30 changes: 25 additions & 5 deletions client/ayon_sitesync/sitesync.py
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,18 @@ async def sync_loop(self):
project_name = None
enabled_projects = self.addon.get_enabled_projects()
for project_name in enabled_projects:
await self._sync_project(project_name)
try:
await self._sync_project(project_name)
except (ConnectionResetError, ResumableError):
raise
except Exception:
# one broken project must not stop syncing of the
# other projects, skip it until next loop
self.log.warning(
"Unhandled exception while syncing project "
"'{}', skipping it until next loop".format(
project_name),
exc_info=True)

duration = time.time() - start_time
self.log.debug("One loop took {:.2f}s".format(duration))
Expand All @@ -399,10 +410,12 @@ async def sync_loop(self):
"ResumableError in sync loop, trying next loop",
exc_info=True)
except Exception:
self.stop()
# do not kill the whole sync server on unexpected error,
# log it and retry on next loop after a delay
self.log.warning(
"Unhandled except. in sync loop, stopping server",
"Unhandled except. in sync loop, trying next loop",
exc_info=True)
await asyncio.sleep(60)

def stop(self):
"""Sets is_running flag to false, 'check_shutdown' shuts server down"""
Expand Down Expand Up @@ -459,8 +472,15 @@ def _working_sites(self, project_name, sync_config):
local_site, remote_site))
return None, None

local_site_config = sync_config.get("sites")[local_site]
remote_site_config = sync_config.get("sites")[remote_site]
site_configs = sync_config.get("sites") or {}
local_site_config = site_configs.get(local_site)
remote_site_config = site_configs.get(remote_site)
if local_site_config is None or remote_site_config is None:
self.log.warning(
"Active or remote site '{}'/'{}' is not configured for "
"project '{}', skipping".format(
local_site, remote_site, project_name))
return None, None
if not all([
_site_is_working(
self.addon, project_name, local_site, local_site_config
Expand Down
2 changes: 1 addition & 1 deletion client/ayon_sitesync/version.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# -*- coding: utf-8 -*-
"""Package declaring AYON addon 'sitesync' version."""
__version__ = "1.3.1+dev"
__version__ = "1.3.1+lbs.4"
4 changes: 2 additions & 2 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "sitesync-addon",
"private": true,
"version": "1.3.1+dev",
"version": "1.3.1+lbs.4",
"type": "module",
"scripts": {
"dev": "vite",
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/main.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const AddonWrapper = () => {
// const addonName = useContext(AddonContext).addonName
const addonName = 'sitesync'
// const addonVersion = useContext(AddonContext).addonVersion
const addonVersion = '1.3.1+dev'
const addonVersion = '1.3.1+lbs.4'
const accessToken = useContext(AddonContext).accessToken
const projectName = useContext(AddonContext).projectName
const userName = useContext(AddonContext).userName
Expand Down
2 changes: 1 addition & 1 deletion package.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"""Package declaring addon version."""
name = "sitesync"
title = "SiteSync"
version = "1.3.1+dev"
version = "1.3.1+lbs.4"
client_dir = "ayon_sitesync"

ayon_launcher_version = ">=1.4.3"
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "SiteSync"
version = "1.3.1+dev"
version = "1.3.1+lbs.4"
description = "SiteSync Addon"
authors = ["Ynput s.r.o. <info@ynput.io>"]
license = "MIT License"
Expand Down
5 changes: 4 additions & 1 deletion server/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ async def get_site_sync_state(
await check_sync_status_table(project_name)
conditions = []

if representationIds is not None:
if representationIds:
conditions.append(f"r.id IN {SQLTool.array(representationIds)}")

if folderFilter:
Expand Down Expand Up @@ -552,6 +552,9 @@ async def get_representations_site_sync_state(
"""List all sites on all representations and their state"""
await check_sync_status_table(project_name)

if not representationIds:
return []

conditions = [
f"representation_id IN {SQLTool.array(representationIds)}"
]
Expand Down
Loading