feat: basic file interface
This commit is contained in:
parent
8d2b9e5580
commit
63ba6d35d5
@ -113,7 +113,7 @@ class BlDatablock(ReplicatedDatablock):
|
|||||||
if instance and hasattr(instance, 'uuid'):
|
if instance and hasattr(instance, 'uuid'):
|
||||||
instance.uuid = self.uuid
|
instance.uuid = self.uuid
|
||||||
|
|
||||||
# self.diff_method = DIFF_BINARY
|
self.diff_method = DIFF_BINARY
|
||||||
|
|
||||||
def resolve(self):
|
def resolve(self):
|
||||||
datablock_ref = None
|
datablock_ref = None
|
||||||
|
166
multi_user/bl_types/bl_file_datablock.py
Normal file
166
multi_user/bl_types/bl_file_datablock.py
Normal file
@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
# ##### 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
|
@ -23,9 +23,10 @@ import logging
|
|||||||
import pathlib
|
import pathlib
|
||||||
from .. import utils
|
from .. import utils
|
||||||
from .dump_anything import Loader, Dumper
|
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_id = "fonts"
|
||||||
bl_class = bpy.types.VectorFont
|
bl_class = bpy.types.VectorFont
|
||||||
bl_delay_refresh = 1
|
bl_delay_refresh = 1
|
||||||
@ -34,36 +35,15 @@ class BlFont(BlDatablock):
|
|||||||
bl_check_common = False
|
bl_check_common = False
|
||||||
bl_icon = 'FILE_FONT'
|
bl_icon = 'FILE_FONT'
|
||||||
|
|
||||||
def _construct(self, data):
|
def _load_metadata(self, data, target):
|
||||||
if data['filepath'] == '<builtin>':
|
# No metadate for fonts
|
||||||
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):
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def _dump(self, instance=None):
|
def _dump_metadata(self, instance=None):
|
||||||
data = {
|
return {
|
||||||
'filepath':instance.filepath,
|
'filepath': instance.filepath,
|
||||||
'name':instance.name
|
'name': instance.name
|
||||||
}
|
}
|
||||||
if instance.filepath != '<builtin>' and not instance.is_embedded_data:
|
|
||||||
file = open(instance.filepath, "rb")
|
|
||||||
data['font_file'] = file.read()
|
|
||||||
file.close()
|
|
||||||
return data
|
|
||||||
|
|
||||||
def diff(self):
|
def diff(self):
|
||||||
return False
|
return False
|
||||||
|
Loading…
Reference in New Issue
Block a user