mirror of
https://github.com/ynput/ayon-core.git
synced 2026-01-01 08:24:53 +01:00
Merge branch 'release/2.13.1'
This commit is contained in:
commit
cd3a067308
3 changed files with 125 additions and 43 deletions
|
|
@ -22,8 +22,9 @@ class PhotoshopServerStub():
|
||||||
def open(self, path):
|
def open(self, path):
|
||||||
"""
|
"""
|
||||||
Open file located at 'path' (local).
|
Open file located at 'path' (local).
|
||||||
:param path: <string> file path locally
|
Args:
|
||||||
:return: None
|
path(string): file path locally
|
||||||
|
Returns: None
|
||||||
"""
|
"""
|
||||||
self.websocketserver.call(self.client.call
|
self.websocketserver.call(self.client.call
|
||||||
('Photoshop.open', path=path)
|
('Photoshop.open', path=path)
|
||||||
|
|
@ -32,9 +33,10 @@ class PhotoshopServerStub():
|
||||||
def read(self, layer, layers_meta=None):
|
def read(self, layer, layers_meta=None):
|
||||||
"""
|
"""
|
||||||
Parses layer metadata from Headline field of active document
|
Parses layer metadata from Headline field of active document
|
||||||
:param layer: <namedTuple Layer("id":XX, "name":"YYY")
|
Args:
|
||||||
:param layers_meta: full list from Headline (for performance in loops)
|
layer: <namedTuple Layer("id":XX, "name":"YYY")
|
||||||
:return:
|
layers_meta: full list from Headline (for performance in loops)
|
||||||
|
Returns:
|
||||||
"""
|
"""
|
||||||
if layers_meta is None:
|
if layers_meta is None:
|
||||||
layers_meta = self.get_layers_metadata()
|
layers_meta = self.get_layers_metadata()
|
||||||
|
|
@ -44,22 +46,26 @@ class PhotoshopServerStub():
|
||||||
def imprint(self, layer, data, all_layers=None, layers_meta=None):
|
def imprint(self, layer, data, all_layers=None, layers_meta=None):
|
||||||
"""
|
"""
|
||||||
Save layer metadata to Headline field of active document
|
Save layer metadata to Headline field of active document
|
||||||
:param layer: <namedTuple> Layer("id": XXX, "name":'YYY')
|
Args:
|
||||||
:param data: <string> json representation for single layer
|
layer (namedtuple): Layer("id": XXX, "name":'YYY')
|
||||||
:param all_layers: <list of namedTuples> - for performance, could be
|
data(string): json representation for single layer
|
||||||
|
all_layers (list of namedtuples): for performance, could be
|
||||||
injected for usage in loop, if not, single call will be
|
injected for usage in loop, if not, single call will be
|
||||||
triggered
|
triggered
|
||||||
:param layers_meta: <string> json representation from Headline
|
layers_meta(string): json representation from Headline
|
||||||
(for performance - provide only if imprint is in
|
(for performance - provide only if imprint is in
|
||||||
loop - value should be same)
|
loop - value should be same)
|
||||||
:return: None
|
Returns: None
|
||||||
"""
|
"""
|
||||||
if not layers_meta:
|
if not layers_meta:
|
||||||
layers_meta = self.get_layers_metadata()
|
layers_meta = self.get_layers_metadata()
|
||||||
# json.dumps writes integer values in a dictionary to string, so
|
# json.dumps writes integer values in a dictionary to string, so
|
||||||
# anticipating it here.
|
# anticipating it here.
|
||||||
if str(layer.id) in layers_meta and layers_meta[str(layer.id)]:
|
if str(layer.id) in layers_meta and layers_meta[str(layer.id)]:
|
||||||
layers_meta[str(layer.id)].update(data)
|
if data:
|
||||||
|
layers_meta[str(layer.id)].update(data)
|
||||||
|
else:
|
||||||
|
layers_meta.pop(str(layer.id))
|
||||||
else:
|
else:
|
||||||
layers_meta[str(layer.id)] = data
|
layers_meta[str(layer.id)] = data
|
||||||
|
|
||||||
|
|
@ -83,7 +89,7 @@ class PhotoshopServerStub():
|
||||||
"""
|
"""
|
||||||
Returns JSON document with all(?) layers in active document.
|
Returns JSON document with all(?) layers in active document.
|
||||||
|
|
||||||
:return: <list of namedtuples>
|
Returns: <list of namedtuples>
|
||||||
Format of tuple: { 'id':'123',
|
Format of tuple: { 'id':'123',
|
||||||
'name': 'My Layer 1',
|
'name': 'My Layer 1',
|
||||||
'type': 'GUIDE'|'FG'|'BG'|'OBJ'
|
'type': 'GUIDE'|'FG'|'BG'|'OBJ'
|
||||||
|
|
@ -97,8 +103,9 @@ class PhotoshopServerStub():
|
||||||
def get_layers_in_layers(self, layers):
|
def get_layers_in_layers(self, layers):
|
||||||
"""
|
"""
|
||||||
Return all layers that belong to layers (might be groups).
|
Return all layers that belong to layers (might be groups).
|
||||||
:param layers: <list of namedTuples>
|
Args:
|
||||||
:return: <list of namedTuples>
|
layers <list of namedTuples>:
|
||||||
|
Returns: <list of namedTuples>
|
||||||
"""
|
"""
|
||||||
all_layers = self.get_layers()
|
all_layers = self.get_layers()
|
||||||
ret = []
|
ret = []
|
||||||
|
|
@ -116,7 +123,7 @@ class PhotoshopServerStub():
|
||||||
def create_group(self, name):
|
def create_group(self, name):
|
||||||
"""
|
"""
|
||||||
Create new group (eg. LayerSet)
|
Create new group (eg. LayerSet)
|
||||||
:return: <namedTuple Layer("id":XX, "name":"YYY")>
|
Returns: <namedTuple Layer("id":XX, "name":"YYY")>
|
||||||
"""
|
"""
|
||||||
ret = self.websocketserver.call(self.client.call
|
ret = self.websocketserver.call(self.client.call
|
||||||
('Photoshop.create_group',
|
('Photoshop.create_group',
|
||||||
|
|
@ -128,7 +135,7 @@ class PhotoshopServerStub():
|
||||||
def group_selected_layers(self, name):
|
def group_selected_layers(self, name):
|
||||||
"""
|
"""
|
||||||
Group selected layers into new LayerSet (eg. group)
|
Group selected layers into new LayerSet (eg. group)
|
||||||
:return: <json representation of Layer>
|
Returns: <json representation of Layer>
|
||||||
"""
|
"""
|
||||||
res = self.websocketserver.call(self.client.call
|
res = self.websocketserver.call(self.client.call
|
||||||
('Photoshop.group_selected_layers',
|
('Photoshop.group_selected_layers',
|
||||||
|
|
@ -139,7 +146,7 @@ class PhotoshopServerStub():
|
||||||
def get_selected_layers(self):
|
def get_selected_layers(self):
|
||||||
"""
|
"""
|
||||||
Get a list of actually selected layers
|
Get a list of actually selected layers
|
||||||
:return: <list of Layer('id':XX, 'name':"YYY")>
|
Returns: <list of Layer('id':XX, 'name':"YYY")>
|
||||||
"""
|
"""
|
||||||
res = self.websocketserver.call(self.client.call
|
res = self.websocketserver.call(self.client.call
|
||||||
('Photoshop.get_selected_layers'))
|
('Photoshop.get_selected_layers'))
|
||||||
|
|
@ -147,9 +154,10 @@ class PhotoshopServerStub():
|
||||||
|
|
||||||
def select_layers(self, layers):
|
def select_layers(self, layers):
|
||||||
"""
|
"""
|
||||||
Selecte specified layers in Photoshop
|
Selects specified layers in Photoshop by its ids
|
||||||
:param layers: <list of Layer('id':XX, 'name':"YYY")>
|
Args:
|
||||||
:return: None
|
layers: <list of Layer('id':XX, 'name':"YYY")>
|
||||||
|
Returns: None
|
||||||
"""
|
"""
|
||||||
layer_ids = [layer.id for layer in layers]
|
layer_ids = [layer.id for layer in layers]
|
||||||
|
|
||||||
|
|
@ -161,7 +169,7 @@ class PhotoshopServerStub():
|
||||||
def get_active_document_full_name(self):
|
def get_active_document_full_name(self):
|
||||||
"""
|
"""
|
||||||
Returns full name with path of active document via ws call
|
Returns full name with path of active document via ws call
|
||||||
:return: <string> full path with name
|
Returns(string): full path with name
|
||||||
"""
|
"""
|
||||||
res = self.websocketserver.call(
|
res = self.websocketserver.call(
|
||||||
self.client.call('Photoshop.get_active_document_full_name'))
|
self.client.call('Photoshop.get_active_document_full_name'))
|
||||||
|
|
@ -171,7 +179,7 @@ class PhotoshopServerStub():
|
||||||
def get_active_document_name(self):
|
def get_active_document_name(self):
|
||||||
"""
|
"""
|
||||||
Returns just a name of active document via ws call
|
Returns just a name of active document via ws call
|
||||||
:return: <string> file name
|
Returns(string): file name
|
||||||
"""
|
"""
|
||||||
res = self.websocketserver.call(self.client.call
|
res = self.websocketserver.call(self.client.call
|
||||||
('Photoshop.get_active_document_name'))
|
('Photoshop.get_active_document_name'))
|
||||||
|
|
@ -181,7 +189,7 @@ class PhotoshopServerStub():
|
||||||
def is_saved(self):
|
def is_saved(self):
|
||||||
"""
|
"""
|
||||||
Returns true if no changes in active document
|
Returns true if no changes in active document
|
||||||
:return: <boolean>
|
Returns: <boolean>
|
||||||
"""
|
"""
|
||||||
return self.websocketserver.call(self.client.call
|
return self.websocketserver.call(self.client.call
|
||||||
('Photoshop.is_saved'))
|
('Photoshop.is_saved'))
|
||||||
|
|
@ -189,7 +197,7 @@ class PhotoshopServerStub():
|
||||||
def save(self):
|
def save(self):
|
||||||
"""
|
"""
|
||||||
Saves active document
|
Saves active document
|
||||||
:return: None
|
Returns: None
|
||||||
"""
|
"""
|
||||||
self.websocketserver.call(self.client.call
|
self.websocketserver.call(self.client.call
|
||||||
('Photoshop.save'))
|
('Photoshop.save'))
|
||||||
|
|
@ -197,10 +205,11 @@ class PhotoshopServerStub():
|
||||||
def saveAs(self, image_path, ext, as_copy):
|
def saveAs(self, image_path, ext, as_copy):
|
||||||
"""
|
"""
|
||||||
Saves active document to psd (copy) or png or jpg
|
Saves active document to psd (copy) or png or jpg
|
||||||
:param image_path: <string> full local path
|
Args:
|
||||||
:param ext: <string psd|jpg|png>
|
image_path(string): full local path
|
||||||
:param as_copy: <boolean>
|
ext: <string psd|jpg|png>
|
||||||
:return: None
|
as_copy: <boolean>
|
||||||
|
Returns: None
|
||||||
"""
|
"""
|
||||||
self.websocketserver.call(self.client.call
|
self.websocketserver.call(self.client.call
|
||||||
('Photoshop.saveAs',
|
('Photoshop.saveAs',
|
||||||
|
|
@ -211,9 +220,10 @@ class PhotoshopServerStub():
|
||||||
def set_visible(self, layer_id, visibility):
|
def set_visible(self, layer_id, visibility):
|
||||||
"""
|
"""
|
||||||
Set layer with 'layer_id' to 'visibility'
|
Set layer with 'layer_id' to 'visibility'
|
||||||
:param layer_id: <int>
|
Args:
|
||||||
:param visibility: <true - set visible, false - hide>
|
layer_id: <int>
|
||||||
:return: None
|
visibility: <true - set visible, false - hide>
|
||||||
|
Returns: None
|
||||||
"""
|
"""
|
||||||
self.websocketserver.call(self.client.call
|
self.websocketserver.call(self.client.call
|
||||||
('Photoshop.set_visible',
|
('Photoshop.set_visible',
|
||||||
|
|
@ -224,7 +234,7 @@ class PhotoshopServerStub():
|
||||||
"""
|
"""
|
||||||
Reads layers metadata from Headline from active document in PS.
|
Reads layers metadata from Headline from active document in PS.
|
||||||
(Headline accessible by File > File Info)
|
(Headline accessible by File > File Info)
|
||||||
:return: <string> - json documents
|
Returns(string): - json documents
|
||||||
"""
|
"""
|
||||||
layers_data = {}
|
layers_data = {}
|
||||||
res = self.websocketserver.call(self.client.call('Photoshop.read'))
|
res = self.websocketserver.call(self.client.call('Photoshop.read'))
|
||||||
|
|
@ -234,22 +244,26 @@ class PhotoshopServerStub():
|
||||||
pass
|
pass
|
||||||
return layers_data
|
return layers_data
|
||||||
|
|
||||||
def import_smart_object(self, path):
|
def import_smart_object(self, path, layer_name):
|
||||||
"""
|
"""
|
||||||
Import the file at `path` as a smart object to active document.
|
Import the file at `path` as a smart object to active document.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
path (str): File path to import.
|
path (str): File path to import.
|
||||||
|
layer_name (str): Unique layer name to differentiate how many times
|
||||||
|
same smart object was loaded
|
||||||
"""
|
"""
|
||||||
res = self.websocketserver.call(self.client.call
|
res = self.websocketserver.call(self.client.call
|
||||||
('Photoshop.import_smart_object',
|
('Photoshop.import_smart_object',
|
||||||
path=path))
|
path=path, name=layer_name))
|
||||||
|
|
||||||
return self._to_records(res).pop()
|
return self._to_records(res).pop()
|
||||||
|
|
||||||
def replace_smart_object(self, layer, path):
|
def replace_smart_object(self, layer, path, layer_name):
|
||||||
"""
|
"""
|
||||||
Replace the smart object `layer` with file at `path`
|
Replace the smart object `layer` with file at `path`
|
||||||
|
layer_name (str): Unique layer name to differentiate how many times
|
||||||
|
same smart object was loaded
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
layer (namedTuple): Layer("id":XX, "name":"YY"..).
|
layer (namedTuple): Layer("id":XX, "name":"YY"..).
|
||||||
|
|
@ -257,8 +271,18 @@ class PhotoshopServerStub():
|
||||||
"""
|
"""
|
||||||
self.websocketserver.call(self.client.call
|
self.websocketserver.call(self.client.call
|
||||||
('Photoshop.replace_smart_object',
|
('Photoshop.replace_smart_object',
|
||||||
layer=layer,
|
layer_id=layer.id,
|
||||||
path=path))
|
path=path, name=layer_name))
|
||||||
|
|
||||||
|
def delete_layer(self, layer_id):
|
||||||
|
"""
|
||||||
|
Deletes specific layer by it's id.
|
||||||
|
Args:
|
||||||
|
layer_id (int): id of layer to delete
|
||||||
|
"""
|
||||||
|
self.websocketserver.call(self.client.call
|
||||||
|
('Photoshop.delete_layer',
|
||||||
|
layer_id=layer_id))
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
self.client.close()
|
self.client.close()
|
||||||
|
|
@ -267,8 +291,8 @@ class PhotoshopServerStub():
|
||||||
"""
|
"""
|
||||||
Converts string json representation into list of named tuples for
|
Converts string json representation into list of named tuples for
|
||||||
dot notation access to work.
|
dot notation access to work.
|
||||||
:return: <list of named tuples>
|
Returns: <list of named tuples>
|
||||||
:param res: <string> - json representation
|
res(string): - json representation
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
layers_data = json.loads(res)
|
layers_data = json.loads(res)
|
||||||
|
|
|
||||||
|
|
@ -133,14 +133,24 @@ class ExtractReviewCutUp(pype.api.Extractor):
|
||||||
"{ffprobe_path} -i \"{full_input_path}\" -show_streams "
|
"{ffprobe_path} -i \"{full_input_path}\" -show_streams "
|
||||||
"-select_streams a -loglevel error"
|
"-select_streams a -loglevel error"
|
||||||
).format(**locals())
|
).format(**locals())
|
||||||
|
|
||||||
self.log.debug("ffprob_cmd: {}".format(ffprob_cmd))
|
self.log.debug("ffprob_cmd: {}".format(ffprob_cmd))
|
||||||
audio_check_output = pype.api.subprocess(ffprob_cmd)
|
audio_check_output = pype.api.subprocess(ffprob_cmd)
|
||||||
self.log.debug(
|
self.log.debug(
|
||||||
"audio_check_output: {}".format(audio_check_output))
|
"audio_check_output: {}".format(audio_check_output))
|
||||||
|
|
||||||
|
# Fix one frame difference
|
||||||
|
""" TODO: this is just work-around for issue:
|
||||||
|
https://github.com/pypeclub/pype/issues/659
|
||||||
|
"""
|
||||||
|
frame_duration_extend = 1
|
||||||
|
if audio_check_output:
|
||||||
|
frame_duration_extend = 0
|
||||||
|
|
||||||
# translate frame to sec
|
# translate frame to sec
|
||||||
start_sec = float(frame_start) / fps
|
start_sec = float(frame_start) / fps
|
||||||
duration_sec = float(frame_end - frame_start + 1) / fps
|
duration_sec = float(
|
||||||
|
(frame_end - frame_start) + frame_duration_extend) / fps
|
||||||
|
|
||||||
empty_add = None
|
empty_add = None
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
from avalon import api, photoshop
|
from avalon import api, photoshop
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
|
||||||
stub = photoshop.stub()
|
stub = photoshop.stub()
|
||||||
|
|
||||||
|
|
@ -13,10 +15,13 @@ class ImageLoader(api.Loader):
|
||||||
representations = ["*"]
|
representations = ["*"]
|
||||||
|
|
||||||
def load(self, context, name=None, namespace=None, data=None):
|
def load(self, context, name=None, namespace=None, data=None):
|
||||||
|
layer_name = self._get_unique_layer_name(context["asset"]["name"],
|
||||||
|
name)
|
||||||
with photoshop.maintained_selection():
|
with photoshop.maintained_selection():
|
||||||
layer = stub.import_smart_object(self.fname)
|
layer = stub.import_smart_object(self.fname, layer_name)
|
||||||
|
|
||||||
self[:] = [layer]
|
self[:] = [layer]
|
||||||
|
namespace = namespace or layer_name
|
||||||
|
|
||||||
return photoshop.containerise(
|
return photoshop.containerise(
|
||||||
name,
|
name,
|
||||||
|
|
@ -27,11 +32,25 @@ class ImageLoader(api.Loader):
|
||||||
)
|
)
|
||||||
|
|
||||||
def update(self, container, representation):
|
def update(self, container, representation):
|
||||||
|
""" Switch asset or change version """
|
||||||
layer = container.pop("layer")
|
layer = container.pop("layer")
|
||||||
|
|
||||||
|
context = representation.get("context", {})
|
||||||
|
|
||||||
|
namespace_from_container = re.sub(r'_\d{3}$', '',
|
||||||
|
container["namespace"])
|
||||||
|
layer_name = "{}_{}".format(context["asset"], context["subset"])
|
||||||
|
# switching assets
|
||||||
|
if namespace_from_container != layer_name:
|
||||||
|
layer_name = self._get_unique_layer_name(context["asset"],
|
||||||
|
context["subset"])
|
||||||
|
else: # switching version - keep same name
|
||||||
|
layer_name = container["namespace"]
|
||||||
|
|
||||||
|
path = api.get_representation_path(representation)
|
||||||
with photoshop.maintained_selection():
|
with photoshop.maintained_selection():
|
||||||
stub.replace_smart_object(
|
stub.replace_smart_object(
|
||||||
layer, api.get_representation_path(representation)
|
layer, path, layer_name
|
||||||
)
|
)
|
||||||
|
|
||||||
stub.imprint(
|
stub.imprint(
|
||||||
|
|
@ -39,7 +58,36 @@ class ImageLoader(api.Loader):
|
||||||
)
|
)
|
||||||
|
|
||||||
def remove(self, container):
|
def remove(self, container):
|
||||||
container["layer"].Delete()
|
"""
|
||||||
|
Removes element from scene: deletes layer + removes from Headline
|
||||||
|
Args:
|
||||||
|
container (dict): container to be removed - used to get layer_id
|
||||||
|
"""
|
||||||
|
layer = container.pop("layer")
|
||||||
|
stub.imprint(layer, {})
|
||||||
|
stub.delete_layer(layer.id)
|
||||||
|
|
||||||
def switch(self, container, representation):
|
def switch(self, container, representation):
|
||||||
self.update(container, representation)
|
self.update(container, representation)
|
||||||
|
|
||||||
|
def _get_unique_layer_name(self, asset_name, subset_name):
|
||||||
|
"""
|
||||||
|
Gets all layer names and if 'name' is present in them, increases
|
||||||
|
suffix by 1 (eg. creates unique layer name - for Loader)
|
||||||
|
Args:
|
||||||
|
name (string): in format asset_subset
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
(string): name_00X (without version)
|
||||||
|
"""
|
||||||
|
name = "{}_{}".format(asset_name, subset_name)
|
||||||
|
names = {}
|
||||||
|
for layer in stub.get_layers():
|
||||||
|
layer_name = re.sub(r'_\d{3}$', '', layer.name)
|
||||||
|
if layer_name in names.keys():
|
||||||
|
names[layer_name] = names[layer_name] + 1
|
||||||
|
else:
|
||||||
|
names[layer_name] = 1
|
||||||
|
occurrences = names.get(name, 0)
|
||||||
|
|
||||||
|
return "{}_{:0>3d}".format(name, occurrences + 1)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue