Exemple #1
0
    def _remove_layers(self, layer_names=None, layer_ids=None, layers=None):
        if not layer_names and not layer_ids:
            self.log.warning("Got empty layer names list.")
            return

        if layers is None:
            layers = lib.layers_data()

        available_ids = set(layer["layer_id"] for layer in layers)

        if layer_ids is None:
            # Backwards compatibility (layer ids were stored instead of names)
            layer_names_are_ids = True
            for layer_name in layer_names:
                if (not isinstance(layer_name, int)
                        and not layer_name.isnumeric()):
                    layer_names_are_ids = False
                    break

            if layer_names_are_ids:
                layer_ids = layer_names

        layer_ids_to_remove = []
        if layer_ids is not None:
            for layer_id in layer_ids:
                if layer_id in available_ids:
                    layer_ids_to_remove.append(layer_id)

        else:
            layers_by_name = collections.defaultdict(list)
            for layer in layers:
                layers_by_name[layer["name"]].append(layer)

            for layer_name in layer_names:
                layers = layers_by_name[layer_name]
                if len(layers) == 1:
                    layer_ids_to_remove.append(layers[0]["layer_id"])

        if not layer_ids_to_remove:
            self.log.warning("No layers to delete.")
            return

        george_script_lines = []
        for layer_id in layer_ids_to_remove:
            line = "tv_layerkill {}".format(layer_id)
            george_script_lines.append(line)
        george_script = "\n".join(george_script_lines)
        lib.execute_george_through_file(george_script)
Exemple #2
0
    def load(self, context, name, namespace, options):
        stretch = options.get("stretch", self.defaults["stretch"])
        timestretch = options.get("timestretch", self.defaults["timestretch"])
        preload = options.get("preload", self.defaults["preload"])

        load_options = []
        if stretch:
            load_options.append("\"STRETCH\"")
        if timestretch:
            load_options.append("\"TIMESTRETCH\"")
        if preload:
            load_options.append("\"PRELOAD\"")

        load_options_str = ""
        for load_option in load_options:
            load_options_str += (load_option + " ")

        # Prepare layer name
        asset_name = context["asset"]["name"]
        version_name = context["version"]["name"]
        layer_name = "{}_{}_v{:0>3}".format(
            asset_name,
            name,
            version_name
        )
        # Fill import script with filename and layer name
        # - filename mus not contain backwards slashes
        george_script = self.import_script.format(
            self.fname.replace("\\", "/"),
            layer_name,
            load_options_str
        )
        return lib.execute_george_through_file(george_script)
Exemple #3
0
    def load(self, context, name, namespace, options):
        stretch = options.get("stretch", self.defaults["stretch"])
        timestretch = options.get("timestretch", self.defaults["timestretch"])
        preload = options.get("preload", self.defaults["preload"])

        load_options = []
        if stretch:
            load_options.append("\"STRETCH\"")
        if timestretch:
            load_options.append("\"TIMESTRETCH\"")
        if preload:
            load_options.append("\"PRELOAD\"")

        load_options_str = ""
        for load_option in load_options:
            load_options_str += (load_option + " ")

        # Prepare layer name
        asset_name = context["asset"]["name"]
        subset_name = context["subset"]["name"]
        layer_name = self.get_unique_layer_name(asset_name, subset_name)

        # Fill import script with filename and layer name
        # - filename mus not contain backwards slashes
        george_script = self.import_script.format(
            self.fname.replace("\\", "/"), layer_name, load_options_str)

        lib.execute_george_through_file(george_script)

        loaded_layer = None
        layers = lib.layers_data()
        for layer in layers:
            if layer["name"] == layer_name:
                loaded_layer = layer
                break

        if loaded_layer is None:
            raise AssertionError(
                "Loading probably failed during execution of george script.")

        layer_names = [loaded_layer["name"]]
        namespace = namespace or layer_name
        return pipeline.containerise(name=name,
                                     namespace=namespace,
                                     members=layer_names,
                                     context=context,
                                     loader=self.__class__.__name__)
Exemple #4
0
    def render_review(self, filename_template, output_dir, frame_start,
                      frame_end):
        """ Export images from TVPaint using `tv_savesequence` command.

        Args:
            filename_template (str): Filename template of an output. Template
                should already contain extension. Template may contain only
                keyword argument `{frame}` or index argument (for same value).
                Extension in template must match `save_mode`.
            output_dir (str): Directory where files will be stored.
            first_frame (int): Starting frame from which export will begin.
            last_frame (int): On which frame export will end.

        Retruns:
            tuple: With 2 items first is list of filenames second is path to
                thumbnail.
        """
        self.log.debug("Preparing data for rendering.")
        first_frame_filepath = os.path.join(
            output_dir, filename_template.format(frame=frame_start))
        mark_in = frame_start - 1
        mark_out = frame_end - 1

        george_script_lines = [
            "tv_SaveMode \"PNG\"", "export_path = \"{}\"".format(
                first_frame_filepath.replace("\\", "/")),
            "tv_savesequence '\"'export_path'\"' {} {}".format(
                mark_in, mark_out)
        ]
        lib.execute_george_through_file("\n".join(george_script_lines))

        output = []
        first_frame_filepath = None
        for frame in range(frame_start, frame_end + 1):
            filename = filename_template.format(frame=frame)
            output.append(filename)
            if first_frame_filepath is None:
                first_frame_filepath = os.path.join(output_dir, filename)

        thumbnail_filepath = os.path.join(output_dir, "thumbnail.jpg")
        if first_frame_filepath and os.path.exists(first_frame_filepath):
            source_img = Image.open(first_frame_filepath)
            thumbnail_obj = Image.new("RGB", source_img.size, (255, 255, 255))
            thumbnail_obj.paste(source_img)
            thumbnail_obj.save(thumbnail_filepath)
        return output, thumbnail_filepath
Exemple #5
0
    def process(self):
        self.log.debug("Query data from workfile.")
        instances = pipeline.list_instances()
        layers_data = lib.layers_data()

        self.log.debug("Checking for selection groups.")
        # Collect group ids from selection
        group_ids = set()
        for layer in layers_data:
            if layer["selected"]:
                group_ids.add(layer["group_id"])

        # Raise if there is no selection
        if not group_ids:
            raise AssertionError("Nothing is selected.")

        # This creator should run only on one group
        if len(group_ids) > 1:
            raise AssertionError("More than one group is in selection.")

        group_id = tuple(group_ids)[0]
        # If group id is `0` it is `default` group which is invalid
        if group_id == 0:
            raise AssertionError(
                "Selection is not in group. Can't mark selection as Beauty."
            )

        self.log.debug(f"Selected group id is \"{group_id}\".")
        self.data["group_id"] = group_id

        family = self.data["family"]
        # Extract entered name
        name = self.data["subset"][len(family):]
        self.log.info(f"Extracted name from subset name \"{name}\".")
        self.data["name"] = name

        # Change subset name by template
        subset_name = self.subset_template.format(**{
            "family": self.family,
            "name": name
        })
        self.log.info(f"New subset name \"{subset_name}\".")
        self.data["subset"] = subset_name

        # Check for instances of same group
        existing_instance = None
        existing_instance_idx = None
        # Check if subset name is not already taken
        same_subset_instance = None
        same_subset_instance_idx = None
        for idx, instance in enumerate(instances):
            if instance["family"] == family:
                if instance["group_id"] == group_id:
                    existing_instance = instance
                    existing_instance_idx = idx
                elif instance["subset"] == subset_name:
                    same_subset_instance = instance
                    same_subset_instance_idx = idx

            if (
                same_subset_instance_idx is not None
                and existing_instance_idx is not None
            ):
                break

        if same_subset_instance_idx is not None:
            if self._ask_user_subset_override(same_subset_instance):
                instances.pop(same_subset_instance_idx)
            else:
                return

        if existing_instance is not None:
            self.log.info(
                f"Beauty instance for group id {group_id} already exists"
                ", overriding"
            )
            instances[existing_instance_idx] = self.data
        else:
            instances.append(self.data)

        self.write_instances(instances)

        if not self.rename_group:
            self.log.info("Group rename function is turned off. Skipping")
            return

        self.log.debug("Querying groups data from workfile.")
        groups_data = lib.groups_data()

        self.log.debug("Changing name of the group.")
        selected_group = None
        for group_data in groups_data:
            if group_data["group_id"] == group_id:
                selected_group = group_data

        # Rename TVPaint group (keep color same)
        # - groups can't contain spaces
        new_group_name = name.replace(" ", "_")
        rename_script = self.rename_script_template.format(
            clip_id=selected_group["clip_id"],
            group_id=selected_group["group_id"],
            r=selected_group["red"],
            g=selected_group["green"],
            b=selected_group["blue"],
            name=new_group_name
        )
        lib.execute_george_through_file(rename_script)

        self.log.info(
            f"Name of group with index {group_id}"
            f" was changed to \"{new_group_name}\"."
        )
Exemple #6
0
    def _render_layer(self, layer, tmp_filename_template, output_dir, behavior,
                      mark_in_index, mark_out_index):
        layer_id = layer["layer_id"]
        frame_start_index = layer["frame_start"]
        frame_end_index = layer["frame_end"]
        exposure_frames = lib.get_exposure_frames(layer_id, frame_start_index,
                                                  frame_end_index)

        if frame_start_index not in exposure_frames:
            exposure_frames.append(frame_start_index)

        layer_files_by_frame = {}
        george_script_lines = ["tv_SaveMode \"PNG\""]
        layer_position = layer["position"]

        for frame_idx in exposure_frames:
            filename = tmp_filename_template.format(pos=layer_position,
                                                    frame=frame_idx)
            dst_path = "/".join([output_dir, filename])
            layer_files_by_frame[frame_idx] = os.path.normpath(dst_path)

            # Go to frame
            george_script_lines.append("tv_layerImage {}".format(frame_idx))
            # Store image to output
            george_script_lines.append("tv_saveimage \"{}\"".format(dst_path))

        self.log.debug("Rendering Exposure frames {} of layer {} ({})".format(
            str(exposure_frames), layer_id, layer["name"]))
        # Let TVPaint render layer's image
        lib.execute_george_through_file("\n".join(george_script_lines))

        # Fill frames between `frame_start_index` and `frame_end_index`
        self.log.debug(
            ("Filling frames between first and last frame of layer ({} - {})."
             ).format(frame_start_index + 1, frame_end_index + 1))

        _debug_filled_frames = []
        prev_filepath = None
        for frame_idx in range(frame_start_index, frame_end_index + 1):
            if frame_idx in layer_files_by_frame:
                prev_filepath = layer_files_by_frame[frame_idx]
                continue

            if prev_filepath is None:
                raise ValueError("BUG: First frame of layer was not rendered!")
            _debug_filled_frames.append(frame_idx)
            filename = tmp_filename_template.format(pos=layer_position,
                                                    frame=frame_idx)
            new_filepath = "/".join([output_dir, filename])
            self._copy_image(prev_filepath, new_filepath)
            layer_files_by_frame[frame_idx] = new_filepath

        self.log.debug("Filled frames {}".format(str(_debug_filled_frames)))

        # Fill frames by pre/post behavior of layer
        pre_behavior = behavior["pre"]
        post_behavior = behavior["post"]
        self.log.debug(
            ("Completing image sequence of layer by pre/post behavior."
             " PRE: {} | POST: {}").format(pre_behavior, post_behavior))

        # Pre behavior
        self._fill_frame_by_pre_behavior(layer, pre_behavior, mark_in_index,
                                         layer_files_by_frame,
                                         tmp_filename_template, output_dir)
        self._fill_frame_by_post_behavior(layer, post_behavior, mark_out_index,
                                          layer_files_by_frame,
                                          tmp_filename_template, output_dir)
        return layer_files_by_frame
Exemple #7
0
    def update(self, container, representation):
        """Replace container with different version.

        New layers are loaded as first step. Then is tried to change data in
        new layers with data from old layers. When that is done old layers are
        removed.
        """
        # Create new containers first
        context = get_representation_context(representation)

        # Get layer ids from previous container
        old_layer_names = self.get_members_from_container(container)

        # Backwards compatibility (layer ids were stored instead of names)
        old_layers_are_ids = True
        for name in old_layer_names:
            if isinstance(name, int) or name.isnumeric():
                continue
            old_layers_are_ids = False
            break

        old_layers = []
        layers = lib.layers_data()
        previous_layer_ids = set(layer["layer_id"] for layer in layers)
        if old_layers_are_ids:
            for layer in layers:
                if layer["layer_id"] in old_layer_names:
                    old_layers.append(layer)
        else:
            layers_by_name = collections.defaultdict(list)
            for layer in layers:
                layers_by_name[layer["name"]].append(layer)

            for layer_name in old_layer_names:
                layers = layers_by_name[layer_name]
                if len(layers) == 1:
                    old_layers.append(layers[0])

        # Prepare few data
        new_start_position = None
        new_group_id = None
        layer_ids_to_remove = set()
        for layer in old_layers:
            layer_ids_to_remove.add(layer["layer_id"])
            position = layer["position"]
            group_id = layer["group_id"]
            if new_start_position is None:
                new_start_position = position
            elif new_start_position > position:
                new_start_position = position

            if new_group_id is None:
                new_group_id = group_id
            elif new_group_id < 0:
                continue
            elif new_group_id != group_id:
                new_group_id = -1

        # Remove old container
        self._remove_container(container)
        # Remove old layers
        self._remove_layers(layer_ids=layer_ids_to_remove)

        # Change `fname` to new representation
        self.fname = self.filepath_from_context(context)

        name = container["name"]
        namespace = container["namespace"]
        new_container = self.load(context, name, namespace, {})
        new_layer_names = self.get_members_from_container(new_container)

        layers = lib.layers_data()

        new_layers = []
        for layer in layers:
            if layer["layer_id"] in previous_layer_ids:
                continue
            if layer["name"] in new_layer_names:
                new_layers.append(layer)

        george_script_lines = []
        # Group new layers to same group as previous container layers had
        # - all old layers must be under same group
        if new_group_id is not None and new_group_id > 0:
            for layer in new_layers:
                line = "tv_layercolor \"set\" {} {}".format(
                    layer["layer_id"], new_group_id)
                george_script_lines.append(line)

        # Rename new layer to have same name
        # - only if both old and new have one layer
        if len(old_layers) == 1 and len(new_layers) == 1:
            layer_name = old_layers[0]["name"]
            george_script_lines.append("tv_layerrename {} \"{}\"".format(
                new_layers[0]["layer_id"], layer_name))

        # Change position of new layer
        # - this must be done before remove old layers
        if len(new_layers) == 1 and new_start_position is not None:
            new_layer = new_layers[0]
            george_script_lines.extend([
                "tv_layerset {}".format(new_layer["layer_id"]),
                "tv_layermove {}".format(new_start_position)
            ])

        # Execute george scripts if there are any
        if george_script_lines:
            george_script = "\n".join(george_script_lines)
            lib.execute_george_through_file(george_script)
Exemple #8
0
    def load(self, context, name, namespace, options):
        # Create temp file for output
        output_file = tempfile.NamedTemporaryFile(mode="w",
                                                  prefix="pype_tvp_",
                                                  suffix=".txt",
                                                  delete=False)
        output_file.close()
        output_filepath = output_file.name.replace("\\", "/")

        # Prepare george script
        import_script = "\n".join(self.import_script_lines)
        george_script = import_script.format(self.fname.replace("\\", "/"),
                                             output_filepath)
        self.log.info("*** George script:\n{}\n***".format(george_script))
        # Execute geoge script
        lib.execute_george_through_file(george_script)

        # Read output file
        lines = []
        with open(output_filepath, "r") as file_stream:
            for line in file_stream:
                line = line.rstrip()
                if line:
                    lines.append(line)

        # Clean up temp file
        os.remove(output_filepath)

        output = {}
        for line in lines:
            key, value = line.split("|")
            output[key] = value

        success = output.get("success")
        # Successfully loaded sound
        if success == "0":
            return

        if success == "":
            raise ValueError(
                "Your TVPaint version does not support loading of"
                " sound through George script. Please use manual load.")

        if success is None:
            raise ValueError("Unknown error happened during load."
                             " Please report and try to use manual load.")

        # Possible errors by TVPaint documentation
        # https://www.tvpaint.com/doc/tvpaint-animation-11/george-commands#tv_soundclipnew
        if success == "-1":
            raise ValueError(
                "BUG: George command did not get enough arguments.")

        if success == "-2":
            # Who know what does that mean?
            raise ValueError("No current clip without mixer.")

        if success == "-3":
            raise ValueError("TVPaint couldn't read the file.")

        if success == "-4":
            raise ValueError("TVPaint couldn't add the track.")

        raise ValueError("BUG: Unknown success value {}.".format(success))