libreflow.extensions.sk.export-psd-layers 1.0.0__tar.gz → 1.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- libreflow_extensions_sk_export_psd_layers-1.1.0/CHANGELOG.md +54 -0
- {libreflow_extensions_sk_export_psd_layers-1.0.0/src/libreflow.extensions.sk.export_psd_layers.egg-info → libreflow_extensions_sk_export_psd_layers-1.1.0}/PKG-INFO +31 -1
- {libreflow_extensions_sk_export_psd_layers-1.0.0 → libreflow_extensions_sk_export_psd_layers-1.1.0}/setup.py +6 -11
- libreflow_extensions_sk_export_psd_layers-1.1.0/src/libreflow/extensions/sk/export_psd_layers/__init__.py +598 -0
- {libreflow_extensions_sk_export_psd_layers-1.0.0 → libreflow_extensions_sk_export_psd_layers-1.1.0}/src/libreflow/extensions/sk/export_psd_layers/_version.py +3 -3
- {libreflow_extensions_sk_export_psd_layers-1.0.0 → libreflow_extensions_sk_export_psd_layers-1.1.0/src/libreflow.extensions.sk.export_psd_layers.egg-info}/PKG-INFO +31 -1
- {libreflow_extensions_sk_export_psd_layers-1.0.0 → libreflow_extensions_sk_export_psd_layers-1.1.0}/src/libreflow.extensions.sk.export_psd_layers.egg-info/SOURCES.txt +1 -0
- libreflow_extensions_sk_export_psd_layers-1.1.0/src/libreflow.extensions.sk.export_psd_layers.egg-info/requires.txt +1 -0
- libreflow_extensions_sk_export_psd_layers-1.0.0/CHANGELOG.md +0 -26
- libreflow_extensions_sk_export_psd_layers-1.0.0/src/libreflow/extensions/sk/export_psd_layers/__init__.py +0 -125
- {libreflow_extensions_sk_export_psd_layers-1.0.0 → libreflow_extensions_sk_export_psd_layers-1.1.0}/MANIFEST.in +0 -0
- {libreflow_extensions_sk_export_psd_layers-1.0.0 → libreflow_extensions_sk_export_psd_layers-1.1.0}/README.md +0 -0
- {libreflow_extensions_sk_export_psd_layers-1.0.0 → libreflow_extensions_sk_export_psd_layers-1.1.0}/setup.cfg +0 -0
- {libreflow_extensions_sk_export_psd_layers-1.0.0 → libreflow_extensions_sk_export_psd_layers-1.1.0}/src/libreflow/__init__.py +0 -0
- {libreflow_extensions_sk_export_psd_layers-1.0.0 → libreflow_extensions_sk_export_psd_layers-1.1.0}/src/libreflow/extensions/__init__.py +0 -0
- {libreflow_extensions_sk_export_psd_layers-1.0.0 → libreflow_extensions_sk_export_psd_layers-1.1.0}/src/libreflow/extensions/sk/__init__.py +0 -0
- {libreflow_extensions_sk_export_psd_layers-1.0.0 → libreflow_extensions_sk_export_psd_layers-1.1.0}/src/libreflow.extensions.sk.export_psd_layers.egg-info/dependency_links.txt +0 -0
- {libreflow_extensions_sk_export_psd_layers-1.0.0 → libreflow_extensions_sk_export_psd_layers-1.1.0}/src/libreflow.extensions.sk.export_psd_layers.egg-info/top_level.txt +0 -0
- {libreflow_extensions_sk_export_psd_layers-1.0.0 → libreflow_extensions_sk_export_psd_layers-1.1.0}/versioneer.py +0 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html)[^1].
|
|
7
|
+
|
|
8
|
+
<!---
|
|
9
|
+
Types of changes
|
|
10
|
+
|
|
11
|
+
- Added for new features.
|
|
12
|
+
- Changed for changes in existing functionality.
|
|
13
|
+
- Deprecated for soon-to-be removed features.
|
|
14
|
+
- Removed for now removed features.
|
|
15
|
+
- Fixed for any bug fixes.
|
|
16
|
+
- Security in case of vulnerabilities.
|
|
17
|
+
|
|
18
|
+
-->
|
|
19
|
+
|
|
20
|
+
## [Unreleased]
|
|
21
|
+
|
|
22
|
+
## [1.1.0] - 2025-04-24
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
|
|
26
|
+
* An action to export a psd for preview
|
|
27
|
+
* Use the `psd-tools` python module
|
|
28
|
+
* Three PNGs, one in full format, one with no characters and one without a safety margin
|
|
29
|
+
* These files are not tracked in Libreflow, they remain local
|
|
30
|
+
* A specific upload to kitsu action is available for this use case
|
|
31
|
+
* An action to publish and export at the same time
|
|
32
|
+
* An action to export layers in batch
|
|
33
|
+
* Available at a film hierarchy level
|
|
34
|
+
|
|
35
|
+
### Changed
|
|
36
|
+
|
|
37
|
+
* Export layers action now uses `psd-tools` python module instead of a Adobe extend script
|
|
38
|
+
* Process can takes longer, but Photoshop is no longer required
|
|
39
|
+
|
|
40
|
+
### Fixed
|
|
41
|
+
|
|
42
|
+
* Hide export layers action when file has no published revisions
|
|
43
|
+
|
|
44
|
+
## [1.0.1] - 2025-04-04
|
|
45
|
+
|
|
46
|
+
### Fixed
|
|
47
|
+
|
|
48
|
+
* including .jsx files in setup.py
|
|
49
|
+
|
|
50
|
+
## [1.0.0] - 2025-03-27
|
|
51
|
+
|
|
52
|
+
### Added
|
|
53
|
+
|
|
54
|
+
* Extension to export the layers of a Photoshop project as png images
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: libreflow.extensions.sk.export_psd_layers
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.1.0
|
|
4
4
|
Home-page: https://gitlab.com/lfs.coop/libreflow/libreflow_launcher
|
|
5
5
|
Author: Thomas Thiebaut
|
|
6
6
|
Author-email: autor@les-fees-speciales.coop
|
|
@@ -12,6 +12,7 @@ Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 or l
|
|
|
12
12
|
Classifier: Operating System :: OS Independent
|
|
13
13
|
Requires-Python: >=3.7
|
|
14
14
|
Description-Content-Type: text/markdown
|
|
15
|
+
Requires-Dist: psd-tools
|
|
15
16
|
Dynamic: author
|
|
16
17
|
Dynamic: author-email
|
|
17
18
|
Dynamic: classifier
|
|
@@ -20,6 +21,7 @@ Dynamic: description-content-type
|
|
|
20
21
|
Dynamic: home-page
|
|
21
22
|
Dynamic: keywords
|
|
22
23
|
Dynamic: license
|
|
24
|
+
Dynamic: requires-dist
|
|
23
25
|
Dynamic: requires-python
|
|
24
26
|
|
|
25
27
|
# Sk Export Psd Layers
|
|
@@ -48,6 +50,34 @@ Types of changes
|
|
|
48
50
|
|
|
49
51
|
## [Unreleased]
|
|
50
52
|
|
|
53
|
+
## [1.1.0] - 2025-04-24
|
|
54
|
+
|
|
55
|
+
### Added
|
|
56
|
+
|
|
57
|
+
* An action to export a psd for preview
|
|
58
|
+
* Use the `psd-tools` python module
|
|
59
|
+
* Three PNGs, one in full format, one with no characters and one without a safety margin
|
|
60
|
+
* These files are not tracked in Libreflow, they remain local
|
|
61
|
+
* A specific upload to kitsu action is available for this use case
|
|
62
|
+
* An action to publish and export at the same time
|
|
63
|
+
* An action to export layers in batch
|
|
64
|
+
* Available at a film hierarchy level
|
|
65
|
+
|
|
66
|
+
### Changed
|
|
67
|
+
|
|
68
|
+
* Export layers action now uses `psd-tools` python module instead of a Adobe extend script
|
|
69
|
+
* Process can takes longer, but Photoshop is no longer required
|
|
70
|
+
|
|
71
|
+
### Fixed
|
|
72
|
+
|
|
73
|
+
* Hide export layers action when file has no published revisions
|
|
74
|
+
|
|
75
|
+
## [1.0.1] - 2025-04-04
|
|
76
|
+
|
|
77
|
+
### Fixed
|
|
78
|
+
|
|
79
|
+
* including .jsx files in setup.py
|
|
80
|
+
|
|
51
81
|
## [1.0.0] - 2025-03-27
|
|
52
82
|
|
|
53
83
|
### Added
|
|
@@ -2,19 +2,18 @@ import setuptools
|
|
|
2
2
|
import versioneer
|
|
3
3
|
import os
|
|
4
4
|
|
|
5
|
-
readme = os.path.normpath(os.path.join(__file__,
|
|
5
|
+
readme = os.path.normpath(os.path.join(__file__, "..", "README.md"))
|
|
6
6
|
with open(readme, "r", encoding="utf-8") as fh:
|
|
7
7
|
long_description = fh.read()
|
|
8
8
|
|
|
9
|
-
long_description +=
|
|
9
|
+
long_description += "\n\n"
|
|
10
10
|
|
|
11
|
-
changelog = os.path.normpath(os.path.join(__file__,
|
|
11
|
+
changelog = os.path.normpath(os.path.join(__file__, "..", "CHANGELOG.md"))
|
|
12
12
|
with open(changelog, "r", encoding="utf-8") as fh:
|
|
13
13
|
long_description += fh.read()
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
setuptools.setup(
|
|
17
|
-
|
|
18
17
|
name="libreflow.extensions.sk.export_psd_layers",
|
|
19
18
|
version=versioneer.get_version(),
|
|
20
19
|
cmdclass=versioneer.get_cmdclass(),
|
|
@@ -32,15 +31,11 @@ setuptools.setup(
|
|
|
32
31
|
"Operating System :: OS Independent",
|
|
33
32
|
],
|
|
34
33
|
keywords="kabaret libreflow",
|
|
35
|
-
install_requires=[],
|
|
36
|
-
python_requires=
|
|
34
|
+
install_requires=["psd-tools"],
|
|
35
|
+
python_requires=">=3.7",
|
|
37
36
|
packages=setuptools.find_packages("src"),
|
|
38
37
|
package_dir={"": "src"},
|
|
39
38
|
package_data={
|
|
40
|
-
|
|
41
|
-
"*.css",
|
|
42
|
-
'*.png'
|
|
43
|
-
],
|
|
39
|
+
"": ["*.css", "*.png", "*.jsx"],
|
|
44
40
|
},
|
|
45
|
-
|
|
46
41
|
)
|
|
@@ -0,0 +1,598 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import json
|
|
3
|
+
import gazu
|
|
4
|
+
import time
|
|
5
|
+
|
|
6
|
+
from kabaret import flow
|
|
7
|
+
from libreflow.flows.default.flow.film import Film
|
|
8
|
+
from libreflow.baseflow.file import (
|
|
9
|
+
GenericRunAction,
|
|
10
|
+
TrackedFile,
|
|
11
|
+
FileRevisionNameChoiceValue,
|
|
12
|
+
UploadPNGToKitsu,
|
|
13
|
+
PublishAndRenderPlayblast,
|
|
14
|
+
)
|
|
15
|
+
from psd_tools import PSDImage
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class ExportPSDLayers(flow.Action):
|
|
19
|
+
ICON = ("icons.flow", "photoshop")
|
|
20
|
+
|
|
21
|
+
_file = flow.Parent()
|
|
22
|
+
_files = flow.Parent(2)
|
|
23
|
+
_shot = flow.Parent(5)
|
|
24
|
+
_sequence = flow.Parent(7)
|
|
25
|
+
|
|
26
|
+
revision = flow.Param(None, FileRevisionNameChoiceValue)
|
|
27
|
+
|
|
28
|
+
def allow_context(self, context):
|
|
29
|
+
return (
|
|
30
|
+
context
|
|
31
|
+
and self._file.format.get() in ["psd", "psb"]
|
|
32
|
+
and len(
|
|
33
|
+
self._file.get_revision_names(
|
|
34
|
+
sync_status="Available", published_only=True
|
|
35
|
+
)
|
|
36
|
+
) > 0
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
def needs_dialog(self):
|
|
40
|
+
return True
|
|
41
|
+
|
|
42
|
+
def get_buttons(self):
|
|
43
|
+
self.revision.revert_to_default()
|
|
44
|
+
return ["Export", "Cancel"]
|
|
45
|
+
|
|
46
|
+
def ensure_render_folder(self):
|
|
47
|
+
folder_name = self._file.complete_name.get()
|
|
48
|
+
folder_name += "_render"
|
|
49
|
+
|
|
50
|
+
if not self._files.has_folder(folder_name):
|
|
51
|
+
self._files.create_folder_action.folder_name.set(folder_name)
|
|
52
|
+
self._files.create_folder_action.category.set("Outputs")
|
|
53
|
+
self._files.create_folder_action.tracked.set(True)
|
|
54
|
+
self._files.create_folder_action.run(None)
|
|
55
|
+
|
|
56
|
+
return self._files[folder_name]
|
|
57
|
+
|
|
58
|
+
def ensure_render_folder_revision(self):
|
|
59
|
+
folder = self.ensure_render_folder()
|
|
60
|
+
revision_name = self.revision.get()
|
|
61
|
+
source_revision = self._file.get_revision(self.revision.get())
|
|
62
|
+
|
|
63
|
+
if not folder.has_revision(revision_name):
|
|
64
|
+
revision = folder.add_revision(revision_name)
|
|
65
|
+
folder.set_current_user_on_revision(revision_name)
|
|
66
|
+
else:
|
|
67
|
+
revision = folder.get_revision(revision_name)
|
|
68
|
+
|
|
69
|
+
revision.comment.set(source_revision.comment.get())
|
|
70
|
+
|
|
71
|
+
folder.ensure_last_revision_oid()
|
|
72
|
+
|
|
73
|
+
self._files.touch()
|
|
74
|
+
|
|
75
|
+
return revision
|
|
76
|
+
|
|
77
|
+
def run(self, button):
|
|
78
|
+
if button == "Cancel":
|
|
79
|
+
return
|
|
80
|
+
|
|
81
|
+
session = self.root().session()
|
|
82
|
+
log_format = "[EXPORT LAYERS] {message}"
|
|
83
|
+
|
|
84
|
+
# Start log message
|
|
85
|
+
session.log_info(
|
|
86
|
+
log_format.format(
|
|
87
|
+
message=f"Export started - {self._sequence.name()} {self._shot.name()} {self._file.name()} {self.revision.get()}"
|
|
88
|
+
)
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
# Get revisions
|
|
92
|
+
source_revision = self._file.get_revision(self.revision.get())
|
|
93
|
+
render_revision = self.ensure_render_folder_revision()
|
|
94
|
+
|
|
95
|
+
# Open photoshop file
|
|
96
|
+
psb = PSDImage.open(source_revision.get_path())
|
|
97
|
+
|
|
98
|
+
# JSON structure for layers order
|
|
99
|
+
layers_data = {
|
|
100
|
+
"from": os.path.basename(source_revision.get_path()),
|
|
101
|
+
"layers": [],
|
|
102
|
+
"hidden_layers": []
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
# Export image layers
|
|
106
|
+
folder_name = os.path.basename(render_revision.get_path())
|
|
107
|
+
|
|
108
|
+
for layer in reversed(psb):
|
|
109
|
+
output_path = os.path.join(
|
|
110
|
+
render_revision.get_path(),
|
|
111
|
+
"{folder}-{layer}.png".format(folder=folder_name, layer=layer.name),
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
session.log_info(log_format.format(message=f'Exporting layer {layer.name}'))
|
|
115
|
+
|
|
116
|
+
image = layer.composite(viewport=psb.bbox)
|
|
117
|
+
image.save(output_path)
|
|
118
|
+
|
|
119
|
+
# Push layer in correct JSON data
|
|
120
|
+
layers_data["layers" if layer.visible else "hidden_layers"].append(
|
|
121
|
+
layer.name
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
# Export JSON data
|
|
125
|
+
json_object = json.dumps(layers_data)
|
|
126
|
+
json_path = os.path.join(render_revision.get_path(), "layers.json")
|
|
127
|
+
|
|
128
|
+
session.log_info(log_format.format(message='Saving layers.json'))
|
|
129
|
+
with open(json_path, "w") as outfile:
|
|
130
|
+
outfile.write(json_object)
|
|
131
|
+
|
|
132
|
+
session.log_info(log_format.format(message='Export complete'))
|
|
133
|
+
|
|
134
|
+
return self.get_result(close=True)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
class SequencesSelectAll(flow.values.SessionValue):
|
|
138
|
+
DEFAULT_EDITOR = "bool"
|
|
139
|
+
|
|
140
|
+
_action = flow.Parent()
|
|
141
|
+
|
|
142
|
+
def _fill_ui(self, ui):
|
|
143
|
+
super(SequencesSelectAll, self)._fill_ui(ui)
|
|
144
|
+
if self._action.sequences.choices() == []:
|
|
145
|
+
ui["hidden"] = True
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
class SequencesMultiChoiceValue(flow.values.SessionValue):
|
|
149
|
+
DEFAULT_EDITOR = "multichoice"
|
|
150
|
+
|
|
151
|
+
_action = flow.Parent()
|
|
152
|
+
|
|
153
|
+
def choices(self):
|
|
154
|
+
return self._action._film.sequences.mapped_names()
|
|
155
|
+
|
|
156
|
+
def revert_to_default(self):
|
|
157
|
+
self.choices()
|
|
158
|
+
self.set([])
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
class TaskChoiceValue(flow.values.SessionValue):
|
|
162
|
+
DEFAULT_EDITOR = "choice"
|
|
163
|
+
|
|
164
|
+
_action = flow.Parent()
|
|
165
|
+
|
|
166
|
+
def choices(self):
|
|
167
|
+
return ['Any', 'BG_Layout', 'BG_Color']
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
class ExportPSDLayersBatch(flow.Action):
|
|
171
|
+
ICON = ("icons.flow", "photoshop")
|
|
172
|
+
|
|
173
|
+
select_all = (
|
|
174
|
+
flow.SessionParam(False, SequencesSelectAll).ui(editor="bool").watched()
|
|
175
|
+
)
|
|
176
|
+
sequences = flow.SessionParam([], SequencesMultiChoiceValue)
|
|
177
|
+
task_target = flow.SessionParam("Any", TaskChoiceValue)
|
|
178
|
+
|
|
179
|
+
_film = flow.Parent()
|
|
180
|
+
|
|
181
|
+
def allow_context(self, context):
|
|
182
|
+
user = self.root().project().get_user()
|
|
183
|
+
return user.status.get() == "Admin"
|
|
184
|
+
|
|
185
|
+
def get_buttons(self):
|
|
186
|
+
return ["Export", "Close"]
|
|
187
|
+
|
|
188
|
+
def needs_dialog(self):
|
|
189
|
+
self.message.set("<h2>Batch Export Layers</h2>")
|
|
190
|
+
return True
|
|
191
|
+
|
|
192
|
+
def child_value_changed(self, child_value):
|
|
193
|
+
if child_value is self.select_all:
|
|
194
|
+
if child_value.get():
|
|
195
|
+
self.sequences.set(self.sequences.choices())
|
|
196
|
+
else:
|
|
197
|
+
self.sequences.revert_to_default()
|
|
198
|
+
|
|
199
|
+
def run(self, button):
|
|
200
|
+
if button == "Close":
|
|
201
|
+
return
|
|
202
|
+
|
|
203
|
+
session = self.root().session()
|
|
204
|
+
log_format = "[BATCH EXPORT LAYERS] {status} - {sequence} {shot} {file} {revision}"
|
|
205
|
+
|
|
206
|
+
for seq_name in self.sequences.get():
|
|
207
|
+
seq = self._film.sequences[seq_name]
|
|
208
|
+
for shot in seq.shots.mapped_items():
|
|
209
|
+
for task in shot.tasks.mapped_items():
|
|
210
|
+
# Ignore if specific task parameter is used
|
|
211
|
+
if (
|
|
212
|
+
self.task_target.get() != "Any"
|
|
213
|
+
and task.name() != self.task_target.get().lower()
|
|
214
|
+
):
|
|
215
|
+
continue
|
|
216
|
+
for f in task.files.mapped_items():
|
|
217
|
+
# Get only photoshop files
|
|
218
|
+
if (
|
|
219
|
+
f.format.get() in ["psd", "psb"]
|
|
220
|
+
and len(
|
|
221
|
+
f.get_revision_names(
|
|
222
|
+
sync_status="Available", published_only=True
|
|
223
|
+
)
|
|
224
|
+
) > 0
|
|
225
|
+
):
|
|
226
|
+
# Check if revision is already exported
|
|
227
|
+
if task.files.has_folder(f'{task.name()}_render'):
|
|
228
|
+
render_folder = task.files[f'{task.name()}_render']
|
|
229
|
+
render_revision = render_folder.get_head_revision(sync_status="Available")
|
|
230
|
+
if render_revision and os.path.exists(render_revision.get_path()):
|
|
231
|
+
session.log_warning(
|
|
232
|
+
log_format.format(
|
|
233
|
+
status="Already exported",
|
|
234
|
+
sequence=seq.name(),
|
|
235
|
+
shot=shot.name(),
|
|
236
|
+
file=f.display_name.get(),
|
|
237
|
+
revision=render_revision.name()
|
|
238
|
+
)
|
|
239
|
+
)
|
|
240
|
+
continue
|
|
241
|
+
|
|
242
|
+
# Start export base action
|
|
243
|
+
f.export_layers.revision.revert_to_default()
|
|
244
|
+
f.export_layers.run("Export")
|
|
245
|
+
|
|
246
|
+
# Wait for base action to finish
|
|
247
|
+
for sp in (
|
|
248
|
+
self.root()
|
|
249
|
+
.session()
|
|
250
|
+
.cmds.SubprocessManager.list_runner_infos()
|
|
251
|
+
):
|
|
252
|
+
if sp["is_running"] and sp["label"] == "Export Layers":
|
|
253
|
+
while sp["is_running"]:
|
|
254
|
+
time.sleep(1)
|
|
255
|
+
sp = (
|
|
256
|
+
self.root()
|
|
257
|
+
.session()
|
|
258
|
+
.cmds.SubprocessManager.get_runner_info(
|
|
259
|
+
sp["id"]
|
|
260
|
+
)
|
|
261
|
+
)
|
|
262
|
+
break
|
|
263
|
+
|
|
264
|
+
# Upload render to exchange
|
|
265
|
+
render_folder = f.export_layers.ensure_render_folder()
|
|
266
|
+
render_revision = render_folder.get_head_revision(sync_status="Available")
|
|
267
|
+
|
|
268
|
+
if render_revision:
|
|
269
|
+
render_revision.upload.run('Upload')
|
|
270
|
+
|
|
271
|
+
session.log_info('[BATCH EXPORT LAYERS] Batch complete')
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
class PublishandExportPSD(PublishAndRenderPlayblast):
|
|
275
|
+
ICON = ('icons.libreflow', 'publish')
|
|
276
|
+
|
|
277
|
+
def allow_context(self, context):
|
|
278
|
+
return (
|
|
279
|
+
context
|
|
280
|
+
and super(PublishandExportPSD, self).allow_context(context)
|
|
281
|
+
and self._file.format.get() in ["psd", "psb"]
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
def get_buttons(self):
|
|
285
|
+
return ["Publish and Export Preview", "Cancel"]
|
|
286
|
+
|
|
287
|
+
def _configure_and_render(self, revision_name, upload_after_publish):
|
|
288
|
+
export_preview = self._file.export_preview
|
|
289
|
+
export_preview.revision.set(revision_name)
|
|
290
|
+
export_preview.upload_to_kitsu.set(upload_after_publish)
|
|
291
|
+
|
|
292
|
+
return export_preview.run("Export")
|
|
293
|
+
|
|
294
|
+
def run(self, button):
|
|
295
|
+
if button == "Cancel":
|
|
296
|
+
return
|
|
297
|
+
|
|
298
|
+
project_settings = self.root().project().settings()
|
|
299
|
+
if (
|
|
300
|
+
self.comment.get() == ""
|
|
301
|
+
and not project_settings.optional_publish_comment.get()
|
|
302
|
+
):
|
|
303
|
+
self.message.set(
|
|
304
|
+
"<h2>Publish</h2>Please enter a comment to describe your changes."
|
|
305
|
+
)
|
|
306
|
+
return self.get_result(close=False)
|
|
307
|
+
|
|
308
|
+
# Update parameter presets
|
|
309
|
+
self.update_presets()
|
|
310
|
+
|
|
311
|
+
# Publish
|
|
312
|
+
publish_action = self._file.publish_action
|
|
313
|
+
publish_action.publish_file(
|
|
314
|
+
self._file,
|
|
315
|
+
comment=self.comment.get(),
|
|
316
|
+
keep_editing=self.keep_editing.get(),
|
|
317
|
+
upload_after_publish=False,
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
# Playblast
|
|
321
|
+
ret = self._configure_and_render(
|
|
322
|
+
self._file.get_head_revision().name(), self.upload_after_publish.get()
|
|
323
|
+
)
|
|
324
|
+
|
|
325
|
+
return ret
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
class ExportPSDPreview(flow.Action):
|
|
329
|
+
ICON = ("icons.flow", "photoshop")
|
|
330
|
+
|
|
331
|
+
revision = flow.Param(None, FileRevisionNameChoiceValue)
|
|
332
|
+
upload_to_kitsu = flow.BoolParam(False)
|
|
333
|
+
|
|
334
|
+
_file = flow.Parent()
|
|
335
|
+
_shot = flow.Parent(5)
|
|
336
|
+
_sequence = flow.Parent(7)
|
|
337
|
+
|
|
338
|
+
def allow_context(self, context):
|
|
339
|
+
return (
|
|
340
|
+
context
|
|
341
|
+
and self._file.format.get() in ["psd", "psb"]
|
|
342
|
+
and len(
|
|
343
|
+
self._file.get_revision_names(
|
|
344
|
+
sync_status="Available", published_only=True
|
|
345
|
+
)
|
|
346
|
+
) > 0
|
|
347
|
+
)
|
|
348
|
+
|
|
349
|
+
def needs_dialog(self):
|
|
350
|
+
self.revision.revert_to_default()
|
|
351
|
+
return True
|
|
352
|
+
|
|
353
|
+
def get_buttons(self):
|
|
354
|
+
return ["Export", "Cancel"]
|
|
355
|
+
|
|
356
|
+
def get_target_path(self):
|
|
357
|
+
rev = self._file.get_revision(self.revision.get())
|
|
358
|
+
return rev.get_path()
|
|
359
|
+
|
|
360
|
+
def export_full(self, psb, file_path):
|
|
361
|
+
self.root().session().log_info("Exporting full project")
|
|
362
|
+
|
|
363
|
+
output_path = f"{os.path.splitext(file_path)[0]}.png"
|
|
364
|
+
|
|
365
|
+
image = psb.composite()
|
|
366
|
+
image.save(output_path)
|
|
367
|
+
|
|
368
|
+
return output_path
|
|
369
|
+
|
|
370
|
+
def export_no_chara(self, psb, file_path):
|
|
371
|
+
self.root().session().log_info("Exporting project without characters")
|
|
372
|
+
|
|
373
|
+
output_path = f"{os.path.splitext(file_path)[0]}_no_chara.png"
|
|
374
|
+
|
|
375
|
+
image = psb.composite(
|
|
376
|
+
layer_filter=lambda layer: layer.is_visible()
|
|
377
|
+
and "character" not in layer.name
|
|
378
|
+
and "INFO" not in layer.name
|
|
379
|
+
and "REF" not in layer.name
|
|
380
|
+
)
|
|
381
|
+
|
|
382
|
+
image.save(output_path)
|
|
383
|
+
|
|
384
|
+
return output_path
|
|
385
|
+
|
|
386
|
+
def export_cropped(self, psb, file_path):
|
|
387
|
+
self.root().session().log_info("Exporting project in film format")
|
|
388
|
+
|
|
389
|
+
output_path = f"{os.path.splitext(file_path)[0]}_no_safety.png"
|
|
390
|
+
|
|
391
|
+
frame_bbox = (0, 0, 0, 0)
|
|
392
|
+
|
|
393
|
+
frame_layer = psb.find("FRAME 4096x1716")
|
|
394
|
+
|
|
395
|
+
if frame_layer is None:
|
|
396
|
+
raise Exception("FRAME LAYER NOT FOUND")
|
|
397
|
+
else:
|
|
398
|
+
frame_bbox = frame_layer.bbox
|
|
399
|
+
|
|
400
|
+
if frame_bbox == (0, 0, 0, 0):
|
|
401
|
+
raise Exception("Frame Layer has no bounding box")
|
|
402
|
+
|
|
403
|
+
# Substract by 10pixels (width and height) in order to remove border margin
|
|
404
|
+
frame_bbox = (
|
|
405
|
+
frame_bbox[0] + 5,
|
|
406
|
+
frame_bbox[1] + 5,
|
|
407
|
+
frame_bbox[2] - 5,
|
|
408
|
+
frame_bbox[3] - 5,
|
|
409
|
+
)
|
|
410
|
+
|
|
411
|
+
image = psb.composite()
|
|
412
|
+
image = image.crop(frame_bbox)
|
|
413
|
+
image.save(output_path)
|
|
414
|
+
|
|
415
|
+
return output_path
|
|
416
|
+
|
|
417
|
+
def run(self, button):
|
|
418
|
+
if button == "Cancel":
|
|
419
|
+
return
|
|
420
|
+
|
|
421
|
+
path = self.get_target_path()
|
|
422
|
+
psb = PSDImage.open(path)
|
|
423
|
+
|
|
424
|
+
full_img_path = self.export_full(psb, path)
|
|
425
|
+
no_chara_img_path = self.export_no_chara(psb, path)
|
|
426
|
+
cropped_img_path = self.export_cropped(psb, path)
|
|
427
|
+
|
|
428
|
+
if self.upload_to_kitsu.get():
|
|
429
|
+
self._file.upload_preview.full_img_path.set(full_img_path)
|
|
430
|
+
self._file.upload_preview.no_chara_img_path.set(no_chara_img_path)
|
|
431
|
+
self._file.upload_preview.cropped_img_path.set(cropped_img_path)
|
|
432
|
+
self._file.upload_preview.revision_name.set(self.revision.get())
|
|
433
|
+
return self.get_result(next_action=self._file.upload_preview.oid())
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
class UploadPSDPreview(UploadPNGToKitsu):
|
|
437
|
+
_file = flow.Parent()
|
|
438
|
+
|
|
439
|
+
full_img_path = flow.Param("").ui(hidden=True)
|
|
440
|
+
no_chara_img_path = flow.Param("").ui(hidden=True)
|
|
441
|
+
cropped_img_path = flow.Param("").ui(hidden=True)
|
|
442
|
+
|
|
443
|
+
def allow_context(self, context):
|
|
444
|
+
return context and context.endswith(".details")
|
|
445
|
+
|
|
446
|
+
def upload_preview(
|
|
447
|
+
self,
|
|
448
|
+
kitsu_entity,
|
|
449
|
+
task_type_name,
|
|
450
|
+
task_status_name,
|
|
451
|
+
path_list,
|
|
452
|
+
comment="",
|
|
453
|
+
user_name=None,
|
|
454
|
+
):
|
|
455
|
+
kitsu_api = self.root().project().kitsu_api()
|
|
456
|
+
|
|
457
|
+
# Get user
|
|
458
|
+
user = kitsu_api.get_user(user_name)
|
|
459
|
+
|
|
460
|
+
# Get task
|
|
461
|
+
task = kitsu_api.get_task(kitsu_entity, task_type_name)
|
|
462
|
+
|
|
463
|
+
if task is None or user is None:
|
|
464
|
+
return False
|
|
465
|
+
|
|
466
|
+
# Add comment with preview
|
|
467
|
+
|
|
468
|
+
# Check if preview file exists
|
|
469
|
+
for file_path in path_list:
|
|
470
|
+
if not os.path.exists(file_path):
|
|
471
|
+
self.root().session().log_error(
|
|
472
|
+
f"Preview file '{file_path}' does not exists."
|
|
473
|
+
)
|
|
474
|
+
return False
|
|
475
|
+
|
|
476
|
+
task_status = gazu.task.get_task_status_by_name(task_status_name)
|
|
477
|
+
|
|
478
|
+
# Check if status is valid
|
|
479
|
+
if task_status is None:
|
|
480
|
+
task_statuses = gazu.task.all_task_statuses()
|
|
481
|
+
names = [ts["name"] for ts in task_statuses]
|
|
482
|
+
self.root().session().log_error(
|
|
483
|
+
(
|
|
484
|
+
f"Invalid task status '{task_status_name}'."
|
|
485
|
+
"Should be one of " + str(names) + "."
|
|
486
|
+
)
|
|
487
|
+
)
|
|
488
|
+
return False
|
|
489
|
+
|
|
490
|
+
comment = gazu.task.add_comment(task, task_status, comment=comment)
|
|
491
|
+
|
|
492
|
+
for file_path in path_list:
|
|
493
|
+
try:
|
|
494
|
+
gazu.task.add_preview(task, comment, file_path)
|
|
495
|
+
except json.decoder.JSONDecodeError:
|
|
496
|
+
self.root().session().log_warning(
|
|
497
|
+
f"Invalid response from Gazu while uploading preview {file_path}"
|
|
498
|
+
)
|
|
499
|
+
|
|
500
|
+
return True
|
|
501
|
+
|
|
502
|
+
def run(self, button):
|
|
503
|
+
if button == "Cancel":
|
|
504
|
+
return
|
|
505
|
+
|
|
506
|
+
self.update_presets()
|
|
507
|
+
|
|
508
|
+
if not self._check_kitsu_params():
|
|
509
|
+
self.root().session().log_error("KITSU PARAM ERROR")
|
|
510
|
+
return self.get_result(close=False)
|
|
511
|
+
|
|
512
|
+
kitsu_api = self.root().project().kitsu_api()
|
|
513
|
+
kitsu_entity = self._ensure_kitsu_entity()
|
|
514
|
+
|
|
515
|
+
if kitsu_entity is None:
|
|
516
|
+
self.root().session().log_error(
|
|
517
|
+
"No Kitsu entity for file " + self._file.oid()
|
|
518
|
+
)
|
|
519
|
+
return self.get_result(close=False)
|
|
520
|
+
|
|
521
|
+
task_status_data = kitsu_api.get_task_status(
|
|
522
|
+
short_name=self.target_task_status.names_dict[self.target_task_status.get()]
|
|
523
|
+
)
|
|
524
|
+
|
|
525
|
+
success = self.upload_preview(
|
|
526
|
+
kitsu_entity=kitsu_entity,
|
|
527
|
+
task_type_name=self.target_task_type.get(),
|
|
528
|
+
task_status_name=task_status_data["name"],
|
|
529
|
+
path_list=[
|
|
530
|
+
self.full_img_path.get(),
|
|
531
|
+
self.no_chara_img_path.get(),
|
|
532
|
+
self.cropped_img_path.get(),
|
|
533
|
+
],
|
|
534
|
+
comment=self.comment.get(),
|
|
535
|
+
)
|
|
536
|
+
|
|
537
|
+
if not success:
|
|
538
|
+
self.message.set(
|
|
539
|
+
(
|
|
540
|
+
"<h2>Upload playblast to Kitsu</h2>"
|
|
541
|
+
"<font color=#FF584D>An error occured "
|
|
542
|
+
"while uploading the preview.</font>"
|
|
543
|
+
)
|
|
544
|
+
)
|
|
545
|
+
return self.get_result(close=False)
|
|
546
|
+
|
|
547
|
+
rev = self._file.get_revision(self.revision_name.get())
|
|
548
|
+
rev.set_status("on_kitsu")
|
|
549
|
+
|
|
550
|
+
|
|
551
|
+
def publish_and_export_preview(parent):
|
|
552
|
+
if isinstance(parent, TrackedFile):
|
|
553
|
+
r = flow.Child(PublishandExportPSD)
|
|
554
|
+
r.name = "publish_and_export_preview"
|
|
555
|
+
r.index = 26
|
|
556
|
+
return r
|
|
557
|
+
|
|
558
|
+
|
|
559
|
+
def upload_preview(parent):
|
|
560
|
+
if isinstance(parent, TrackedFile):
|
|
561
|
+
r = flow.Child(UploadPSDPreview)
|
|
562
|
+
r.name = "upload_preview"
|
|
563
|
+
return r
|
|
564
|
+
|
|
565
|
+
|
|
566
|
+
def export_psd_layers(parent):
|
|
567
|
+
if isinstance(parent, TrackedFile):
|
|
568
|
+
r = flow.Child(ExportPSDLayers)
|
|
569
|
+
r.name = "export_layers"
|
|
570
|
+
r.index = 50
|
|
571
|
+
return r
|
|
572
|
+
|
|
573
|
+
if isinstance(parent, Film):
|
|
574
|
+
r = flow.Child(ExportPSDLayersBatch).ui(
|
|
575
|
+
label="Batch Export Layers", dialog_size=(750, 800)
|
|
576
|
+
)
|
|
577
|
+
r.name = "export_layers_batch"
|
|
578
|
+
r.index = None
|
|
579
|
+
return r
|
|
580
|
+
|
|
581
|
+
|
|
582
|
+
def export_psd_preview(parent):
|
|
583
|
+
if isinstance(parent, TrackedFile):
|
|
584
|
+
r = flow.Child(ExportPSDPreview)
|
|
585
|
+
r.name = "export_preview"
|
|
586
|
+
r.index = 49
|
|
587
|
+
return r
|
|
588
|
+
|
|
589
|
+
|
|
590
|
+
def install_extensions(session):
|
|
591
|
+
return {
|
|
592
|
+
"export_psd_layers": [
|
|
593
|
+
export_psd_layers,
|
|
594
|
+
export_psd_preview,
|
|
595
|
+
upload_preview,
|
|
596
|
+
publish_and_export_preview,
|
|
597
|
+
]
|
|
598
|
+
}
|
|
@@ -8,11 +8,11 @@ import json
|
|
|
8
8
|
|
|
9
9
|
version_json = '''
|
|
10
10
|
{
|
|
11
|
-
"date": "2025-
|
|
11
|
+
"date": "2025-04-24T19:49:41+0200",
|
|
12
12
|
"dirty": false,
|
|
13
13
|
"error": null,
|
|
14
|
-
"full-revisionid": "
|
|
15
|
-
"version": "1.
|
|
14
|
+
"full-revisionid": "29231b33e0e266c52c6065cb665d5fbbaf827fdf",
|
|
15
|
+
"version": "1.1.0"
|
|
16
16
|
}
|
|
17
17
|
''' # END VERSION_JSON
|
|
18
18
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: libreflow.extensions.sk.export_psd_layers
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.1.0
|
|
4
4
|
Home-page: https://gitlab.com/lfs.coop/libreflow/libreflow_launcher
|
|
5
5
|
Author: Thomas Thiebaut
|
|
6
6
|
Author-email: autor@les-fees-speciales.coop
|
|
@@ -12,6 +12,7 @@ Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 or l
|
|
|
12
12
|
Classifier: Operating System :: OS Independent
|
|
13
13
|
Requires-Python: >=3.7
|
|
14
14
|
Description-Content-Type: text/markdown
|
|
15
|
+
Requires-Dist: psd-tools
|
|
15
16
|
Dynamic: author
|
|
16
17
|
Dynamic: author-email
|
|
17
18
|
Dynamic: classifier
|
|
@@ -20,6 +21,7 @@ Dynamic: description-content-type
|
|
|
20
21
|
Dynamic: home-page
|
|
21
22
|
Dynamic: keywords
|
|
22
23
|
Dynamic: license
|
|
24
|
+
Dynamic: requires-dist
|
|
23
25
|
Dynamic: requires-python
|
|
24
26
|
|
|
25
27
|
# Sk Export Psd Layers
|
|
@@ -48,6 +50,34 @@ Types of changes
|
|
|
48
50
|
|
|
49
51
|
## [Unreleased]
|
|
50
52
|
|
|
53
|
+
## [1.1.0] - 2025-04-24
|
|
54
|
+
|
|
55
|
+
### Added
|
|
56
|
+
|
|
57
|
+
* An action to export a psd for preview
|
|
58
|
+
* Use the `psd-tools` python module
|
|
59
|
+
* Three PNGs, one in full format, one with no characters and one without a safety margin
|
|
60
|
+
* These files are not tracked in Libreflow, they remain local
|
|
61
|
+
* A specific upload to kitsu action is available for this use case
|
|
62
|
+
* An action to publish and export at the same time
|
|
63
|
+
* An action to export layers in batch
|
|
64
|
+
* Available at a film hierarchy level
|
|
65
|
+
|
|
66
|
+
### Changed
|
|
67
|
+
|
|
68
|
+
* Export layers action now uses `psd-tools` python module instead of a Adobe extend script
|
|
69
|
+
* Process can takes longer, but Photoshop is no longer required
|
|
70
|
+
|
|
71
|
+
### Fixed
|
|
72
|
+
|
|
73
|
+
* Hide export layers action when file has no published revisions
|
|
74
|
+
|
|
75
|
+
## [1.0.1] - 2025-04-04
|
|
76
|
+
|
|
77
|
+
### Fixed
|
|
78
|
+
|
|
79
|
+
* including .jsx files in setup.py
|
|
80
|
+
|
|
51
81
|
## [1.0.0] - 2025-03-27
|
|
52
82
|
|
|
53
83
|
### Added
|
|
@@ -8,6 +8,7 @@ src/libreflow/__init__.py
|
|
|
8
8
|
src/libreflow.extensions.sk.export_psd_layers.egg-info/PKG-INFO
|
|
9
9
|
src/libreflow.extensions.sk.export_psd_layers.egg-info/SOURCES.txt
|
|
10
10
|
src/libreflow.extensions.sk.export_psd_layers.egg-info/dependency_links.txt
|
|
11
|
+
src/libreflow.extensions.sk.export_psd_layers.egg-info/requires.txt
|
|
11
12
|
src/libreflow.extensions.sk.export_psd_layers.egg-info/top_level.txt
|
|
12
13
|
src/libreflow/extensions/__init__.py
|
|
13
14
|
src/libreflow/extensions/sk/__init__.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
psd-tools
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
All notable changes to this project will be documented in this file.
|
|
4
|
-
|
|
5
|
-
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
-
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html)[^1].
|
|
7
|
-
|
|
8
|
-
<!---
|
|
9
|
-
Types of changes
|
|
10
|
-
|
|
11
|
-
- Added for new features.
|
|
12
|
-
- Changed for changes in existing functionality.
|
|
13
|
-
- Deprecated for soon-to-be removed features.
|
|
14
|
-
- Removed for now removed features.
|
|
15
|
-
- Fixed for any bug fixes.
|
|
16
|
-
- Security in case of vulnerabilities.
|
|
17
|
-
|
|
18
|
-
-->
|
|
19
|
-
|
|
20
|
-
## [Unreleased]
|
|
21
|
-
|
|
22
|
-
## [1.0.0] - 2025-03-27
|
|
23
|
-
|
|
24
|
-
### Added
|
|
25
|
-
|
|
26
|
-
* Extension to export the layers of a Photoshop project as png images
|
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
|
|
3
|
-
from kabaret import flow
|
|
4
|
-
from kabaret.flow.object import _Manager
|
|
5
|
-
|
|
6
|
-
from libreflow.baseflow.file import GenericRunAction,TrackedFile,TrackedFolder,FileRevisionNameChoiceValue
|
|
7
|
-
|
|
8
|
-
class ExportPSDLayers(GenericRunAction):
|
|
9
|
-
_MANAGER_TYPE = _Manager
|
|
10
|
-
|
|
11
|
-
ICON = ('icons.flow', 'photoshop')
|
|
12
|
-
|
|
13
|
-
_file = flow.Parent()
|
|
14
|
-
_files = flow.Parent(2)
|
|
15
|
-
_task = flow.Parent(3)
|
|
16
|
-
_shot = flow.Parent(5)
|
|
17
|
-
_sequence = flow.Parent(7)
|
|
18
|
-
|
|
19
|
-
revision = flow.Param(None, FileRevisionNameChoiceValue)
|
|
20
|
-
|
|
21
|
-
exec_path = ""
|
|
22
|
-
|
|
23
|
-
def allow_context(self, context):
|
|
24
|
-
return (
|
|
25
|
-
context
|
|
26
|
-
and self._file.format.get() in ['psd', 'psb']
|
|
27
|
-
)
|
|
28
|
-
|
|
29
|
-
def needs_dialog(self):
|
|
30
|
-
return True
|
|
31
|
-
|
|
32
|
-
def get_buttons(self):
|
|
33
|
-
self.revision.revert_to_default()
|
|
34
|
-
|
|
35
|
-
msg = ""
|
|
36
|
-
|
|
37
|
-
site_env = self.root().project().get_current_site().site_environment
|
|
38
|
-
if site_env.has_mapped_name('PHOTOSHOP_EXEC_PATH'):
|
|
39
|
-
self.exec_path = site_env['PHOTOSHOP_EXEC_PATH'].value.get()
|
|
40
|
-
self.message.set(msg)
|
|
41
|
-
return ['Export','Cancel']
|
|
42
|
-
|
|
43
|
-
else :
|
|
44
|
-
msg = "<font color = red><b>Photoshop executable not found in site environment</b></font>"
|
|
45
|
-
self.message.set(msg)
|
|
46
|
-
return ['Cancel']
|
|
47
|
-
|
|
48
|
-
def ensure_render_folder(self):
|
|
49
|
-
folder_name = self._file.display_name.get().split('.')[0]
|
|
50
|
-
folder_name += '_render'
|
|
51
|
-
|
|
52
|
-
if not self._files.has_folder(folder_name):
|
|
53
|
-
self._files.create_folder_action.folder_name.set(folder_name)
|
|
54
|
-
self._files.create_folder_action.category.set('Outputs')
|
|
55
|
-
self._files.create_folder_action.tracked.set(True)
|
|
56
|
-
self._files.create_folder_action.run(None)
|
|
57
|
-
|
|
58
|
-
return self._files[folder_name]
|
|
59
|
-
|
|
60
|
-
def ensure_render_folder_revision(self):
|
|
61
|
-
folder = self.ensure_render_folder()
|
|
62
|
-
revision_name = self.revision.get()
|
|
63
|
-
revisions = folder.get_revisions()
|
|
64
|
-
source_revision = self._file.get_revision(self.revision.get())
|
|
65
|
-
|
|
66
|
-
if not folder.has_revision(revision_name):
|
|
67
|
-
revision = folder.add_revision(revision_name)
|
|
68
|
-
folder.set_current_user_on_revision(revision_name)
|
|
69
|
-
else:
|
|
70
|
-
revision = folder.get_revision(revision_name)
|
|
71
|
-
|
|
72
|
-
revision.comment.set(source_revision.comment.get())
|
|
73
|
-
|
|
74
|
-
folder.ensure_last_revision_oid()
|
|
75
|
-
|
|
76
|
-
self._files.touch()
|
|
77
|
-
|
|
78
|
-
return revision
|
|
79
|
-
|
|
80
|
-
def runner_name_and_tags(self):
|
|
81
|
-
return 'Photoshop', []
|
|
82
|
-
|
|
83
|
-
def get_run_label(self):
|
|
84
|
-
return 'Export Layers'
|
|
85
|
-
|
|
86
|
-
def target_file_extension(self):
|
|
87
|
-
if self._file.format.get() == 'psb':
|
|
88
|
-
return 'psb'
|
|
89
|
-
elif self._file.format.get() == 'psd':
|
|
90
|
-
return 'psd'
|
|
91
|
-
|
|
92
|
-
def extra_argv(self):
|
|
93
|
-
rev = self._file.get_revision(self.revision.get())
|
|
94
|
-
current_dir = os.path.split(__file__)[0]
|
|
95
|
-
script_path = os.path.normpath(os.path.join(current_dir,"scripts/LFS_PSD_export_to_PNG.jsx"))
|
|
96
|
-
|
|
97
|
-
return [rev.get_path(),script_path]
|
|
98
|
-
|
|
99
|
-
def run(self, button):
|
|
100
|
-
if button == 'Cancel':
|
|
101
|
-
return
|
|
102
|
-
|
|
103
|
-
folder_path = self.ensure_render_folder_revision().get_path()
|
|
104
|
-
|
|
105
|
-
super(ExportPSDLayers, self).run(button)
|
|
106
|
-
return self.get_result(close=True)
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
def export_psd_layers(parent):
|
|
110
|
-
if isinstance(parent, TrackedFile):
|
|
111
|
-
r = flow.Child(ExportPSDLayers)
|
|
112
|
-
r.name = 'export_layers'
|
|
113
|
-
return r
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
def install_extensions(session):
|
|
117
|
-
return {
|
|
118
|
-
"export_psd_layers": [
|
|
119
|
-
export_psd_layers,
|
|
120
|
-
]
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
from . import _version
|
|
125
|
-
__version__ = _version.get_versions()['version']
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|