From a22ae98ee25f4cd3ed4528efc3d8838122af9783 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 15 Apr 2020 15:26:35 +0200 Subject: [PATCH] feat(ppro): wip publishing from premiere --- .../publish/collect_context.py | 73 ++++---- .../publish/collect_instances.py | 66 ++++---- .../publish/extract_post_json.py | 0 pype/premiere/lib.py | 2 - pype/premiere/ppro/js/pype.js | 34 +++- pype/premiere/ppro/js/pype_restapi_client.js | 156 ++++++++++-------- pype/services/adobe_communicator/__init__.py | 6 + .../adobe_communicator/adobe_comunicator.py | 16 ++ .../adobe_communicator/lib/__init__.py | 1 + pype/services/adobe_communicator/lib/lib.py | 39 +++-- .../adobe_communicator/lib/publishing.py | 146 ++++++++++++++++ 11 files changed, 363 insertions(+), 176 deletions(-) rename pype/plugins/{aport => adobecommunicator}/publish/collect_context.py (52%) rename pype/plugins/{aport => adobecommunicator}/publish/collect_instances.py (82%) rename pype/plugins/{aport => adobecommunicator}/publish/extract_post_json.py (100%) create mode 100644 pype/services/adobe_communicator/lib/publishing.py diff --git a/pype/plugins/aport/publish/collect_context.py b/pype/plugins/adobecommunicator/publish/collect_context.py similarity index 52% rename from pype/plugins/aport/publish/collect_context.py rename to pype/plugins/adobecommunicator/publish/collect_context.py index 35811d6378..b21c6708bd 100644 --- a/pype/plugins/aport/publish/collect_context.py +++ b/pype/plugins/adobecommunicator/publish/collect_context.py @@ -1,10 +1,13 @@ import os import pyblish.api -from avalon import api as avalon +from avalon import ( + io, + api as avalon +) from pype import api as pype import json from pathlib import Path - +from pprint import pformat class CollectContextDataFromAport(pyblish.api.ContextPlugin): """ @@ -18,32 +21,36 @@ class CollectContextDataFromAport(pyblish.api.ContextPlugin): """ - label = "Collect Aport Context" + label = "AdobeCommunicator Collect Context" order = pyblish.api.CollectorOrder - 0.49 def process(self, context): - + io.install() # get json paths from data - rqst_json_data_path = Path(context.data['rqst_json_data_path']) - post_json_data_path = Path(context.data['post_json_data_path']) + input_json_path = os.environ.get("AC_PUBLISH_INPATH") + output_json_path = os.environ.get("AC_PUBLISH_OUTPATH") + + rqst_json_data_path = Path(input_json_path) + post_json_data_path = Path(output_json_path) # get avalon session data and convert \ to / - session = avalon.session - self.log.info(os.environ['AVALON_PROJECTS']) - projects = Path(session['AVALON_PROJECTS']).resolve() - wd = Path(session['AVALON_WORKDIR']).resolve() - session['AVALON_PROJECTS'] = str(projects) - session['AVALON_WORKDIR'] = str(wd) + _S = avalon.session - context.data["avalonSession"] = session - self.log.debug("avalonSession: {}".format(session)) + projects = Path(_S["AVALON_PROJECTS"]).resolve() + asset = _S["AVALON_ASSET"] + workdir = Path(_S["AVALON_WORKDIR"]).resolve() + _S["AVALON_PROJECTS"] = str(projects) + _S["AVALON_WORKDIR"] = str(workdir) + + context.data["avalonSession"] = _S + self.log.info(f"__ avalonSession: `{_S}`") # get stagin directory from recieved path to json - context.data["stagingDir"] = staging_dir = post_json_data_path.parent + context.data["stagingDir"] = post_json_data_path.parent # get data from json file recieved with rqst_json_data_path.open(mode='r') as f: - context.data['jsonData'] = json_data = json.load(f) + context.data["jsonData"] = json_data = json.load(f) assert json_data, "No `data` in json file" # get and check host type @@ -51,50 +58,32 @@ class CollectContextDataFromAport(pyblish.api.ContextPlugin): host_version = json_data.get("hostVersion", None) assert host, "No `host` data in json file" assert host_version, "No `hostVersion` data in json file" - context.data["host"] = session["AVALON_APP"] = host + context.data["host"] = _S["AVALON_APP"] = host context.data["hostVersion"] = \ - session["AVALON_APP_VERSION"] = host_version + _S["AVALON_APP_VERSION"] = host_version # register pyblish for filtering of hosts in plugins pyblish.api.deregister_all_hosts() pyblish.api.register_host(host) - # get path to studio templates - templates_dir = os.getenv("PYPE_STUDIO_TEMPLATES", None) - assert templates_dir, "Missing `PYPE_STUDIO_TEMPLATES` in os.environ" - - # get presets for host - presets_dir = os.path.join(templates_dir, "presets", host) - assert os.path.exists( - presets_dir), "Required path `{}` doesn't exist".format(presets_dir) - - # load all available preset json files - preset_data = dict() - for file in os.listdir(presets_dir): - name, ext = os.path.splitext(file) - with open(os.path.join(presets_dir, file)) as prst: - preset_data[name] = json.load(prst) - - context.data['presets'] = preset_data - assert preset_data, "No `presets` data in json file" - self.log.debug("preset_data: {}".format(preset_data)) - # get current file current_file = json_data.get("currentFile", None) assert current_file, "No `currentFile` data in json file" context.data["currentFile"] = Path(current_file).resolve() # get project data from avalon - project_data = pype.get_project_data() + project_data = io.find_one({'type': 'project'}) assert project_data, "No `project_data` data in avalon db" context.data["projectData"] = project_data self.log.debug("project_data: {}".format(project_data)) # get asset data from avalon and fix all paths - asset_data = pype.get_asset_data() + asset_data = io.find_one({ + "type": 'asset', + "name": asset + })["data"] assert asset_data, "No `asset_data` data in avalon db" - asset_data = {k: v.replace("\\", "/") for k, v in asset_data.items() - if isinstance(v, str)} + context.data["assetData"] = asset_data self.log.debug("asset_data: {}".format(asset_data)) diff --git a/pype/plugins/aport/publish/collect_instances.py b/pype/plugins/adobecommunicator/publish/collect_instances.py similarity index 82% rename from pype/plugins/aport/publish/collect_instances.py rename to pype/plugins/adobecommunicator/publish/collect_instances.py index 0af2215dca..13358ca618 100644 --- a/pype/plugins/aport/publish/collect_instances.py +++ b/pype/plugins/adobecommunicator/publish/collect_instances.py @@ -1,12 +1,5 @@ import os -import json import pyblish.api -from avalon import ( - io, - api as avalon -) - -from pype import api as pype class CollectInstancesFromJson(pyblish.api.ContextPlugin): @@ -26,7 +19,11 @@ class CollectInstancesFromJson(pyblish.api.ContextPlugin): def process(self, context): - a_session = context.data.get("avalonSession") + _S = context.data["avalonSession"] + asset = _S["AVALON_ASSET"] + task = _S["AVALON_TASK"] + host = _S["AVALON_APP"] + json_data = context.data.get("jsonData", None) assert json_data, "No `json_data` data in json file" @@ -36,56 +33,49 @@ class CollectInstancesFromJson(pyblish.api.ContextPlugin): staging_dir = json_data.get("stagingDir", None) assert staging_dir, "No `stagingDir` path in json file" - presets = context.data["presets"] + host = context.data["host"] + presets = context.data["presets"][host] + rules_tasks = presets["rules_tasks"] ftrack_types = rules_tasks["ftrackTypes"] - assert ftrack_types, "No `ftrack_types` data in `/templates/presets/[host]/rules_tasks.json` file" + assert ftrack_types, ("No `ftrack_types` data in" + "`/presets/[host]/rules_tasks.json` file") context.data["ftrackTypes"] = ftrack_types asset_default = presets["asset_default"] - assert asset_default, "No `asset_default` data in `/templates/presets/[host]/asset_default.json` file" - - asset_name = a_session["AVALON_ASSET"] - entity = io.find_one({"name": asset_name, - "type": "asset"}) + assert asset_default, ("No `asset_default` data in" + "`/presets/[host]/asset_default.json` file") # get frame start > first try from asset data - frame_start = context.data["assetData"].get("fstart", None) + frame_start = context.data["assetData"].get("frameStart", None) if not frame_start: - self.log.debug("frame_start not on assetData") - # get frame start > second try from parent data - frame_start = pype.get_data_hierarchical_attr(entity, "fstart") - if not frame_start: - self.log.debug("frame_start not on any parent entity") - # get frame start > third try from parent data - frame_start = asset_default["fstart"] + self.log.debug("frame_start not on any parent entity") + # get frame start > third try from parent data + frame_start = asset_default["frameStart"] assert frame_start, "No `frame_start` data found, " "please set `fstart` on asset" self.log.debug("frame_start: `{}`".format(frame_start)) # get handles > first try from asset data - handles = context.data["assetData"].get("handles", None) - if not handles: + handle_start = context.data["assetData"].get("handleStart", None) + handle_end = context.data["assetData"].get("handleEnd", None) + if not all([handle_start, handle_end]): # get frame start > second try from parent data - handles = pype.get_data_hierarchical_attr(entity, "handles") - if not handles: - # get frame start > third try from parent data - handles = asset_default["handles"] + handle_start = asset_default["handleStart"] + handle_end = asset_default["handleEnd"] - assert handles, "No `handles` data found, " - "please set `fstart` on asset" - self.log.debug("handles: `{}`".format(handles)) + assert all([ + handle_start, + handle_end]), ("No `handle_start, handle_end` data found") instances = [] - task = a_session["AVALON_TASK"] current_file = os.path.basename(context.data.get("currentFile")) name, ext = os.path.splitext(current_file) # get current file host - host = a_session["AVALON_APP"] family = "projectfile" families = "filesave" subset_name = "{0}{1}".format(task, 'Default') @@ -109,7 +99,7 @@ class CollectInstancesFromJson(pyblish.api.ContextPlugin): "task": task, "representation": ext[1:], "host": host, - "asset": asset_name, + "asset": asset, "label": label, "name": name, # "hierarchy": hierarchy, @@ -152,6 +142,7 @@ class CollectInstancesFromJson(pyblish.api.ContextPlugin): tasks = [t["task"] for t in tags if t.get("task")] else: + self.log.debug("defaultTasks: `{}`".format(rules_tasks["defaultTasks"])) tasks = rules_tasks["defaultTasks"] self.log.debug("tasks: `{}`".format(tasks)) @@ -197,7 +188,7 @@ class CollectInstancesFromJson(pyblish.api.ContextPlugin): family = subset subset_name = "{0}{1}".format(subset, "Main") elif "reference" in subset: - family ="render" + family = "render" subset_name = "{0}{1}".format(family, "Reference") else: subset_name = "{0}{1}".format(subset, 'Default') @@ -218,7 +209,8 @@ class CollectInstancesFromJson(pyblish.api.ContextPlugin): "tasks": subset_dict[subset], "taskTypes": inst['tasksTypes'], "fstart": frame_start, - "handles": handles, + "handleStart": handle_start, + "handleEnd": handle_end, "host": host, "asset": asset, "hierarchy": hierarchy, diff --git a/pype/plugins/aport/publish/extract_post_json.py b/pype/plugins/adobecommunicator/publish/extract_post_json.py similarity index 100% rename from pype/plugins/aport/publish/extract_post_json.py rename to pype/plugins/adobecommunicator/publish/extract_post_json.py diff --git a/pype/premiere/lib.py b/pype/premiere/lib.py index c69681a198..08c712e028 100644 --- a/pype/premiere/lib.py +++ b/pype/premiere/lib.py @@ -42,8 +42,6 @@ if os.getenv("PUBLISH_PATH", None): ) else: os.environ["PUBLISH_PATH"] = self.PUBLISH_PATH -log.debug("PUBLISH_PATH: `{}`".format(os.environ["PUBLISH_PATH"])) - _clearing_cache = ["com.pype", "com.pype.rename"] diff --git a/pype/premiere/ppro/js/pype.js b/pype/premiere/ppro/js/pype.js index fe5fb2a6a7..ff05b37021 100644 --- a/pype/premiere/ppro/js/pype.js +++ b/pype/premiere/ppro/js/pype.js @@ -154,7 +154,7 @@ function convertPathString (path) { new RegExp('\\\\', 'g'), '/').replace(new RegExp('//\\?/', 'g'), ''); } -function publish () { +function _publish () { var $ = querySelector('#publish'); // var gui = $.querySelector('input[name=gui]').checked; var gui = true; @@ -213,16 +213,26 @@ function publish () { setTimeout(function () { if (fs.existsSync(path)) { displayResult('path were rendered: ' + path); - // register publish path - pras.register_plugin_path(publishPath).then(displayResult); // send json to pyblish - pras.publish(jsonSendPath, jsonGetPath, gui).then(function (result) { + var dataToPublish = { + "adobePublishJsonPathSend": jsonSendPath, + "adobePublishJsonPathGet": jsonGetPath, + "gui": gui, + "publishPath": publishPath, + "project": _pype.ENV.AVALON_PROJECT, + "asset": _pype.ENV.AVALON_ASSET, + "task": _pype.ENV.AVALON_TASK, + "workdir": _pype.ENV.AVALON_WORKDIR + } + pras.publish(dataToPublish).then(function (result) { + displayResult( + 'pype.js:publish < pras.publish: ' + JSON.stringify(result)); // check if resulted path exists as file - if (fs.existsSync(result.get_json_path)) { + if (fs.existsSync(result.return_data_path)) { // read json data from resulted path displayResult('Updating metadata of clips after publishing'); - jsonfile.readFile(result.get_json_path, function (json) { + jsonfile.readFile(result.return_data_path, function (json) { _pype.csi.evalScript('$.pype.dumpPublishedInstancesToMetadata(' + JSON.stringify(json) + ');'); }); @@ -336,7 +346,7 @@ $('#btn-deregister').click(function () { }); $('#btn-publish').click(function () { - publish(); + _publish(); }); $('#btn-send-reset').click(function () { @@ -396,7 +406,15 @@ $('#btn-newWorkfileVersion').click(function () { }); $('#btn-testing').click(function () { - pras.get_presets(_pype.ENV.AVALON_PROJECT); + var data = { + "adobePublishJsonPathSend": "C:/Users/jezsc/_PYPE_testing/testing_data/premiere/95478408-91ee-4522-81f6-f1689060664f_send.json", + "adobePublishJsonPathGet": "C:/Users/jezsc/_PYPE_testing/testing_data/premiere/95478408-91ee-4522-81f6-f1689060664f_get.json", + "gui": true, + "project": "J01_jakub_test", + "asset": "editorial", + "task": "conforming" + } + pras.publish(data); }); _pype.getEnv(); diff --git a/pype/premiere/ppro/js/pype_restapi_client.js b/pype/premiere/ppro/js/pype_restapi_client.js index 82bae9a941..ddcc50e391 100644 --- a/pype/premiere/ppro/js/pype_restapi_client.js +++ b/pype/premiere/ppro/js/pype_restapi_client.js @@ -11,33 +11,39 @@ var pras = { var url = _pype.ENV.PYPE_REST_API_URL; return url }, - getRequestFromRestApiServer: function (url, callback) { + getRequestFromRestApiServer: function (url, options, callback) { + _pype.displayResult('url: ' + url); + _pype.displayResult('options: ' + JSON.stringify(options)); + + // define options in case there is null comming + if (options === null) { + options = { + method: 'get', + headers: { + 'Content-Type': 'application/json' + } + } + } + _pype.displayResult('options: ' + JSON.stringify(options)); // send post request to rest api server - fetch(url) - .then(res => { - try { - return res.json(); - } catch(e) { - return res.text(); - } - }) - .then(json => { - if (isPypeData(json)) { - _pype.displayResult( - 'json: ' + JSON.stringify(json.data)); + fetch(url, options).then(res => { + try { + return res.json(); + } catch (e) { + return res.text(); + } + }).then(json => { + if (isPypeData(json)) { + _pype.displayResult('json: ' + JSON.stringify(json.data)); - // send it to callback function - callback(json.data); - } else { - _pype.displayError( - 'Data comming from `{url}` are not correct'.format({url: url})); - callback(null) - } - }) - .catch(err => _pype.displayError( - 'Data comming from `{url}` are not correct.\n\nError: {error}'.format({url: url, error: err})) - ); + // send it to callback function + callback(json.data); + } else { + _pype.displayError('Data comming from `{url}` are not correct'.format({url: url})); + callback(null) + } + }).catch(err => _pype.displayError('Data comming from `{url}` are not correct.\n\nError: {error}'.format({url: url, error: err}))); }, load_representations: function (projectName, requestList) { // preparation for getting representations from api server @@ -46,50 +52,55 @@ var pras = { }, get_presets: function (projectName, callback) { var template = '{serverUrl}/adobe/presets/{projectName}'; - var url = template.format({ - serverUrl: pras.getApiServerUrl(), - projectName: projectName, - }); + var url = template.format({serverUrl: pras.getApiServerUrl(), projectName: projectName}); _pype.displayResult(url); // send request to server - pras.getRequestFromRestApiServer(url, function (result) { + pras.getRequestFromRestApiServer(url, options = null, function (result) { if (result === null) { - _pype.displayError( - 'No data for `{projectName}` project in database'.format( - {projectName: projectName})); + _pype.displayError('No data for `{projectName}` project in database'.format({projectName: projectName})); return null } else { // send the data into jsx and write to module's global variable - _pype.csi.evalScript( - '$.pype.setProjectPreset(' + JSON.stringify(result) + ');', - function (resultBack) { - _pype.displayResult( - '$.pype.setProjectPreset(): ' + resultBack); - callback(resultBack); - // test the returning presets data from jsx if they are there - // _pype.csi.evalScript( - // '$.pype.getProjectPreset();', - // function (resultedPresets) { - // _pype.displayResult( - // '$.pype.getProjectPreset(): ' + resultedPresets); - // }); + _pype.csi.evalScript('$.pype.setProjectPreset(' + JSON.stringify(result) + ');', function (resultBack) { + _pype.displayResult('$.pype.setProjectPreset(): ' + resultBack); + callback(resultBack); + // test the returning presets data from jsx if they are there + // _pype.csi.evalScript( + // '$.pype.getProjectPreset();', + // function (resultedPresets) { + // _pype.displayResult( + // '$.pype.getProjectPreset(): ' + resultedPresets); + // }); }); } }); }, - register_plugin_path: function (publishPath) { - _pype.displayResult('_ publishPath: ' + publishPath); - // preparation for getting representations from api server - }, - deregister_plugin_path: function () { - // preparation for getting representations from api server - }, - publish: function (jsonSendPath, jsonGetPath, gui) { - // preparation for publishing with rest api server - _pype.displayResult('__ publish:jsonSendPath: ' + jsonSendPath); - _pype.displayResult('__ publish:jsonGetPath ' + jsonGetPath); - _pype.displayResult('__ publish:gui ' + gui); + publish: function (data) { + var template = '{serverUrl}/adobe/publish'; + var url = template.format({serverUrl: pras.getApiServerUrl()}); + var _options = { + method: 'POST', + body: JSON.stringify(data), + headers: { + 'Content-Type': 'application/json' + } + }; + _pype.displayResult('__ publish:url ' + url); + _pype.displayResult('__ publish:_options ' + JSON.stringify(_options)); + + // publish data with rest api server + pras.getRequestFromRestApiServer(url, _options, function (result) { + if (result === null) { + _pype.displayError('No data for `{projectName}` project in database'.format({projectName: projectName})); + return null + } else { + _pype.displayResult('pras.publish.getRequestFromRestApiServer: ' + JSON.stringify(result)); + return result + } + // preparation for publishing with rest api server + + }); }, context: function (project, asset, task, app) { // getting context of project @@ -97,24 +108,23 @@ var pras = { }; String.prototype.format = function (arguments) { - var this_string = ''; - for (var char_pos = 0; char_pos < this.length; char_pos++) { - this_string = this_string + this[char_pos]; - } + var this_string = ''; + for (var char_pos = 0; char_pos < this.length; char_pos++) { + this_string = this_string + this[char_pos]; + } - for (var key in arguments) { - var string_key = '{' + key + '}' - this_string = this_string.replace(new RegExp(string_key, 'g'), arguments[key]); - } - return this_string; + for (var key in arguments) { + var string_key = '{' + key + '}' + this_string = this_string.replace(new RegExp(string_key, 'g'), arguments[key]); + } + return this_string; }; function isPypeData(v) { - try{ - return Object.prototype.hasOwnProperty.call( - v, 'success'); - } catch(e){ - /*console.error("not a dict",e);*/ - return false; - } + try { + return Object.prototype.hasOwnProperty.call(v, 'success'); + } catch (e) { + /* console.error("not a dict",e); */ + return false; + } } diff --git a/pype/services/adobe_communicator/__init__.py b/pype/services/adobe_communicator/__init__.py index 4110ab69b5..2bda732be5 100644 --- a/pype/services/adobe_communicator/__init__.py +++ b/pype/services/adobe_communicator/__init__.py @@ -1,5 +1,11 @@ +from .lib import PUBLISH_PATHS + from .adobe_comunicator import AdobeCommunicator +__all__ = [ + "PUBLISH_PATHS" +] + def tray_init(tray_widget, main_widget): return AdobeCommunicator() diff --git a/pype/services/adobe_communicator/adobe_comunicator.py b/pype/services/adobe_communicator/adobe_comunicator.py index a2741270fc..73ce992aa1 100644 --- a/pype/services/adobe_communicator/adobe_comunicator.py +++ b/pype/services/adobe_communicator/adobe_comunicator.py @@ -1,5 +1,7 @@ import os +import pype from pypeapp import config, Logger +from . import PUBLISH_PATHS log = Logger().get_logger("AdobeCommunicator") @@ -18,6 +20,12 @@ class AdobeCommunicator: " Using defaults \"{}\"" ).format(str(self.presets))) + # solve publish paths + PUBLISH_PATHS.clear() + PUBLISH_PATHS.append(os.path.sep.join( + [pype.PLUGINS_DIR, "adobecommunicator", "publish"] + )) + def tray_start(self): return @@ -26,6 +34,14 @@ class AdobeCommunicator: if rest_api_module: self.rest_api_registration(rest_api_module) + # adding ftrack publish path + if "FtrackModule" in modules: + PUBLISH_PATHS.append(os.path.sep.join( + [pype.PLUGINS_DIR, "ftrack", "publish"] + )) + log.info((f"Adobe Communicator Registered PUBLISH_PATHS" + f"> `{PUBLISH_PATHS}`")) + def rest_api_registration(self, module): for prefix, static_path in self.presets["statics"].items(): static_path = static_path.format( diff --git a/pype/services/adobe_communicator/lib/__init__.py b/pype/services/adobe_communicator/lib/__init__.py index 76ed63c3fc..a56e25ac1f 100644 --- a/pype/services/adobe_communicator/lib/__init__.py +++ b/pype/services/adobe_communicator/lib/__init__.py @@ -1 +1,2 @@ +from .publishing import PUBLISH_PATHS from .lib import AdobeRestApi diff --git a/pype/services/adobe_communicator/lib/lib.py b/pype/services/adobe_communicator/lib/lib.py index b5c8cedb9a..665346fe60 100644 --- a/pype/services/adobe_communicator/lib/lib.py +++ b/pype/services/adobe_communicator/lib/lib.py @@ -5,7 +5,12 @@ import bson import bson.json_util from pype.services.rest_api import RestApi, abort, CallbackResult from .io_nonsingleton import DbConnector -from pypeapp import config +from .publishing import run_publish, set_context + +from pypeapp import config, Logger + + +log = Logger().get_logger("AdobeCommunicator") class AdobeRestApi(RestApi): @@ -15,7 +20,9 @@ class AdobeRestApi(RestApi): super().__init__(*args, **kwargs) self.dbcon.install() - @RestApi.route("/projects/", url_prefix="/adobe", methods="GET") + @RestApi.route("/projects/", + url_prefix="/adobe", + methods="GET") def get_project(self, request): project_name = request.url_data["project_name"] if not project_name: @@ -54,7 +61,7 @@ class AdobeRestApi(RestApi): project = self.dbcon[project_name].find_one({"type": "project"}) presets = config.get_presets(project=project["name"]) - + if presets: return CallbackResult(data=self.result_to_json(presets)) @@ -93,22 +100,26 @@ class AdobeRestApi(RestApi): _asset, identificator, _project_name )) - @RestApi.route("/publish/", url_prefix="/adobe", methods="GET") + @RestApi.route("/publish", url_prefix="/adobe", methods="POST") def publish(self, request): """ - http://localhost:8021/premiere/publish/shot021?json_in=this/path/file_in.json&json_out=this/path/file_out.json + http://localhost:8021/adobe/publish """ - asset_name = request.url_data["asset_name"] - query = request.query data = request.request_data - output = { - "message": "Got your data. Thanks.", - "your_data": data, - "your_query": query, - "your_asset_is": asset_name - } - return CallbackResult(data=self.result_to_json(output)) + log.info('Pyblish is running') + try: + set_context( + self.dbcon, + data, + 'adobecommunicator' + ) + result = run_publish(data) + + if result: + return CallbackResult(data=self.result_to_json(result)) + finally: + log.info('Pyblish have stopped') def result_to_json(self, result): """ Converts result of MongoDB query to dict without $oid (ObjectId) diff --git a/pype/services/adobe_communicator/lib/publishing.py b/pype/services/adobe_communicator/lib/publishing.py new file mode 100644 index 0000000000..290559f817 --- /dev/null +++ b/pype/services/adobe_communicator/lib/publishing.py @@ -0,0 +1,146 @@ +import os +import sys +import json +import tempfile +import random +import string +from avalon import api +import pype +from pypeapp import execute +import pyblish.api +from pypeapp import Logger +from pprint import pformat +log = Logger().get_logger(__name__) + +PUBLISH_PATHS = [] + +self = sys.modules[__name__] +self.dbcon = False + + +def set_context(dbcon_in, data, app): + ''' Sets context for pyblish (must be done before pyblish is launched) + :param project: Name of `Project` where instance should be published + :type project: str + :param asset: Name of `Asset` where instance should be published + :type asset: str + ''' + self.dbcon = dbcon_in + S = self.dbcon.Session + project = data["project"] + os.environ["AVALON_PROJECT"] = project + S["AVALON_PROJECT"] = project + + asset = data["asset"] + os.environ["AVALON_ASSET"] = asset + + self.dbcon.install() + av_project = self.dbcon.find_one({'type': 'project'}) + av_asset = self.dbcon.find_one({ + "type": 'asset', + "name": asset + }) + parents = av_asset['data']['parents'] + log.debug(f"__ session: {av_asset}") + log.debug(f"__ session: {parents}") + hierarchy = '' + if parents and len(parents) > 0: + hierarchy = os.path.sep.join(parents) + self.dbcon.uninstall() + + os.environ["AVALON_TASK"] = data["task"] + os.environ["AVALON_WORKDIR"] = data["workdir"] + os.environ["AVALON_HIERARCHY"] = hierarchy + os.environ["AVALON_PROJECTCODE"] = av_project['data'].get('code', '') + os.environ["AVALON_APP"] = app + + self.dbcon.install() + S["current_dir"] = os.path.normpath(os.getcwd()) + log.debug(f"__ session: {S}") + + self.dbcon.uninstall() + + +def run_publish(data, gui=True): + # cli pyblish seems like better solution + return cli_publish(data, gui) + + +def cli_publish(data, gui=True): + self.dbcon.install() + S = self.dbcon.Session + # unregister previouse plugins + pyblish.api.deregister_all_plugins() + + # Registers Global pyblish plugins + pype.install() + + if data.get("publishPath"): + PUBLISH_PATHS.append(data.get("publishPath")) + + # Registers AdobeCommunicator pyblish plugins + for path in PUBLISH_PATHS: + pyblish.api.register_plugin_path(path) + + project_plugins_paths = os.environ.get("PYPE_PROJECT_PLUGINS") + project_name = os.environ["AVALON_PROJECT"] + if project_plugins_paths and project_name: + for path in project_plugins_paths.split(os.pathsep): + if not path: + continue + plugin_path = os.path.join(path, project_name, "plugins") + if os.path.exists(plugin_path): + pyblish.api.register_plugin_path(plugin_path) + api.register_plugin_path(api.Loader, plugin_path) + api.register_plugin_path(api.Creator, plugin_path) + + if data.get("adobePublishJsonPathGet"): + return_data_path = data.get("adobePublishJsonPathGet") + else: + # Create hash name folder in temp + chars = "".join([random.choice(string.ascii_letters) + for i in range(15)]) + staging_dir = tempfile.mkdtemp(chars) + + # create json for return data + return_data_path = ( + staging_dir + os.path.basename(staging_dir) + 'return.json' + ) + + if data.get("adobePublishJsonPathSend"): + json_data_path = data.get("adobePublishJsonPathSend") + else: + # create also json and fill with data + json_data_path = staging_dir + os.path.basename(staging_dir) + '.json' + with open(json_data_path, 'w') as outfile: + json.dump(data, outfile) + + args = [ + "-pp", os.pathsep.join(pyblish.api.registered_paths()) + ] + + if gui: + args += ["gui"] + + envcopy = os.environ.copy() + envcopy["PYBLISH_HOSTS"] = "adobecommunicator" + envcopy["AC_PUBLISH_INPATH"] = json_data_path + envcopy["AC_PUBLISH_OUTPATH"] = return_data_path + envcopy["PYBLISH_GUI"] = "pyblish_lite" + + # print testing env + for k, v in envcopy.items(): + if ("AVALON" in k) or ("PYPE" in k): + log.debug(f"env: {k}: {v}") + + log.debug(f"__ session: {S}") + + while not execute( + [sys.executable, "-u", "-m", "pyblish"] + args, env=envcopy): + self.dbcon.uninstall() + + # check if data are returned back + if os.path.exists(return_data_path): + return {"return_data_path": return_data_path} + else: + return False