forked from rodeofx/shotgunEvents
-
Notifications
You must be signed in to change notification settings - Fork 122
/
update_timecode_from_frames.py
180 lines (147 loc) · 5.76 KB
/
update_timecode_from_frames.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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
# Copyright 2018 Autodesk, Inc. All rights reserved.
#
# Use of this software is subject to the terms of the Autodesk license agreement
# provided at the time of installation or download, or which otherwise accompanies
# this software in either electronic or hard copy form.
#
# See docs folder for detailed usage info.
import os
import math
import shotgun_api3
def registerCallbacks(reg):
"""
Register our callbacks.
:param reg: A Registrar instance provided by the event loop handler.
"""
# Grab authentication env vars for this plugin. Install these into the env
# if they don't already exist.
server = os.environ["SG_SERVER"]
script_name = os.environ["SGDAEMON_UTFF_NAME"]
script_key = os.environ["SGDAEMON_UTFF_KEY"]
args = {
"entity_type": "Shot",
"source_frames_field": "sg_cut_duration",
"target_tc_field": "sg_cut_length_tc",
"fps": 24.0,
}
# Grab an sg connection for the validator.
sg = shotgun_api3.Shotgun(server, script_name=script_name, api_key=script_key)
# Bail if our validator fails.
if not is_valid(sg, reg.logger, args):
reg.logger.warning("Plugin is not valid, will not register callback.")
return
event_filter = {
"Shotgun_%s_Change" % args["entity_type"]: [args["source_frames_field"]]
}
reg.registerCallback(
script_name, script_key, update_shot_cut_duration_timecode, event_filter, args,
)
def is_valid(sg, logger, args):
"""
Validate our args.
:param sg: Shotgun API handle.
:param logger: Logger instance.
:param args: Any additional misc arguments passed through this plugin.
:returns: True if plugin is valid, None if not.
"""
args_to_check = {
"source_frames_field": {"sg_type": "number", "type": "str"},
"target_tc_field": {"sg_type": "timecode", "type": "str"},
"fps": {"type": "float"},
}
# Make sure we can read the entity_type's schema.
try:
entity_schema = sg.schema_field_read(args["entity_type"])
except Exception as e:
logger.warning(
'Can\'t read Shotgun schema for "entity_type" setting\'s value ("%s"): %s'
% (args["entity_type"], e)
)
return
for name, type_targets in args_to_check.iteritems():
# Grab the setting's value type.
value_type = type(args[name]).__name__
# We assume unicode and str to be equivalent for these checks because
# args come back from Django as unicode but are first set by the
# Registrar as str.
if value_type == "unicode":
value_type = "str"
# Make sure the setting value is the correct Python type.
if value_type not in type_targets["type"]:
logger.warning(
'"%s" setting\'s value is type "%s" but should be type "%s," please fix.'
% (name, value_type, type_targets["type"])
)
return
# If we've got a sg_type, we assume the setting refers to a Shotgun
# field. If we don't, stop the checks here.
if not type_targets.get("sg_type"):
continue
# Grab the Shotgun field data type, if the field exists.
sg_type = entity_schema.get(args[name], {}).get("data_type", {}).get("value")
# Make sure the field exists on the entity.
if not sg_type:
logger.warning(
'"%s" setting refers to a %s entity field ("%s") that doesn\'t exist, please fix.'
% (name, args["entity_type"], args[name],)
)
return
# Make sure the field is the correct Shotgun type.
if sg_type not in type_targets["sg_type"]:
logger.warning(
'"%s" setting refers to a Shotgun field that is type "%s" but should be type "%s," please fix.'
% (name, sg_type, type_targets["sg_type"])
)
return
return True
def update_shot_cut_duration_timecode(sg, logger, event, args):
"""
Updates a timecode field based on a frames value field.
:param sg: Shotgun API handle.
:param logger: Logger instance.
:param event: A Shotgun EventLogEntry entity dictionary.
:param args: Any additional misc arguments passed through this plugin.
"""
# Return if we don't have all the field values we need.
if not event.get("meta", {}).get("entity_id"):
return
# Make some vars for convenience.
entity_id = event["meta"]["entity_id"]
fps = float(args["fps"])
# Re-query the entity to gather extra field values.
entity = sg.find_one(
args["entity_type"], [["id", "is", entity_id]], [args["source_frames_field"]],
)
# Return if we don't have an entity dict.
if not entity:
logger.info("No %s with id %s." % (args["entity_type"], entity_id))
return
# If we've got a frame value, update our entity's timecode field value. Note
# that we round up for the timecode conversion to int.
if entity[args["source_frames_field"]] is not None:
sg.update(
args["entity_type"],
entity["id"],
{
args["target_tc_field"]: int(
math.ceil(entity[args["source_frames_field"]] / fps * 1000)
)
},
)
logger.info(
"Updated %s %s timecode with %s"
% (
args["entity_type"],
str(entity["id"]),
{
args["target_tc_field"]: int(
math.ceil(entity[args["source_frames_field"]] / fps * 1000)
)
},
)
)
else:
logger.info(
"Did not update %s with ID %s, nothing to do."
% (args["entity_type"], entity["id"])
)