From 11f29a551fb177b285da4384e59571f4ca1183e4 Mon Sep 17 00:00:00 2001 From: antirotor Date: Wed, 12 Jun 2019 16:11:57 +0200 Subject: [PATCH] feat(tools): added tool for copying textures to publish destination --- .../global/publish/collect_textures.py | 88 ++++++++++ pype/tools/texture_copy/app.py | 162 ++++++++++++++++++ 2 files changed, 250 insertions(+) create mode 100644 pype/plugins/global/publish/collect_textures.py create mode 100644 pype/tools/texture_copy/app.py diff --git a/pype/plugins/global/publish/collect_textures.py b/pype/plugins/global/publish/collect_textures.py new file mode 100644 index 0000000000..c38e911033 --- /dev/null +++ b/pype/plugins/global/publish/collect_textures.py @@ -0,0 +1,88 @@ +import os +import re +import copy +from avalon import io +from pprint import pprint + +import pyblish.api +from avalon import api + + +texture_extensions = ['.tif', '.tiff', '.jpg', '.jpeg', '.tx', '.png', '.tga', + '.psd', '.dpx', '.hdr', '.hdri', '.exr', '.sxr', '.psb'] + + +class CollectTextures(pyblish.api.ContextPlugin): + """ + Gather all texture files in working directory, traversing whole structure. + """ + + order = pyblish.api.CollectorOrder + targets = ["texture"] + label = "Textures" + hosts = ["shell"] + + def process(self, context): + + if os.environ.get("PYPE_PUBLISH_PATHS"): + paths = os.environ["PYPE_PUBLISH_PATHS"].split(os.pathsep) + else: + cwd = context.get("workspaceDir", os.getcwd()) + paths = [cwd] + + textures = [] + for path in paths: + for dir, subdir, files in os.walk(path): + textures.extend( + os.path.join(dir, x) for x in files + if os.path.splitext(x)[1].lower() in texture_extensions) + + self.log.info("Got {} texture files.".format(len(textures))) + if len(textures) < 1: + raise RuntimeError("no textures found.") + + asset_name = os.environ.get("AVALON_ASSET") + family = 'texture' + subset = 'Main' + + project = io.find_one({'type': 'project'}) + asset = io.find_one({ + 'type': 'asset', + 'name': asset_name + }) + + context.data['project'] = project + context.data['asset'] = asset + + for tex in textures: + self.log.info("Processing: {}".format(tex)) + name, ext = os.path.splitext(tex) + simple_name = os.path.splitext(os.path.basename(tex))[0] + instance = context.create_instance(simple_name) + + instance.data.update({ + "subset": subset, + "asset": asset_name, + "label": simple_name, + "name": simple_name, + "family": family, + "families": [family, 'ftrack'], + }) + instance.data['destination_list'] = list() + instance.data['representations'] = list() + instance.data['source'] = 'pype command' + + texture_data = {} + texture_data['anatomy_template'] = 'texture' + texture_data["ext"] = ext + texture_data["label"] = simple_name + texture_data["name"] = "texture" + texture_data["stagingDir"] = os.path.dirname(tex) + texture_data["files"] = os.path.basename(tex) + texture_data["thumbnail"] = False + texture_data["preview"] = False + + instance.data["representations"].append(texture_data) + self.log.info("collected instance: {}".format(instance.data)) + + self.log.info("All collected.") diff --git a/pype/tools/texture_copy/app.py b/pype/tools/texture_copy/app.py new file mode 100644 index 0000000000..8711e86818 --- /dev/null +++ b/pype/tools/texture_copy/app.py @@ -0,0 +1,162 @@ +import os +import re +import click +from avalon import io, api +from pprint import pprint + +from pypeapp.lib.Terminal import Terminal +from pypeapp import Anatomy + +import shutil +import speedcopy + + +t = Terminal() + +texture_extensions = ['.tif', '.tiff', '.jpg', '.jpeg', '.tx', '.png', '.tga', + '.psd', '.dpx', '.hdr', '.hdri', '.exr', '.sxr', '.psb'] + + +class TextureCopy: + + def __init__(self): + if not io.Session: + io.install() + + def _get_textures(self, path): + textures = [] + for dir, subdir, files in os.walk(path): + textures.extend( + os.path.join(dir, x) for x in files + if os.path.splitext(x)[1].lower() in texture_extensions) + return textures + + def _get_project(self, project_name): + project = io.find_one({ + 'type': 'project', + 'name': project_name + }) + return project + + def _get_asset(self, asset_name): + asset = io.find_one({ + 'type': 'asset', + 'name': asset_name + }) + return asset + + def _get_destination_path(self, asset, project): + root = api.registered_root() + PROJECT = api.Session["AVALON_PROJECT"] + hierarchy = "" + parents = asset['data']['parents'] + if parents and len(parents) > 0: + hierarchy = os.path.join(*parents) + + template_data = {"root": root, + "project": {"name": PROJECT, + "code": project['data']['code']}, + "silo": asset['silo'], + "asset": asset['name'], + "family": 'texture', + "subset": 'Main', + "hierarchy": hierarchy} + anatomy = Anatomy() + anatomy_filled = os.path.normpath( + anatomy.format(template_data)['texture']['path']) + return anatomy_filled + + def _get_version(self, path): + versions = [0] + dirs = [f.path for f in os.scandir(path) if f.is_dir()] + for d in dirs: + ver = re.search(r'^v(\d+)$', + os.path.basename(d), + flags=re.IGNORECASE) + if ver is not None: + versions.append(int(ver.group(1))) + + return max(versions) + 1 + + def _copy_textures(self, textures, destination): + for tex in textures: + dst = os.path.join(destination, + os.path.basename(tex)) + t.echo(" - Copy {} -> {}".format(tex, dst)) + try: + speedcopy.copyfile(tex, dst) + except Exception as e: + t.echo("!!! Copying failed") + t.echo("!!! {}".format(e)) + exit(1) + + def process(self, asset, project, path): + """ + Process all textures found in path and copy them to asset under + project. + """ + t.echo(">>> Looking for textures ...") + textures = self._get_textures(path) + if len(textures) < 1: + t.echo("!!! no textures found.") + exit(1) + else: + t.echo(">>> Found {} textures ...".format(len(textures))) + + project = self._get_project(project) + if not project: + t.echo("!!! Project name [ {} ] not found.".format(project)) + exit(1) + + asset = self._get_asset(asset) + if not project: + t.echo("!!! Asset [ {} ] not found in project".format(asset)) + exit(1) + t.echo((">>> Project [ {} ] and " + "asset [ {} ] seems to be OK ...").format(project['name'], + asset['name'])) + + dst_path = self._get_destination_path(asset, project) + t.echo("--- Using [ {} ] as destination path".format(dst_path)) + if not os.path.exists(dst_path): + try: + os.makedirs(dst_path) + except IOError as e: + t.echo("!!! Unable to create destination directory") + t.echo("!!! {}".format(e)) + exit(1) + version = '%02d' % self._get_version(dst_path) + t.echo("--- Using version [ {} ]".format(version)) + dst_path = os.path.join(dst_path, "v{}".format(version)) + t.echo("--- Final destination path [ {} ]".format(dst_path)) + try: + os.makedirs(dst_path) + except FileExistsError: + t.echo("!!! Somethings wrong, version directory already exists") + exit(1) + except IOError as e: + t.echo("!!! Cannot create version directory") + t.echo("!!! {}".format(e)) + exit(1) + + t.echo(">>> copying textures ...") + self._copy_textures(textures, dst_path) + t.echo(">>> done.") + t.echo("<<< terminating ...") + + +@click.command() +@click.option('--asset', required=True) +@click.option('--project', required=True) +@click.option('--path', required=True) +def texture_copy(asset, project, path): + t.echo("*** Running Texture tool ***") + t.echo(">>> Initializing avalon session ...") + os.environ["AVALON_PROJECT"] = project + os.environ["AVALON_ASSET"] = asset + os.environ["AVALON_SILO"] = "" + TextureCopy().process(asset, project, path) + + +if __name__ == '__main__': + texture_copy()