No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

kellertools.py 23KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539
  1. # -*- coding: utf8 -*-
  2. '''
  3. Keller Pipeline Tools
  4. written by stefan ihringer (stefan@bildfehler.de)
  5. version: 2012-06-01
  6. Beschreibung:
  7. diverse Hilfsfunktionen, z.B. um ein Script gemäß der Namenskonvention zu speichern
  8. oder den Pfad einer Write-Node einzustellen.
  9. Todo: Support für andere Departments, nicht (mehr) verfügbare Projekte
  10. create_autowrite(), update_autowrite()
  11. open script from read/write
  12. Filename-Preview im UpdateWritePanel
  13. Bugs: Buchstaben in Shotnummer werden zwar erlaubt aber machen probleme bei update_writenode()
  14. '''
  15. import nuke, nukescripts
  16. import re, os, glob
  17. ## dictionary mit gültigen Projektnamen, wie sie in Dateinamen vorkommen
  18. project_list = {"VS": "Vampirschwestern",
  19. "VT": "Vatertage"}
  20. ## globale Settings:
  21. default_format = "dpx" ## default für AutoWrite nodes
  22. project_folder = r"\\calculon\o\_projekte" ## kein Backslash am Ende der Pfade
  23. scripts_folder = r"300_%s_Work2D\Comp" ## %s wird durch Projektnamen ersetzt
  24. render_folder = "500_Renders" ## für finale Renderings
  25. precomp_folder = "450_PreComp" ## für Vorgerendertes aus den Comps
  26. footage_folder = "220_ProcessedFootage" ## für Footage nach Degrain/Retusche
  27. class KellerTools(KellerNukePlugin):
  28. def configurePlugin(self):
  29. menubar = nuke.menu("Nuke")
  30. m = menubar.addMenu("&Render")
  31. m.addCommand("Save Script By Convention...", 'kellertools.save_by_convention()')
  32. pass
  33. #m = menubar.addMenu("&Workgroup")
  34. #m.addCommand("Update Render Directory...", 'kellertools.update_writenodes()')
  35. class FileSavePanel(nukescripts.PythonPanel) :
  36. def __init__(self, scriptinfo):
  37. '''
  38. Panel, das nach Projekt, Shot, Status, Name und Version fragt, bevor
  39. das Script mit einem gültigen Namen gespeichert werden kann. Scriptinfo
  40. kann ein dictionary sein, dass default-Werte enthält falls das aktuelle
  41. Script bereits mit einem gültigen Namen gespeichert ist.
  42. '''
  43. global project_list
  44. nukescripts.PythonPanel.__init__ (self, "Script speichern...", "com.keller.FileSavePanel", False)
  45. if scriptinfo is None:
  46. scriptinfo = {}
  47. last_artist = "" ## todo: letzten Artist aus irgendwelchen Preferences laden?
  48. last_project = project_list.values()[0]
  49. self.projid = scriptinfo.get('projid', "")
  50. self.project = nuke.Enumeration_Knob("project", "Project", project_list.values())
  51. self.project.setValue(scriptinfo.get("project", last_project))
  52. ## todo: projekt richtig selektieren, unbekannte Projekte erlauben
  53. self.department = nuke.String_Knob("department", "Department", scriptinfo.get('department', "Comp"))
  54. ## todo: department als dropdown zeigen
  55. self.sequence = nuke.String_Knob("sequence", "Sequence/Shot", scriptinfo.get('sequence', ""))
  56. self.shot = nuke.String_Knob("shot", "/", scriptinfo.get('shot', ""))
  57. self.shot.clearFlag(nuke.STARTLINE)
  58. self.identifier = nuke.String_Knob("identifier", "Identifier", scriptinfo.get('identifier', "Comp"))
  59. self.version = nuke.String_Knob("version", "Version", scriptinfo.get('version', "01")+scriptinfo.get('subversion',""))
  60. self.specifier = nuke.String_Knob("opt_specifier", "Specifier (opt.)", scriptinfo.get('opt_specifier', ""))
  61. self.artist = nuke.String_Knob("artist", "Artist", scriptinfo.get('artist', last_artist))
  62. self.info = nuke.String_Knob("opt_info", "Info (opt.)", scriptinfo.get('opt_info', ""))
  63. self.preview = nuke.Text_Knob("preview", "Fileame: ", "...")
  64. for k in (self.project, self.sequence, self.shot, self.identifier, self.specifier, self.version, self.artist, self.info, self.preview):
  65. self.addKnob(k)
  66. def knobChanged (self, knob) :
  67. "Vorschau des Dateinamens updaten, wenn ein Feld geändert wurde."
  68. if knob.name() in ("project", "showPanel"):
  69. for key, value in project_list.items():
  70. if value == self.project.value():
  71. self.projid = key
  72. break
  73. if knob.name() in ("department", "sequence", "shot", "identifier", "opt_specifier", "artist", "opt_info"):
  74. ## Sonderzeichen löschen
  75. cleanstr = re.sub("(?i)[\W_]", "", knob.value())
  76. knob.setValue(cleanstr)
  77. if knob.name() == "shot" and knob.value().upper() == "GEN":
  78. ## "GEN" immer in Großbuchstaben
  79. knob.setValue("GEN")
  80. if knob.name() == "version":
  81. ## version: nur Zahlen und ein optionaler Buchstabe
  82. cleanstr = re.sub("(?i)[^\da-z]", "", knob.value())
  83. try:
  84. cleanstr = re.search("(?i)\d+[a-z]?", cleanstr).group(0)
  85. except:
  86. cleanstr = ""
  87. knob.setValue(cleanstr)
  88. ## Version aus version & subversion zusammensetzen
  89. version_str = ""
  90. v_exp = re.match("(\d+)([a-z])?", self.version.value())
  91. try:
  92. version_str = "%02d" % int(v_exp.group(1))
  93. self.version.setValue(version_str)
  94. version_str = version_str + v_exp.group(2)
  95. self.version.setValue(version_str)
  96. except:
  97. pass
  98. tokens = self.artist.value()
  99. if self.specifier.value():
  100. tokens = self.specifier.value() + "." + tokens
  101. if self.info.value():
  102. tokens = tokens + "." + self.info.value()
  103. newname = "%s_%s_%s_%s.%s_v%s.%s.nk" % (self.projid, self.department.value(), self.sequence.value(), self.shot.value(), self.identifier.value(), version_str, tokens)
  104. self.preview.setValue(newname)
  105. return
  106. def showModalDialog( self ):
  107. result = nukescripts.PythonPanel.showModalDialog( self )
  108. return result
  109. def getInfo( self ):
  110. '''
  111. Gibt ein dictionary mit den Werten der Eingabefelder zurück.
  112. Leere Eingabefelder werden ignoriert, so dass save_by_convention() dann den Fehler bemerkt.
  113. '''
  114. result = {}
  115. for k in (self.project, self.department, self.sequence, self.shot, self.identifier, self.specifier, self.version, self.artist, self.info):
  116. if k.value():
  117. result[k.name()] = re.sub("(?i)[\W_]", "", k.value())
  118. ## Projekt-ID ermitteln
  119. global project_list
  120. for key,value in project_list.items():
  121. if value == result['project']:
  122. result['projid'] = key
  123. break
  124. ## Version mit Buchstaben splitten in version & subversion
  125. v_exp = re.match("(\d+)([a-z])?", result['version'])
  126. if len(v_exp.groups()) == 2 and v_exp.group(2) is not None:
  127. result['version'] = v_exp.group(1)
  128. result['subversion'] = v_exp.group(2)
  129. return result
  130. ##-------------------------------------------------------------------------------------------------------
  131. class UpdateWritePanel(nukescripts.PythonPanel) :
  132. def __init__(self, writename = "", default_target = 0, default_format = "exr", default_space = "", default_specifier = ""):
  133. '''
  134. Panel, das zum Updaten einer Write-Node angezeigt wird und nach Dateiformat
  135. und Colorspace fragt. Es unterstützt Haupt-Renderings, PreComps und ProcessedFootage.
  136. '''
  137. ## TODO: Defaults als dictionary übergeben wir im FileSavePanel
  138. fileformatlist = {"exr":'linear', "dpx":'logC', "tiff":'sRGB', "tga":'sRGB', "jpeg":'sRGB'}
  139. colorspacelist = ['linear', 'logC', 'sRGB', 'rec709']
  140. self.rendertargets = ['Final Render', 'PreComp', 'Processed Footage']
  141. if not default_format:
  142. default_format = "exr"
  143. if not default_space:
  144. ## kein colorspace angegeben: auf Basis des Dateiformats ermitteln oder als letzte Alternative "linear"
  145. default_space = fileformatlist.get(default_format, "linear")
  146. nukescripts.PythonPanel.__init__ (self, "Set Render Path for '%s'" % writename, "com.keller.UpdateWritePanel", False)
  147. self.target = nuke.Enumeration_Knob("target", "Type", self.rendertargets)
  148. self.target.setValue(self.rendertargets[default_target])
  149. tmplist = fileformatlist.keys()
  150. tmplist.sort()
  151. self.fileformat = nuke.Enumeration_Knob("fileformat", "File Format / Color Space", tmplist)
  152. self.fileformat.setValue(default_format)
  153. self.colorspace = nuke.Enumeration_Knob("colorspace", " ", colorspacelist)
  154. self.colorspace.clearFlag(nuke.STARTLINE)
  155. self.colorspace.setValue(default_space)
  156. self.specifier = nuke.String_Knob("specifier", "PreComp Specifier", "")
  157. if default_target == 0:
  158. self.specifier.setFlag(nuke.DISABLED)
  159. self.specifier.setValue("n/a")
  160. else:
  161. self.specifier.setValue(default_specifier)
  162. ## TODO: Dateinamensvorschau wie im FileSavePanel
  163. for k in (self.target, self.fileformat, self.colorspace, self.specifier):
  164. self.addKnob(k)
  165. def knobChanged (self, knob) :
  166. if knob.name() == "target":
  167. i = self.rendertargets.index(knob.value())
  168. if i == 0:
  169. self.specifier.setFlag(nuke.DISABLED)
  170. if self.specifier.value() == "":
  171. self.specifier.setValue("n/a")
  172. elif i == 1:
  173. self.fileformat.setValue("exr")
  174. self.colorspace.setValue("linear")
  175. self.specifier.clearFlag(nuke.DISABLED)
  176. if self.specifier.value() == "n/a":
  177. self.specifier.setValue("")
  178. else:
  179. self.specifier.clearFlag(nuke.DISABLED)
  180. if self.specifier.value() == "n/a":
  181. self.specifier.setValue("")
  182. if knob.name() == "specifier":
  183. ## Sonderzeichen löschen
  184. cleanstr = re.sub("(?i)[\W_]", "", knob.value())
  185. knob.setValue(cleanstr)
  186. def showModalDialog( self ):
  187. "Show the panel as a modal dialog."
  188. result = nukescripts.PythonPanel.showModalDialog( self )
  189. return result
  190. def getInfo( self ):
  191. "Gibt ein dictionary mit den Werten der Eingabefelder zurück."
  192. result = {}
  193. result['target'] = self.rendertargets.index(self.target.value())
  194. result['fileformat'] = self.fileformat.value()
  195. result['colorspace'] = self.colorspace.value()
  196. if result['target'] > 0:
  197. result['specifier'] = re.sub("(?i)[\W_]", "", self.specifier.value())
  198. return result
  199. ##-------------------------------------------------------------------------------------------------------
  200. def parse_scriptname(s_name = "", quick = False):
  201. '''
  202. Analysiert den Dateinamen des angegebenen oder aktuellen Scripts, um Projekt, Shot, etc... zu ermitteln.
  203. Ist der Name ungültig, wird None zurück gegeben, ansonsten ein dictionary mit Stringstücken.
  204. Der Parameter quick kann verwendet werden, um nur schnell ein Verzeichnis zu scannen ohne Projektnamen aufzulösen.
  205. projid und project existieren dann nicht im zurückgegebenen Dictionary.
  206. '''
  207. ## Namenskonvention:
  208. ## [PROJECT]_[DEPARTMENT]_[SEQ]_[SHOT].[IDENTIFIER]_[VERSION][SUBVERSION].[OPT_SPECIFIER].[ARTISTNAME].[OPT_INFO].[EXT]
  209. ## z.B. VS_Comp_030_010.Main_v02b.sih.FixedKeyEdges.nk
  210. if not s_name:
  211. s_name = nuke.root().knob("name").value()
  212. if not s_name:
  213. return None
  214. s_path = os.path.dirname(s_name)
  215. s_name = os.path.splitext(os.path.basename(s_name))[0]
  216. s_exp = re.match(r"""(?P<projid>.+?)_ # VS_
  217. (?P<department>.+?)_ # Comp_
  218. (?P<sequence>\d+)_ # 001_
  219. (?P<shot>\d+|GEN) # 010 (oder "GEN")
  220. \. # .
  221. (?P<identifier>.+?)_ # Comp_
  222. v(?P<version>\d+) # v01
  223. (?P<subversion>[a-z]?) # a
  224. \. # .
  225. (?P<tokens>[\w.]+) # x.artist.y """, s_name, re.IGNORECASE|re.VERBOSE)
  226. if not s_exp:
  227. return None
  228. result = s_exp.groupdict()
  229. ## GEN statt Shotnummer immer in Großbuchstaben
  230. if result['shot'].lower() == "gen":
  231. result['shot'] = "GEN"
  232. if result['subversion'] == None:
  233. result['subversion'] = ""
  234. ## 'tokens' enthält den string nach der Versionsnummer, der 1-3 Felder enthalten kann.
  235. result['artist'] = ""
  236. result['opt_specifier'] = ""
  237. result['opt_info'] = ""
  238. tokens = result['tokens'].split('.', 3)
  239. if len(tokens) == 1:
  240. ## Nur Artistname (kein Check auf dessen Gültigkeit!)
  241. result['artist'] = tokens[0]
  242. elif len(tokens) == 3:
  243. ## Artist + beide optionale Tokens
  244. result.update(dict(zip( ['opt_specifier','opt_artist','opt_info'], tokens )))
  245. else:
  246. ## 2 Tokens. Herausfinden, welcher der beiden der Artistname (2 oder 3 Buchstaben) ist
  247. if re.match("[a-zA-Z][a-zA-Z][a-zA-Z]?", tokens[0]):
  248. result['artist'] = tokens[0]
  249. result['opt_info'] = tokens[1]
  250. elif re.match("[a-zA-Z][a-zA-Z][a-zA-Z]?", tokens[1]):
  251. result['opt_specifier'] = tokens[0]
  252. result['artist'] = tokens[1]
  253. else:
  254. return None
  255. if not quick:
  256. if result['projid'] in project_list:
  257. result['project'] = project_list[result['projid']]
  258. else:
  259. ## Projektnamen aus Dateipfad ermitteln
  260. global scripts_folder
  261. pattern = re.compile(scripts_folder % "(\w+)", re.IGNORECASE)
  262. p_exp = pattern.search(s_path)
  263. if p_exp:
  264. result['project'] = p_exp.group(1)
  265. print("Projektname aus Pfad ermittelt: %s", p_exp.group(1))
  266. return result
  267. def scan_shot_directory(dirname, identifier = ""):
  268. '''
  269. Durchsucht ein Verzeichnis und erstellt eine Liste an vorhandenen Comps.
  270. Wird z.B. verwendet, um nach der nächsthöheren Versionsnummer zu suchen.
  271. Rückgabe ist ein Dict von Versionsnummern->Aristnamen sowie den Feldern
  272. "maxversion" (höchste gefundene Versionsnummer) und "nextversion"
  273. (nächsthöhere Version) bzw. None bei Fehlern / leeren Ordnern.
  274. Wenn Identifier angegeben, werden nur diese Files gesucht.
  275. '''
  276. ## TODO: Buchstaben hinter Version ermitteln
  277. if not os.path.isdir(dirname):
  278. return None
  279. result = {}
  280. maxversion = 0
  281. wildcardsearch = dirname + "/*" + identifier + "_v*.nk"
  282. filelist = glob.glob(wildcardsearch)
  283. ## Todo: exception handling
  284. for f in filelist:
  285. info = parse_scriptname(f, quick = True)
  286. if info:
  287. v = int(info['version'])
  288. if v > maxversion:
  289. maxversion = v
  290. result[v] = info['artist']
  291. if result:
  292. result['maxversion'] = maxversion
  293. result['nextversion'] = maxversion + 1
  294. if identifier:
  295. result['identifier'] = identifier
  296. return result
  297. def build_scriptname(s = {}):
  298. '''
  299. Erstellt einen Dateinamen auf Basis der angegebenen Stringstücke.
  300. projid und project muss beides vorhanden sein.
  301. '''
  302. try:
  303. global project_folder
  304. global scripts_folder
  305. shot_or_gen = s['shot']
  306. if shot_or_gen.upper() == "GEN":
  307. ## gesondertes _GEN-Unterverzeichnis
  308. shot_or_gen = "_GEN"
  309. newpath = scripts_folder % s['project']
  310. newpath = os.path.join( project_folder, s['project'], newpath, s['sequence'], shot_or_gen )
  311. tokens = s['artist']
  312. if s.get('opt_specifier'):
  313. tokens = s.get('opt_specifier') + "." + tokens
  314. if s.get('opt_info'):
  315. tokens = tokens + "." + s.get('opt_info')
  316. newname = "%s_%s_%s_%s.%s_v%02d%s.%s.nk" % (s['projid'], s['department'], s['sequence'], s['shot'], s['identifier'], int(s['version']), s.get('subversion',''), tokens)
  317. return os.path.join(os.path.normpath(newpath), newname)
  318. except:
  319. ## nicht alle benötigten Stringstücke gefunden
  320. return None
  321. def build_renderpath(s = {}, w = {}):
  322. '''
  323. Erstellt einen zum Scriptnamen passenden Dateinamen für write-nodes.
  324. s ist ein dictionary mit scriptinfo (von parse_scriptname)
  325. w ist ein dictionary mit den keys target, fileformat, colorspace und evtl. specifier (vom UpdateWritePanel)
  326. '''
  327. ## TODO: für ProcessedFootage sollte der Name eher dem Footagenamen entsprechen statt dem Comp-Namen.
  328. ## TODO: "GEN" unterstützen
  329. try:
  330. global project_folder
  331. global render_folder
  332. global precomp_folder
  333. global footage_folder
  334. global default_format
  335. fileformat = w['fileformat']
  336. colorspace = w['colorspace']
  337. targetfolders = (render_folder, precomp_folder, footage_folder)
  338. if not fileformat:
  339. fileformat = default_format
  340. if colorspace == "linear":
  341. ## "linear" wird nicht in den Pfad geschrieben. Hier ggf. "lin" setzen, wenn gewünscht
  342. colorspace = ""
  343. elif colorspace == "AlexaV3LogC":
  344. ## "logC" ist kürzer für Pfadnamen
  345. colorspace = "logC"
  346. dirname = "%s_%s_%s_%s.%s_v%02d%s.%s" % (s['projid'], s['department'], s['sequence'], s['shot'], s['identifier'], int(s['version']), s.get('subversion',''), s['artist'])
  347. if w['target'] > 0:
  348. dirname = dirname + "." + w['specifier']
  349. if colorspace:
  350. dirname = dirname + "." + colorspace
  351. filename = dirname + ".%04d." + fileformat
  352. renderpath = os.path.join( project_folder, s['project'], targetfolders[w['target']], s['sequence'], s['shot'], dirname, filename )
  353. return os.path.normpath(renderpath)
  354. except:
  355. ## nicht alle benötigten Stringstücke gefunden
  356. return None
  357. def save_by_convention():
  358. "Fragt nach Dateinamen etc... und speichert das Script nach der Dateinamenskonvention."
  359. scriptinfo = parse_scriptname()
  360. dlg = FileSavePanel(scriptinfo)
  361. if dlg.showModalDialog():
  362. scriptinfo = dlg.getInfo()
  363. newname = build_scriptname(scriptinfo)
  364. if newname is None:
  365. nuke.message("Dateiname konnte nicht erstellt werden.\nWurden alle Felder ausgefüllt?")
  366. else:
  367. try:
  368. # existiert Datei schon? (ggf. von einem anderen Artist!)
  369. ## TODO: Buchstaben hinter Versionsnummer prüfen
  370. versionlist = scan_shot_directory(os.path.dirname(newname), scriptinfo['identifier'])
  371. ##print(versionlist)
  372. try:
  373. if versionlist:
  374. v = int(scriptinfo['version'])
  375. ## wenn zu speichernde Version nicht existiert, wirft versionlist[v] eine Exception
  376. if not nuke.ask("v%02d already exists (artist: %s).\nDo you want to save as v%02d (smallest available version) instead?" % (v, versionlist[v], versionlist['nextversion'])):
  377. raise RuntimeError
  378. # höhere Versionsnummer setzen
  379. scriptinfo['version'] = "%02d" % versionlist['nextversion']
  380. newname = build_scriptname(scriptinfo)
  381. except KeyError:
  382. # zu speichernde Version existiert eh noch nicht
  383. pass
  384. ## create directory
  385. newpath = os.path.dirname(newname)
  386. if newpath and not os.path.exists(newpath):
  387. os.makedirs(newpath)
  388. print "kellertools: created directory " + newpath
  389. print "kellertools: saving as " + newname
  390. nuke.scriptSaveAs(newname)
  391. nuke.message("Script gespeichert: " + nuke.root().knob("name").value())
  392. except OSError:
  393. nuke.message("Error creating " + newpath)
  394. except RuntimeError:
  395. nuke.message("Script nicht gespeichert")
  396. def update_writenode(scriptinfo, the_node):
  397. '''
  398. Stellt den Namen einer Write-Node ein, entweder für finale Renderings, preComps oder
  399. als Ausgabe nach ProcessedFootage.
  400. '''
  401. global default_format
  402. global render_folder
  403. global precomp_folder
  404. global footage_folder
  405. target = 0
  406. fileformat = ""
  407. colorspace = ""
  408. specifier = ""
  409. ## Defaults für Fileformat, Colorspace und Renderziel aus der Write-Node ermitteln
  410. print("kellertools: update_writenode für %s" % the_node.name())
  411. fileformat = the_node.knob("file_type").value()
  412. colorspace = the_node.knob("colorspace").value()
  413. if colorspace == "AlexaV3LogC":
  414. colorspace = "logC"
  415. elif colorspace not in ('linear', 'logC', 'sRGB', 'rec709'):
  416. colorspace = ""
  417. print("kellertools: ermitteltes fileformat: %s/%s" % (fileformat, colorspace))
  418. filename = the_node.knob("file").value()
  419. if precomp_folder.lower() in filename.lower():
  420. target = 1
  421. elif footage_folder.lower() in filename.lower():
  422. target = 2
  423. s_exp = re.search(r"_v\d+[a-z]?.[^.]+.(\w+)", os.path.basename(filename), re.IGNORECASE)
  424. try:
  425. specifier = s_exp.group(1)
  426. except:
  427. pass
  428. ## Dialogfenster anzeigen
  429. dlg = UpdateWritePanel(the_node.name(), target, fileformat, colorspace, specifier)
  430. if dlg.showModalDialog():
  431. writeinfo = dlg.getInfo()
  432. if writeinfo['colorspace'] == "logC":
  433. writeinfo['colorspace'] = "AlexaV3LogC"
  434. elif writeinfo['colorspace'] not in ('sRGB', 'rec709'):
  435. writeinfo['colorspace'] = "linear"
  436. writepath = build_renderpath(scriptinfo, writeinfo)
  437. if writepath:
  438. the_node.knob("file").fromUserText(writepath)
  439. the_node.knob("colorspace").setValue(writeinfo['colorspace'])
  440. the_node.knob("raw").setValue(False)
  441. print "kellertools: neuer Pfad gesetzt (%s/%s)" % (writeinfo['fileformat'], writeinfo['colorspace'])
  442. else:
  443. return False
  444. return True
  445. def update_writenodes():
  446. "Ruft update_writenode() für alle selektierten Write-Nodes auf."
  447. scriptinfo = parse_scriptname()
  448. found_writes = False
  449. if scriptinfo:
  450. for the_node in nuke.selectedNodes():
  451. if the_node.Class() == "Write":
  452. found_writes = True
  453. if not update_writenode(scriptinfo, the_node):
  454. nuke.message("Fehler beim Einstellen von %s.\nFür PreComps ist ein Specifier nötig." % the_node.name())
  455. if not found_writes:
  456. nuke.message("Keine Write-Nodes ausgewählt.")
  457. else:
  458. nuke.message("Scriptname entspricht leider nicht der Konvention.")
  459. def update_autowrite():
  460. '''
  461. Wird vor dem rendern aufgerufen und erzwingt den richtigen Ausgabepfad, wenn eine
  462. Write-Node namens "AutoWrite" gefunden wurde.
  463. '''
  464. ## TODO
  465. pass
  466. def create_autowrite():
  467. '''
  468. Erstellt eine Write-Node, die immer den richtigen Ausgabepfad beinhaltet.
  469. '''
  470. ## TODO
  471. pass
  472. ##fin