From 0325e9d0bd4a595f6cba7a12dd09b938940c0902 Mon Sep 17 00:00:00 2001 From: Swann Date: Thu, 28 Jan 2021 23:59:19 +0100 Subject: [PATCH 1/2] feat: move sequencer to scene --- multi_user/bl_types/__init__.py | 2 +- multi_user/bl_types/bl_scene.py | 143 +++++++++++++++++-- multi_user/bl_types/bl_sequencer.py | 198 --------------------------- multi_user/bl_types/dump_anything.py | 4 +- 4 files changed, 139 insertions(+), 208 deletions(-) delete mode 100644 multi_user/bl_types/bl_sequencer.py diff --git a/multi_user/bl_types/__init__.py b/multi_user/bl_types/__init__.py index 11e41d5..96a5e38 100644 --- a/multi_user/bl_types/__init__.py +++ b/multi_user/bl_types/__init__.py @@ -39,7 +39,7 @@ __all__ = [ 'bl_font', 'bl_sound', 'bl_file', - 'bl_sequencer', + # 'bl_sequencer', 'bl_node_group', 'bl_texture', ] # Order here defines execution order diff --git a/multi_user/bl_types/bl_scene.py b/multi_user/bl_types/bl_scene.py index 95e72ab..2372ef0 100644 --- a/multi_user/bl_types/bl_scene.py +++ b/multi_user/bl_types/bl_scene.py @@ -17,18 +17,20 @@ import logging +from pathlib import Path import bpy import mathutils from deepdiff import DeepDiff from replication.constants import DIFF_JSON, MODIFIED +from ..utils import flush_history, current_milli_time from .bl_collection import (dump_collection_children, dump_collection_objects, load_collection_childrens, load_collection_objects, resolve_collection_dependencies) from .bl_datablock import BlDatablock +from .bl_file import get_filepath from .dump_anything import Dumper, Loader -from ..utils import flush_history RENDER_SETTINGS = [ 'dither_intensity', @@ -266,10 +268,105 @@ VIEW_SETTINGS = [ ] +def dump_sequence(sequence: bpy.types.Sequence) -> dict: + """ Dump a sequence to a dict + + :arg sequence: sequence to dump + :type sequence: bpy.types.Sequence + :return dict: + """ + dumper = Dumper() + dumper.exclude_filter = [ + 'lock', + 'select', + 'select_left_handle', + 'select_right_handle', + 'strobe' + ] + dumper.depth = 1 + data = dumper.dump(sequence) + # TODO: Support multiple images + if sequence.type == 'IMAGE': + data['filenames'] = [e.filename for e in sequence.elements] + # Effect strip inputs + input_count = getattr(sequence, 'input_count', None) + if input_count: + for n in range(input_count): + input_name = f"input_{n+1}" + data[input_name] = getattr(sequence, input_name).name + + return data + + +def load_sequence(sequence_data: dict, sequence_editor: bpy.types.SequenceEditor): + """ Load sequence from dumped data + + :arg sequence_data: sequence to dump + :type sequence_data:dict + :arg sequence_editor: root sequence editor + :type sequence_editor: bpy.types.SequenceEditor + """ + start = current_milli_time() + strip_type = sequence_data.get('type') + strip_name = sequence_data.get('name') + strip_channel = sequence_data.get('channel') + strip_frame_start = sequence_data.get('frame_start') + + sequence = sequence_editor.sequences_all.get(strip_name, None) + + if sequence is None: + if strip_type == 'SCENE': + strip_scene = bpy.data.scenes.get(sequence_data.get('scene')) + sequence = sequence_editor.sequences.new_scene(strip_name, + strip_scene, + strip_channel, + strip_frame_start) + elif strip_type == 'MOVIE': + filepath = get_filepath(Path(sequence_data['filepath']).name) + sequence = sequence_editor.sequences.new_movie(strip_name, + filepath, + strip_channel, + strip_frame_start) + elif strip_type == 'SOUND': + filepath = bpy.data.sounds[sequence_data['sound']].filepath + sequence = sequence_editor.sequences.new_sound(strip_name, + filepath, + strip_channel, + strip_frame_start) + elif strip_type == 'IMAGE': + images_name = sequence_data.get('filenames') + filepath = get_filepath(images_name[0]) + sequence = sequence_editor.sequences.new_image(strip_name, + filepath, + strip_channel, + strip_frame_start) + # load other images + if len(images_name)>1: + for img_idx in range(1,len(images_name)): + sequence.elements.append((images_name[img_idx])) + else: + seq = {} + + for i in range(sequence_data['input_count']): + seq[f"seq{i+1}"] = sequence_editor.sequences_all.get(sequence_data.get(f"input_{i+1}", None)) + + sequence = sequence_editor.sequences.new_effect(name=strip_name, + type=strip_type, + channel=strip_channel, + frame_start=strip_frame_start, + frame_end=sequence_data['frame_final_end'], + **seq) + + loader = Loader() + loader.exclure_filter = ['filepath', 'sound', 'filenames','fps'] + loader.load(sequence, sequence_data) + sequence.select = False + logging.info(f"loading {strip_name} took {current_milli_time()-start} ms") + class BlScene(BlDatablock): bl_id = "scenes" @@ -284,6 +381,7 @@ class BlScene(BlDatablock): def _construct(self, data): instance = bpy.data.scenes.new(data["name"]) instance.uuid = self.uuid + return instance def _load_implementation(self, data, target): @@ -325,6 +423,22 @@ class BlScene(BlDatablock): 'view_settings']['curve_mapping']['black_level'] target.view_settings.curve_mapping.update() + # Sequencer + sequences = data.get('sequences') + + if sequences: + # Create sequencer data + target.sequence_editor_create() + vse = target.sequence_editor + for seq in vse.sequences_all: + if seq.name not in sequences: + vse.sequences.remove(seq) + + for seq_name, seq_data in sequences.items(): + load_sequence(seq_data, vse) + elif target.sequence_editor and not sequences: + target.sequence_editor_clear() + # FIXME: Find a better way after the replication big refacotoring # Keep other user from deleting collection object by flushing their history flush_history() @@ -387,10 +501,14 @@ class BlScene(BlDatablock): data['view_settings']['curve_mapping']['curves'] = scene_dumper.dump( instance.view_settings.curve_mapping.curves) - if instance.sequence_editor: - data['has_sequence'] = True - else: - data['has_sequence'] = False + # Sequence + vse = instance.sequence_editor + if vse: + dumped_sequences = {} + for seq in vse.sequences_all: + dumped_sequences[seq.name] = dump_sequence(seq) + data['sequences'] = dumped_sequences + return data @@ -409,9 +527,18 @@ class BlScene(BlDatablock): deps.append(self.instance.grease_pencil) # Sequences - # deps.extend(list(self.instance.sequence_editor.sequences_all)) - if self.instance.sequence_editor: - deps.append(self.instance.sequence_editor) + vse = self.instance.sequence_editor + if vse: + for sequence in vse.sequences_all: + if sequence.type == 'MOVIE' and sequence.filepath: + deps.append(Path(bpy.path.abspath(sequence.filepath))) + elif sequence.type == 'SOUND' and sequence.sound: + deps.append(sequence.sound) + elif sequence.type == 'IMAGE': + for elem in sequence.elements: + sequence.append( + Path(bpy.path.abspath(sequence.directory), + elem.filename)) return deps diff --git a/multi_user/bl_types/bl_sequencer.py b/multi_user/bl_types/bl_sequencer.py deleted file mode 100644 index d961ddc..0000000 --- a/multi_user/bl_types/bl_sequencer.py +++ /dev/null @@ -1,198 +0,0 @@ -# ##### 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 -from pathlib import Path -import logging - -from .bl_file import get_filepath -from .dump_anything import Loader, Dumper -from .bl_datablock import BlDatablock, get_datablock_from_uuid - -def dump_sequence(sequence: bpy.types.Sequence) -> dict: - """ Dump a sequence to a dict - - :arg sequence: sequence to dump - :type sequence: bpy.types.Sequence - :return dict: - """ - dumper = Dumper() - dumper.exclude_filter = [ - 'lock', - 'select', - 'select_left_handle', - 'select_right_handle', - 'strobe' - ] - dumper.depth = 1 - data = dumper.dump(sequence) - - - # TODO: Support multiple images - if sequence.type == 'IMAGE': - data['filenames'] = [e.filename for e in sequence.elements] - - - # Effect strip inputs - input_count = getattr(sequence, 'input_count', None) - if input_count: - for n in range(input_count): - input_name = f"input_{n+1}" - data[input_name] = getattr(sequence, input_name).name - - return data - - -def load_sequence(sequence_data: dict, sequence_editor: bpy.types.SequenceEditor): - """ Load sequence from dumped data - - :arg sequence_data: sequence to dump - :type sequence_data:dict - :arg sequence_editor: root sequence editor - :type sequence_editor: bpy.types.SequenceEditor - """ - strip_type = sequence_data.get('type') - strip_name = sequence_data.get('name') - strip_channel = sequence_data.get('channel') - strip_frame_start = sequence_data.get('frame_start') - - sequence = sequence_editor.sequences_all.get(strip_name, None) - - if sequence is None: - if strip_type == 'SCENE': - strip_scene = bpy.data.scenes.get(sequence_data.get('scene')) - sequence = sequence_editor.sequences.new_scene(strip_name, - strip_scene, - strip_channel, - strip_frame_start) - elif strip_type == 'MOVIE': - filepath = get_filepath(Path(sequence_data['filepath']).name) - sequence = sequence_editor.sequences.new_movie(strip_name, - filepath, - strip_channel, - strip_frame_start) - elif strip_type == 'SOUND': - filepath = bpy.data.sounds[sequence_data['sound']].filepath - sequence = sequence_editor.sequences.new_sound(strip_name, - filepath, - strip_channel, - strip_frame_start) - elif strip_type == 'IMAGE': - images_name = sequence_data.get('filenames') - filepath = get_filepath(images_name[0]) - sequence = sequence_editor.sequences.new_image(strip_name, - filepath, - strip_channel, - strip_frame_start) - # load other images - if len(images_name)>1: - for img_idx in range(1,len(images_name)): - sequence.elements.append((images_name[img_idx])) - else: - seq = {} - - for i in range(sequence_data['input_count']): - seq[f"seq{i+1}"] = sequence_editor.sequences_all.get(sequence_data.get(f"input_{i+1}", None)) - - sequence = sequence_editor.sequences.new_effect(name=strip_name, - type=strip_type, - channel=strip_channel, - frame_start=strip_frame_start, - frame_end=sequence_data['frame_final_end'], - **seq) - - loader = Loader() - loader.load(sequence, sequence_data) - sequence.select = False - - -class BlSequencer(BlDatablock): - bl_id = "scenes" - bl_class = bpy.types.SequenceEditor - bl_delay_refresh = 1 - bl_delay_apply = 1 - bl_automatic_push = True - bl_check_common = True - bl_icon = 'SEQUENCE' - bl_reload_parent = False - - def _construct(self, data): - # Get the scene - scene_id = data.get('name') - scene = bpy.data.scenes.get(scene_id, None) - - # Create sequencer data - scene.sequence_editor_clear() - scene.sequence_editor_create() - - return scene.sequence_editor - - def resolve(self, construct = True): - scene = bpy.data.scenes.get(self.data['name'], None) - if scene: - if scene.sequence_editor is None and construct: - self.instance = self._construct(self.data) - else: - self.instance = scene.sequence_editor - else: - logging.warning("Sequencer editor scene not found") - - def _load_implementation(self, data, target): - loader = Loader() - # Sequencer - sequences = data.get('sequences') - if sequences: - for seq in target.sequences_all: - if seq.name not in sequences: - target.sequences.remove(seq) - for seq_name, seq_data in sequences.items(): - load_sequence(seq_data, target) - - def _dump_implementation(self, data, instance=None): - assert(instance) - sequence_dumper = Dumper() - sequence_dumper.depth = 1 - sequence_dumper.include_filter = [ - 'proxy_storage', - ] - data = {}#sequence_dumper.dump(instance) - # Sequencer - sequences = {} - - for seq in instance.sequences_all: - sequences[seq.name] = dump_sequence(seq) - - data['sequences'] = sequences - data['name'] = instance.id_data.name - - return data - - - def _resolve_deps_implementation(self): - deps = [] - - for seq in self.instance.sequences_all: - if seq.type == 'MOVIE' and seq.filepath: - deps.append(Path(bpy.path.abspath(seq.filepath))) - elif seq.type == 'SOUND' and seq.sound: - deps.append(seq.sound) - elif seq.type == 'IMAGE': - for e in seq.elements: - deps.append(Path(bpy.path.abspath(seq.directory), e.filename)) - return deps diff --git a/multi_user/bl_types/dump_anything.py b/multi_user/bl_types/dump_anything.py index 4d43180..30c2a13 100644 --- a/multi_user/bl_types/dump_anything.py +++ b/multi_user/bl_types/dump_anything.py @@ -465,6 +465,7 @@ class Loader: self.type_subset = self.match_subset_all self.occlude_read_only = False self.order = ['*'] + self.exclure_filter = [] def load(self, dst_data, src_dumped_data): self._load_any( @@ -475,7 +476,8 @@ class Loader: def _load_any(self, any, dump): for filter_function, load_function in self.type_subset: - if filter_function(any): + if filter_function(any) and \ + any.sub_element_name not in self.exclure_filter: load_function(any, dump) return From c855b5a42494eaa3ce53551b66e7470372fa0fd0 Mon Sep 17 00:00:00 2001 From: Swann Date: Fri, 29 Jan 2021 11:39:00 +0100 Subject: [PATCH 2/2] clean: removed logs --- multi_user/bl_types/bl_scene.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/multi_user/bl_types/bl_scene.py b/multi_user/bl_types/bl_scene.py index 2372ef0..3ad8774 100644 --- a/multi_user/bl_types/bl_scene.py +++ b/multi_user/bl_types/bl_scene.py @@ -24,7 +24,7 @@ import mathutils from deepdiff import DeepDiff from replication.constants import DIFF_JSON, MODIFIED -from ..utils import flush_history, current_milli_time +from ..utils import flush_history from .bl_collection import (dump_collection_children, dump_collection_objects, load_collection_childrens, load_collection_objects, resolve_collection_dependencies) @@ -310,7 +310,6 @@ def load_sequence(sequence_data: dict, sequence_editor: bpy.types.SequenceEditor :arg sequence_editor: root sequence editor :type sequence_editor: bpy.types.SequenceEditor """ - start = current_milli_time() strip_type = sequence_data.get('type') strip_name = sequence_data.get('name') strip_channel = sequence_data.get('channel') @@ -362,10 +361,10 @@ def load_sequence(sequence_data: dict, sequence_editor: bpy.types.SequenceEditor **seq) loader = Loader() + # TODO: Support filepath updates loader.exclure_filter = ['filepath', 'sound', 'filenames','fps'] loader.load(sequence, sequence_data) sequence.select = False - logging.info(f"loading {strip_name} took {current_milli_time()-start} ms") class BlScene(BlDatablock): @@ -430,12 +429,15 @@ class BlScene(BlDatablock): # Create sequencer data target.sequence_editor_create() vse = target.sequence_editor + + # Clear removed sequences for seq in vse.sequences_all: if seq.name not in sequences: vse.sequences.remove(seq) - + # Load existing sequences for seq_name, seq_data in sequences.items(): load_sequence(seq_data, vse) + # If the sequence is no longer used, clear it elif target.sequence_editor and not sequences: target.sequence_editor_clear()