diff --git a/multi_user/bl_types/bl_datablock.py b/multi_user/bl_types/bl_datablock.py
index 22ccb3a..27d3f69 100644
--- a/multi_user/bl_types/bl_datablock.py
+++ b/multi_user/bl_types/bl_datablock.py
@@ -113,7 +113,7 @@ class BlDatablock(ReplicatedDatablock):
if instance and hasattr(instance, 'uuid'):
instance.uuid = self.uuid
- # self.diff_method = DIFF_BINARY
+ self.diff_method = DIFF_BINARY
def resolve(self):
datablock_ref = None
diff --git a/multi_user/bl_types/bl_file_datablock.py b/multi_user/bl_types/bl_file_datablock.py
new file mode 100644
index 0000000..66e6244
--- /dev/null
+++ b/multi_user/bl_types/bl_file_datablock.py
@@ -0,0 +1,166 @@
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#
+# ##### END GPL LICENSE BLOCK #####
+
+
+import bpy
+import mathutils
+import logging
+import pathlib
+import os
+
+from .. import utils
+from .dump_anything import Loader, Dumper
+from replication.data import ReplicatedDatablock
+from replication.constants import (UP, DIFF_BINARY)
+
+
+class BlFileDatablock(ReplicatedDatablock):
+ """BlDatablock
+
+ bl_id : blender internal storage identifier
+ bl_class : blender internal type
+ bl_delay_refresh : refresh rate in second for observers
+ bl_delay_apply : refresh rate in sec for apply
+ bl_automatic_push : boolean
+ bl_icon : type icon (blender icon name)
+ bl_check_common: enable check even in common rights
+ """
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ instance = kwargs.get('instance', None)
+
+ self.preferences = utils.get_preferences()
+
+ if instance and hasattr(instance, 'uuid'):
+ instance.uuid = self.uuid
+
+ self.diff_method = DIFF_BINARY
+
+ def resolve(self):
+ datablock_ref = None
+ datablock_root = getattr(bpy.data, self.bl_id)
+ datablock_ref = utils.find_from_attr('uuid', self.uuid, datablock_root)
+
+ if not datablock_ref:
+ try:
+ datablock_ref = datablock_root[self.data['name']]
+ except Exception:
+ name = self.data.get('name')
+ logging.debug(f"Constructing {name}")
+ datablock_ref = self._construct(data=self.data)
+
+ if datablock_ref:
+ setattr(datablock_ref, 'uuid', self.uuid)
+
+ self.instance = datablock_ref
+
+ def remove_instance(self):
+ """
+ Remove instance from blender data
+ """
+ assert(self.instance)
+
+ datablock_root = getattr(bpy.data, self.bl_id)
+ datablock_root.remove(self.instance)
+
+ def get_filepath(self):
+ ext = pathlib.Path(self.data['filepath']).suffix
+ if ext:
+ name = f"{self.uuid}{ext}"
+ return os.path.join(self.preferences.cache_directory, name)
+ else:
+ return self.data['filepath']
+
+ def _construct(self, data):
+ filepath = self.get_filepath()
+
+ # Step 1: load content
+ if 'file' in data.keys():
+ self._write_content(data['file'], filepath)
+ else:
+ logging.warning("No data to write, skipping.")
+ # Step 2: construct the file
+ root = getattr(bpy.data, self.bl_id)
+
+ # Step 3: construct the datablock
+ return root.load(filepath)
+
+ def _dump(self, instance=None):
+ # Step 1: dump related metadata
+ data = self._dump_metadata(instance=instance)
+
+ # Step 2: dump file content
+ file_content = self._read_content(instance.filepath)
+
+ if file_content:
+ data['file'] = file_content
+
+ return data
+
+ def _load(self, target, data):
+ self._load_metadata(target, data)
+
+ def _dump_metadata(self, data, target):
+ """
+ Dump datablock metadata
+ """
+ raise NotImplementedError()
+
+ def _read_content(self, filepath):
+ """
+ Dump file content
+ """
+ logging.info("Reading file content")
+
+ content = None
+
+ try:
+ file = open(bpy.path.abspath(self.instance.filepath), 'rb')
+ content = file.read()
+ except IOError:
+ logging.warning(f"{filepath} doesn't exist, skipping")
+ else:
+ file.close()
+
+ return content
+
+ def _load_metadata(self, target, data):
+ raise NotImplementedError
+
+ def _write_content(self, content, filepath):
+ """
+ Write content on the disk
+ """
+ logging.info("Writing file content")
+
+ try:
+ file = open(filepath, 'wb')
+ file.write(content)
+ except IOError:
+ logging.warning(f"{self.uuid} writing error, skipping.")
+ else:
+ file.close()
+
+ def resolve_deps(self):
+ return []
+
+ def is_valid(self):
+ return getattr(bpy.data, self.bl_id).get(self.data['name'])
+
+ def diff(self):
+ return False
diff --git a/multi_user/bl_types/bl_font.py b/multi_user/bl_types/bl_font.py
index 599f3ad..0029d1e 100644
--- a/multi_user/bl_types/bl_font.py
+++ b/multi_user/bl_types/bl_font.py
@@ -23,9 +23,10 @@ import logging
import pathlib
from .. import utils
from .dump_anything import Loader, Dumper
-from .bl_datablock import BlDatablock
+from .bl_file_datablock import BlFileDatablock
-class BlFont(BlDatablock):
+
+class BlFont(BlFileDatablock):
bl_id = "fonts"
bl_class = bpy.types.VectorFont
bl_delay_refresh = 1
@@ -34,36 +35,15 @@ class BlFont(BlDatablock):
bl_check_common = False
bl_icon = 'FILE_FONT'
- def _construct(self, data):
- if data['filepath'] == '':
- return bpy.data.fonts.load(data['filepath'])
- elif 'font_file' in data.keys():
- prefs = utils.get_preferences()
- ext = pathlib.Path(data['filepath']).suffix
- font_name = f"{self.uuid}{ext}"
- font_path = os.path.join(prefs.cache_directory, font_name)
-
- os.makedirs(prefs.cache_directory, exist_ok=True)
- file = open(font_path, 'wb')
- file.write(data["font_file"])
- file.close()
-
- logging.info(f'loading {font_path}')
- return bpy.data.fonts.load(font_path)
-
- def _load(self, data, target):
+ def _load_metadata(self, data, target):
+ # No metadate for fonts
pass
- def _dump(self, instance=None):
- data = {
- 'filepath':instance.filepath,
- 'name':instance.name
+ def _dump_metadata(self, instance=None):
+ return {
+ 'filepath': instance.filepath,
+ 'name': instance.name
}
- if instance.filepath != '' and not instance.is_embedded_data:
- file = open(instance.filepath, "rb")
- data['font_file'] = file.read()
- file.close()
- return data
def diff(self):
return False