29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225 | class BatchMovieCreator(TrayPublishCreator):
"""Creates instances from movie file(s).
Intended for .mov files, but should work for any video file.
Doesn't handle image sequences though.
"""
identifier = "render_movie_batch"
label = "Batch Movies"
product_base_type = "render"
product_type = product_base_type
description = "Publish batch of video files"
create_allow_context_change = False
version_regex = re.compile(r"^(.+)_v([0-9]+)$")
# Position batch creator after simple creators
order = 110
product_types = []
def get_icon(self):
return "fa.file"
def create(self, product_name, data, pre_create_data):
file_paths = pre_create_data.get("filepath")
if not file_paths:
return
ambiguity_warnings = []
data_by_folder_id = collections.defaultdict(list)
for file_info in file_paths:
instance_data = copy.deepcopy(data)
file_name = file_info["filenames"][0]
filepath = os.path.join(file_info["directory"], file_name)
instance_data["creator_attributes"] = {"filepath": filepath}
folder_entities, version = get_folder_entities_from_filename(
self.project_name,
file_name,
self.version_regex
)
if version is not None:
instance_data["version"] = version
if not folder_entities:
raise CreatorError(
f"Couldn't find folder entity for '{file_name}'"
)
if len(folder_entities) > 1:
paths = "\n".join(f"- {f['path']}" for f in folder_entities)
ambiguity_warnings.append(
f"'{file_name}' matched multiple folders:\n{paths}"
)
# take first folder entity, if multiple matched
data_by_folder_id[folder_entities[0]["id"]].append(
(instance_data, folder_entities[0])
)
all_task_entities = ayon_api.get_tasks(
self.project_name, folder_ids=set(data_by_folder_id.keys())
)
task_entity_by_folder_id = collections.defaultdict(dict)
for task_entity in all_task_entities:
folder_id = task_entity["folderId"]
task_name = task_entity["name"].lower()
task_entity_by_folder_id[folder_id][task_name] = task_entity
project_entity = self.create_context.get_current_project_entity()
project_settings = self.create_context.get_current_project_settings()
for folder_id, instance_tuples in data_by_folder_id.items():
for (instance_data, folder_entity) in instance_tuples:
task_entities_by_name = task_entity_by_folder_id[folder_id]
task_name = None
task_entity = None
for default_task_name in self.default_tasks:
_name = default_task_name.lower()
if _name in task_entities_by_name:
task_entity = task_entities_by_name[_name]
task_name = task_entity["name"]
break
product_type = instance_data.get("productType")
if not product_type:
product_type = self.product_base_type
product_name = self._get_product_name(
self.project_name,
project_settings,
project_entity,
folder_entity,
task_entity,
data["variant"],
product_type,
)
instance_data["folderPath"] = folder_entity["path"]
instance_data["task"] = task_name
# Create new instance
new_instance = CreatedInstance(
product_base_type=self.product_base_type,
product_type=product_type,
product_name=product_name,
data=instance_data,
creator=self,
)
self._store_new_instance(new_instance)
if ambiguity_warnings:
raise CreatorError(
"Multiple folders matched one or more batch movie files:\n"
+ "\n".join(ambiguity_warnings)
+ "\n\nMake sure that it's attached to the correct folder "
+ "in the next step."
)
def _get_product_name(
self,
project_name: str,
project_settings: dict[str, Any],
project_entity: dict[str, Any],
folder_entity: dict[str, Any],
task_entity: Optional[dict[str, Any]],
variant: str,
product_type: str,
) -> str:
"""Create product name according to standard template process"""
kwargs = dict(
project_name=project_name,
project_entity=project_entity,
folder_entity=folder_entity,
task_entity=task_entity,
product_base_type=self.product_base_type,
product_type=product_type,
variant=variant,
host_name=self.create_context.host_name,
project_settings=project_settings,
)
try:
product_name = get_product_name(**kwargs)
except TaskNotSetError:
# Create instance with fake task
# - instance will be marked as invalid so it can't be published
# but user have ability to change it
# NOTE: This expect that there is not task 'Undefined' on folder
kwargs["task_entity"] = {
"type": "Undefined",
"name": "Undefined",
"label": "Undefined",
}
product_name = get_product_name(**kwargs)
return product_name
def get_instance_attr_defs(self):
return [
BoolDef(
"add_review_family",
default=True,
label="Review"
)
]
def get_pre_create_attr_defs(self):
product_types = self.product_types
if not product_types:
product_types = ["render"]
# Use same attributes as for instance attributes
return [
FileDef(
"filepath",
folders=False,
single_item=False,
extensions=self.extensions,
allow_sequences=False,
label="Filepath"
),
BoolDef(
"add_review_family",
default=True,
label="Review"
),
EnumDef(
"product_type",
items=product_types,
label="Product type",
),
]
def get_detail_description(self) -> str:
return """# Publish batch of .mov to multiple folders.
File names must then contain only folder name, or folder name +
version, eg. 'chair.mov', 'chair_v001.mov', not really safe
`my_chair_v001.mov`.
"""
|