├── .gitignore
├── LICENSE
├── MagicPlugins
├── init.py
├── install_plugin_dialog.py
├── magic_plugins.py
├── menu.py
├── plugins
│ ├── Internet.png
│ └── Internet
│ │ ├── 3D.png
│ │ ├── 3D
│ │ └── placeholder
│ │ ├── Blink.png
│ │ ├── Blink
│ │ └── placeholder
│ │ ├── Channel.png
│ │ ├── Channel
│ │ └── placeholder
│ │ ├── Color.png
│ │ ├── Color
│ │ └── placeholder
│ │ ├── Deep.png
│ │ ├── Deep
│ │ └── placeholder
│ │ ├── Draw.png
│ │ ├── Draw
│ │ └── placeholder
│ │ ├── Filter.png
│ │ ├── Filter
│ │ └── placeholder
│ │ ├── Keyer.png
│ │ ├── Keyer
│ │ └── placeholder
│ │ ├── Merge.png
│ │ ├── Merge
│ │ └── placeholder
│ │ ├── Other.png
│ │ ├── Other
│ │ └── placeholder
│ │ ├── Transform.png
│ │ └── Transform
│ │ └── placeholder
└── resources
│ ├── MagicPlugins.png
│ ├── header.png
│ ├── icon.png
│ ├── install_plugin.png
│ ├── installing_plugin_library.png
│ ├── installing_plugin_menu.png
│ ├── installing_plugin_ui.png
│ └── open_folder.png
├── README.md
└── init.py
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .pyc
3 | __pycache__
4 | .vscode
5 | tab_stats.dat
6 | .MagicPlugins/plugin
7 | ./test
8 | MagicPlugins/plugins/
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Gilles Vink
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/MagicPlugins/init.py:
--------------------------------------------------------------------------------
1 | """
2 | MagicPlugins by Gilles Vink
3 |
4 | Init.py script to automatically load Nuke plugins
5 |
6 | """
7 |
8 | import magic_plugins
9 |
10 | magic_plugins = magic_plugins.MagicPlugins()
11 | magic_plugins.load_plugins()
12 |
--------------------------------------------------------------------------------
/MagicPlugins/install_plugin_dialog.py:
--------------------------------------------------------------------------------
1 | import nuke
2 | import nukescripts
3 | import os
4 |
5 |
6 | class InstallPluginDialog(nukescripts.PythonPanel):
7 | def __init__(self):
8 | """Allows user to select plugin to install, we will ask the user
9 | a few things.
10 |
11 | We need the path to the plugin the user wants to install.
12 | We may want the icon path to the plugin.
13 | Then we want to specify a category for the plugin.
14 |
15 | And last but not least, if it is a library plugin, we need to
16 | correct Nuke version for the plugin. This option is only available
17 | if the plugin is a .dll, .so or a .dylib file."""
18 | nukescripts.PythonPanel.__init__(self, "MagicPlugins - Install plugin")
19 |
20 | plugin_categories = [
21 | "3D",
22 | "Blink",
23 | "Channel",
24 | "Color",
25 | "Deep",
26 | "Draw",
27 | "Filter",
28 | "Keyer",
29 | "Merge",
30 | "Other",
31 | "Transform",
32 | ]
33 |
34 | # Getting the current Nuke version
35 | current_nuke_version = str(
36 | ("%i.%i" % (nuke.NUKE_VERSION_MAJOR, nuke.NUKE_VERSION_MINOR))
37 | )
38 |
39 | # List containing all Nuke versions to date
40 | nuke_versions = [
41 | "13.2",
42 | "13.1",
43 | "13.0",
44 | "12.2",
45 | "12.1",
46 | "12.0",
47 | "11.3",
48 | "11.2",
49 | "11.1",
50 | "11.0",
51 | ]
52 |
53 | # Add Nuke version if it is not present in the list already
54 | # (new releases while code is not updated)
55 | if current_nuke_version not in nuke_versions:
56 | nuke_versions.append(current_nuke_version)
57 |
58 | # Defining extensions we would call basic
59 | self.basic_extensions = (
60 | ".nk",
61 | ".gizmo",
62 | )
63 |
64 | # These are the extensions that are Nuke specific plugins
65 | self.library_extensions = (
66 | ".dll",
67 | ".so",
68 | ".dylib",
69 | )
70 |
71 | # Getting the script location to locate the header image
72 | script_location = os.path.dirname(os.path.realpath(__file__))
73 | header_image = os.path.join(script_location, "resources", "header.png")
74 | header_image = header_image.replace(os.sep, "/")
75 |
76 | # Here we will create all the knobs
77 |
78 | self.header = nuke.Text_Knob(
79 | "header",
80 | "",
81 | "
" % header_image,
82 | )
83 | self.help_text = nuke.Text_Knob(
84 | "helpText",
85 | "",
86 | "Select the file you want to install \n (.gizmo, .nk, .dll, .so "
87 | "and .dylib files are supported). \n You can optionally select an "
88 | "icon for the file to use.",
89 | )
90 |
91 | self.divider_1 = nuke.Text_Knob("divider1", "", "")
92 |
93 | self.plugin_file_path = nuke.File_Knob(
94 | "pluginPath", "Plugin file path"
95 | )
96 |
97 | self.divider_2 = nuke.Text_Knob("divider2", "", "")
98 |
99 | self.icon_file_path = nuke.File_Knob(
100 | "iconPath", "Icon file path (optional)"
101 | )
102 |
103 | self.divider_3 = nuke.Text_Knob("divider3", "", "")
104 |
105 | self.plugin_category = nuke.Enumeration_Knob(
106 | "pluginCategory", "Plugin category", plugin_categories
107 | )
108 |
109 | self.divider_4 = nuke.Text_Knob("divider4", "", "")
110 |
111 | self.library_nuke_version = nuke.Enumeration_Knob(
112 | "nukeVersion", "Nuke version", nuke_versions
113 | )
114 | self.library_nuke_version.setVisible(False)
115 |
116 | self.divider_5 = nuke.Text_Knob("divider5", "", "")
117 | self.divider_5.setVisible(False)
118 |
119 | # Now we will add the knobs to the dialog window
120 | for knob in (
121 | self.header,
122 | self.help_text,
123 | self.divider_1,
124 | self.plugin_file_path,
125 | self.divider_2,
126 | self.icon_file_path,
127 | self.divider_3,
128 | self.plugin_category,
129 | self.divider_4,
130 | self.library_nuke_version,
131 | self.divider_5,
132 | ):
133 | self.addKnob(knob)
134 |
135 | def knobChanged(self, knob):
136 | # If the knob is the plugin path, check and validate it
137 | if knob == self.plugin_file_path:
138 | file_path = knob.value()
139 |
140 | # Skip validation if the file path is empty
141 | if file_path is not "":
142 |
143 | # We will call the validation function to check the path
144 | validated = self.__validate_plugin_path(file_path)
145 | if validated is not True:
146 |
147 | # Let the user know if it is not validated
148 | nuke.message(validated)
149 |
150 | # Set the knob to empty
151 | knob.setValue("")
152 |
153 | # If the knob is the icon path, check and validate it
154 | elif knob == self.icon_file_path:
155 | file_path = knob.value()
156 |
157 | # Skip validation if the file path is empty
158 | if file_path is not "":
159 |
160 | # We will call the validation function to check the path
161 | validated = self.__validate_icon_path(file_path)
162 | if validated is not True:
163 |
164 | # Let the user know if it is not validated
165 | nuke.message(validated)
166 |
167 | # Set the knob to empty
168 | knob.setValue("")
169 |
170 | def __validate_plugin_path(self, file_path):
171 | """This function will validate the plugin, if the plugin is validated
172 | it will return True, else it will return a reason why
173 | it is not validated.
174 |
175 | Besides that, it will set the visibility for the version knobs. Because
176 | for the library plugins we will need to specify the Nuke version."""
177 |
178 | # First of all let's check if the file really exists on disk
179 | if os.path.isfile(file_path):
180 |
181 | # Let's assume we won't validate the file, and that we
182 | # don't want to see the Nuke version knob
183 | validated = False
184 | visibility = False
185 |
186 | # If the file ends with a basic extension (.nk and .gizmo) we
187 | # will accept it and set the nuke version visibility to false
188 | if file_path.endswith(self.basic_extensions):
189 | validated = True
190 | visibility = False
191 |
192 | # If the file ends with a library extension (.dll, .so and .dylib)
193 | # we will accept it and set the nuke version visibility to true
194 | elif file_path.endswith(self.library_extensions):
195 | validated = True
196 | visibility = True
197 |
198 | # If we can't validate it, the extension is not accepted
199 | else:
200 | validated = "Unknown extension"
201 |
202 | # Set the visibility for the Nuke version knob
203 | for knob in (
204 | self.library_nuke_version,
205 | self.divider_5,
206 | ):
207 | knob.setVisible(visibility)
208 |
209 | return validated
210 |
211 | # If the file is not found, let the user know
212 | else:
213 | return "File %s not found" % file_path
214 |
215 | def __validate_icon_path(self, file_path):
216 | """Function to validate the icon provided, if
217 | the file is validated we will return a True value. Otherwise
218 | a message containing the error will be provided."""
219 |
220 | # First of all let's check if the file exists
221 | if os.path.isfile(file_path):
222 |
223 | # Now we will check if the file is a png file
224 | if file_path.endswith(".png"):
225 | return True
226 |
227 | # If the file is something different than a png, we will
228 | # let the user know another file is needed
229 | else:
230 | return "File %s is not a png" % file_path
231 |
232 | # If the file is not found, we will let the user know the
233 | # file does not exist on disk
234 | else:
235 | return "File %s is not found" % file_path
236 |
--------------------------------------------------------------------------------
/MagicPlugins/magic_plugins.py:
--------------------------------------------------------------------------------
1 | """
2 | MagicPlugins by Gilles Vink (2022)
3 |
4 | Script to automatically load gizmo, nk and library files
5 | and create menus
6 |
7 | """
8 |
9 | import os
10 | import sys
11 | import nuke
12 |
13 |
14 | # Only load if we have a GUI
15 | if nuke.GUI:
16 | import install_plugin_dialog
17 | from shutil import copy2
18 |
19 |
20 | class MagicPlugins(object):
21 | """Main class containing all functions to
22 | populate our menu and load plugins"""
23 |
24 | def __init__(self):
25 | """In the init function we will create the variables
26 | we need in the entire script to load the plugins.
27 |
28 | Like the directory to scan for plugins, the current Nuke version,
29 | and the list of plugins we want to load.
30 |
31 | In the menu_name variable we can assign the name for the menu."""
32 |
33 | # Startup message
34 | magic_plugins_version = 1.2
35 | self.__print("Version %s" % str(magic_plugins_version))
36 |
37 | # Getting install location to load plugins
38 | self.script_directory = os.path.dirname(os.path.realpath(__file__))
39 | plugins_directory = os.path.join(self.script_directory, "plugins")
40 |
41 | # Fix for Windows based systems
42 | self.plugins_directory = plugins_directory.replace(os.sep, "/")
43 |
44 | # Getting the current Nuke version required
45 | # to define which library file to use
46 | self.nuke_version = str(
47 | ("%i.%i" % (nuke.NUKE_VERSION_MAJOR, nuke.NUKE_VERSION_MINOR))
48 | )
49 |
50 | # Getting operating system to determine library extension
51 | # and to call the folder open function
52 | self.operating_system = sys.platform
53 | # Always collect all the plugins when this script is initialized
54 | self.plugins = self.__locate_plugins(self.plugins_directory)
55 |
56 | # This is the name we use for our menu in Nuke
57 | self.menu_name = "MagicPlugins"
58 |
59 | # These are the plugins we would call library
60 | self.library_extensions = (".dll", ".so", ".dylib")
61 |
62 | def load_plugins(self):
63 | """We will always use this function to load plugins
64 | in the init.py file"""
65 |
66 | # Print out that we are starting to load the plugins
67 | self.__print("Loading all plugins")
68 | added_directories = []
69 |
70 | for plugin in self.plugins:
71 | file_path = plugin.get("file_path")
72 | plugin_directory = os.path.dirname(file_path)
73 |
74 | if plugin_directory not in added_directories:
75 | nuke.pluginAddPath(plugin_directory)
76 | added_directories.append(plugin_directory)
77 |
78 | self.__print("Loaded plugins")
79 |
80 | def build_menu(self):
81 | """We will use this function in the menu.py file
82 | to build the menus in the UI"""
83 |
84 | self.__print("Adding plugins to menu")
85 |
86 | # Defining the toolbar to add menus to
87 | magic_toolbar = nuke.toolbar("Nodes")
88 |
89 | # Getting the collected plugins
90 | plugins = self.plugins
91 |
92 | # Via the create menu function we will build the folders in the menu
93 | self.__create_menus(magic_toolbar, plugins)
94 |
95 | # Via the populate menu function we will add the plugins in the menu
96 | self.__populate_menu(magic_toolbar, plugins)
97 |
98 | self.__print("Done, menu builded and populated with plugins :)")
99 |
100 | def install_plugin(self):
101 | """Using this function the user can install plugins
102 | easily via Nuke itself using a popup where the user
103 | can select the path to the plugin, select an icon
104 | and click install."""
105 |
106 | plugin_dialog = install_plugin_dialog.InstallPluginDialog()
107 | plugin_dialog.setMinimumSize(360, 380)
108 | if plugin_dialog.showModalDialog():
109 | file_path = plugin_dialog.plugin_file_path.value()
110 |
111 | # Making sure file is selected
112 | if not file_path == "":
113 | extension = os.path.splitext(file_path)[1]
114 | icon_path = plugin_dialog.icon_file_path.value()
115 | category = plugin_dialog.plugin_category.value()
116 |
117 | nuke_version = None
118 |
119 | if icon_path is "":
120 | icon_path = None
121 |
122 | if file_path.endswith((".dll", ".so", ".dylib")):
123 | nuke_version = plugin_dialog.library_nuke_version.value()
124 |
125 | # Installing plugin
126 | installation = self.__install_to_plugins(
127 | plugin_path=file_path,
128 | category=category,
129 | icon_path=icon_path,
130 | extension=extension,
131 | nuke_version=nuke_version,
132 | )
133 |
134 | nuke.message(installation)
135 | self.__print(installation)
136 |
137 | # If no file is selected, let user know
138 | else:
139 | nuke.message("Please select a plugin file to install")
140 |
141 | return
142 |
143 | @staticmethod
144 | def __print(text):
145 | message = "[MagicPlugins] %s" % text
146 | print(message)
147 |
148 | def __install_to_plugins(
149 | self,
150 | plugin_path,
151 | category,
152 | extension,
153 | icon_path=None,
154 | nuke_version=None,
155 | ):
156 | """This function contains all the logic to copy the
157 | provided tool to the correct folder and ingest it into the
158 | Nuke menu so the artist can use it."""
159 |
160 | # Extract the plugin name, so we can use it for the name in Nuke
161 | plugin_name = os.path.basename(plugin_path)
162 |
163 | # If a Nuke version is provided, we will need to build an extra folder
164 | # Other than that we will build the install directory given
165 | # the provided variables
166 | if nuke_version:
167 | install_directory = os.path.join(
168 | self.plugins_directory,
169 | "Internet",
170 | category,
171 | nuke_version,
172 | )
173 |
174 | else:
175 | install_directory = os.path.join(
176 | self.plugins_directory,
177 | "Internet",
178 | category,
179 | )
180 |
181 | # Now we will append the plugin name to complete the install path
182 | # for the plugin
183 | plugin_install_path = os.path.join(install_directory, plugin_name)
184 | plugin_install_path = plugin_install_path.replace(os.sep, "/")
185 |
186 | # If an icon path is provided, we will build the installation
187 | # path for that one too
188 | if icon_path is not None:
189 | # Basically we will just get the name from the plugin, and replace
190 | # the extension with png, so it matches the plugin name
191 | icon_name = plugin_name.replace(extension, ".png")
192 | icon_install_path = os.path.join(install_directory, icon_name)
193 | icon_install_path = icon_install_path.replace(os.sep, "/")
194 |
195 | # If a plugin already exists, ask the user if overwrite is desired
196 | if os.path.isfile(plugin_install_path):
197 | if not nuke.ask(
198 | "There is already a plugin installed in that directory, "
199 | "do you want to overwrite the file?"
200 | ):
201 | self.__print("Installation aborted")
202 | return "Installation aborted"
203 |
204 | try:
205 | # Let's copy the plugin
206 | copy2(plugin_path, plugin_install_path)
207 | if icon_path is not None:
208 | copy2(icon_path, icon_install_path)
209 |
210 | # Prevent loading if it is not the correct Nuke version
211 | if nuke_version:
212 | if not nuke_version == self.nuke_version:
213 | return "Installation successful for %s" % plugin_name
214 |
215 | # Now we will add the plugin to the menu, here
216 | # we will add the icon too
217 | self.__add_plugin_to_menu(
218 | file_path=plugin_install_path,
219 | plugin_name=plugin_name,
220 | icon_path=icon_install_path,
221 | )
222 |
223 | else:
224 |
225 | # Prevent loading if it is not the correct Nuke version
226 | if nuke_version:
227 | if not nuke_version == self.nuke_version:
228 | return "Installation successful for %s" % plugin_name
229 |
230 | # Now we will add the plugin to the menu, without icon
231 | self.__add_plugin_to_menu(
232 | file_path=plugin_install_path,
233 | plugin_name=plugin_name,
234 | )
235 |
236 | # Append the plugin path to Nuke
237 | nuke.pluginAddPath(install_directory)
238 |
239 | return "Installation successful for %s" % plugin_name
240 |
241 | # If something went wrong, we will catch the error here,
242 | # and the artist will see it
243 | except Exception as error:
244 | return "Something went wrong... %s" % str(error)
245 |
246 | def __add_plugin_to_menu(self, file_path, plugin_name, icon_path=None):
247 | """Basically the same as the populate menu function, except we
248 | won't walk through a directory to add every plugin"""
249 |
250 | # Create the toolbar to add the node command to
251 | magic_toolbar = nuke.toolbar("Nodes")
252 |
253 | # Node types where we use the createNode() function
254 | node_types = (".gizmo", ".dll", ".dylib", ".so")
255 |
256 | # Get the plugin category for the plugin given the file path
257 | # so we can build the correct name in the menu
258 | menu_name = self.__get_plugin_category(file_path)
259 |
260 | # We need to scan every directory again to see new created directories
261 | plugins = self.__locate_plugins(self.plugins_directory)
262 |
263 | # Build new menu's
264 | self.__create_menus(magic_toolbar, plugins)
265 |
266 | # If the current plugin is a node,
267 | # like we specified in the node_types variable,
268 | # build the createNode() function
269 | if file_path.endswith(node_types):
270 | magic_toolbar.addCommand(
271 | menu_name,
272 | "nuke.createNode('%s')" % plugin_name,
273 | icon=icon_path,
274 | )
275 |
276 | # If the plugin is a Nuke file, we use the nodePaste() function
277 | elif file_path.endswith(".nk"):
278 | magic_toolbar.addCommand(
279 | menu_name,
280 | "nuke.nodePaste('%s')" % file_path,
281 | icon=icon_path,
282 | )
283 |
284 | def __create_menus(self, toolbar, plugins):
285 | """Via this function we will build the folders in the menu.
286 | We could skip this function, but if we want icons,
287 | (of course we want icons!), we need to build the menu first."""
288 |
289 | # Getting the directory to scan for plugins
290 | plugins_directory = self.plugins_directory
291 |
292 | menu_name = self.menu_name
293 | menu_icon = os.path.join(
294 | self.script_directory, "resources", "icon.png"
295 | )
296 | menu_icon = menu_icon.replace(os.sep, "/")
297 |
298 | # Creating the main menu item
299 | toolbar.addMenu(menu_name, icon=menu_icon)
300 |
301 | # We are collecting the length for the directory, to only keep
302 | # the category names.
303 | # Like we have //network_drive/nuke_plugins/plugins/myplugins/folder/
304 | # we keep /plugins/myplugins/folder/
305 | plugins_directory_length = len(plugins_directory) + 1
306 |
307 | plugin_file_paths = []
308 |
309 | # We build a little dictionary for the folders we need to scan
310 | for plugin in plugins:
311 | file_path = plugin.get("file_path")
312 | plugin_file_paths.append(file_path)
313 |
314 | # Walk through the dictionary to scan every folder, and
315 | # if possible, add an icon.
316 | for root, dirs, files in os.walk(plugins_directory):
317 | for directory in sorted(dirs):
318 | directory_path = os.path.join(root, directory)
319 | directory_path = directory_path.replace(os.sep, "/")
320 |
321 | # If the folder is added in the plugin_file_paths list, add
322 | # the folder as a menu item. This makes sure no empty folders
323 | # are added.
324 | if any(directory_path in s for s in plugin_file_paths):
325 | directory_dirname = os.path.dirname(directory_path)
326 | directory_name = directory
327 |
328 | # We will build the possible icon path, and check if the
329 | # icon exists.
330 | directory_icon = directory_name + ".png"
331 | icon_path = os.path.join(directory_dirname, directory_icon)
332 | icon_path = icon_path.replace(os.sep, "/")
333 |
334 | # Here we will build the directory path to only contain the
335 | # path after the plugins folder, so we can save it as a
336 | # category path to add in the menu
337 | category = directory_path[plugins_directory_length:]
338 |
339 | category = os.path.join(menu_name, category)
340 | category = category.replace(os.sep, "/")
341 |
342 | # If the icon exists, add it, otherwise just
343 | # create a simple menu item
344 | if os.path.isfile(icon_path):
345 | toolbar.addMenu(category, icon=icon_path)
346 |
347 | else:
348 | toolbar.addMenu(category)
349 |
350 | # Adding a divider line to distinguish commands and plugins
351 | divider_name = os.path.join(menu_name, "-")
352 | divider_name = divider_name.replace(os.sep, "/")
353 | toolbar.addCommand(divider_name, "")
354 |
355 | # Add install plugin button
356 | install_plugin_name = os.path.join(menu_name, "Install plugin")
357 | install_plugin_name = install_plugin_name.replace(os.sep, "/")
358 |
359 | # Getting the icon path
360 | plugin_icon = os.path.join(
361 | self.script_directory, "resources", "install_plugin.png"
362 | )
363 | plugin_icon = plugin_icon.replace(os.sep, "/")
364 |
365 | toolbar.addCommand(
366 | install_plugin_name,
367 | "magic_plugins.install_plugin()",
368 | icon=plugin_icon,
369 | )
370 |
371 | # Add install plugin button
372 | open_folder_name = os.path.join(menu_name, "Open plugin folder")
373 | open_folder_name = open_folder_name.replace(os.sep, "/")
374 |
375 | # Getting the icon path
376 | folder_icon = os.path.join(
377 | self.script_directory, "resources", "open_folder.png"
378 | )
379 | folder_icon = folder_icon.replace(os.sep, "/")
380 |
381 | toolbar.addCommand(
382 | open_folder_name,
383 | "magic_plugins.open_folder()",
384 | icon=folder_icon,
385 | )
386 |
387 | def open_folder(self):
388 | """Via this function the user can
389 | easily open the folder where the plugins are located.
390 |
391 | It will open de file browser depending on the
392 | system the user is using.
393 | """
394 | plugin_folder = self.plugins_directory
395 | operating_system = self.operating_system
396 |
397 | if operating_system == "darwin":
398 | os.system("open %s" % plugin_folder)
399 |
400 | elif operating_system == "win32":
401 | plugin_folder = os.path.normpath(plugin_folder)
402 | os.system("explorer %s" % plugin_folder)
403 |
404 | elif operating_system == "linux":
405 | os.system('xdg-open "%s"' % plugin_folder)
406 |
407 | else:
408 | nuke.critical("Couldn't find operating system")
409 |
410 | def __populate_menu(self, magic_toolbar, plugins):
411 | """Via this function we will add all
412 | the available plugins in the menu
413 |
414 | To use this function we need a specified
415 | toolbar and a plugin dictionary.
416 | """
417 |
418 | # Iterate trough the provided dictionary to add plugins
419 | for plugin in plugins:
420 | # Get the data for the plugin necessary to build the menu item
421 | plugin_name = plugin.get("plugin_name")
422 | plugin_type = plugin.get("plugin_type")
423 | file_path = plugin.get("file_path")
424 | icon_path = plugin.get("icon_path")
425 |
426 | # Node types where we use the createNode() function
427 | node_types = ("gizmo", "dll", "dylib", "so")
428 |
429 | # Get the plugin category for the plugin given the file path
430 | # so we can build the correct name in the menu
431 | menu_name = self.__get_plugin_category(file_path)
432 |
433 | # If the current plugin is a node,
434 | # like we specified in the node_types variable,
435 | # build the createNode() function
436 | if any(s in plugin_type for s in node_types):
437 | magic_toolbar.addCommand(
438 | menu_name,
439 | "nuke.createNode('%s')" % plugin_name,
440 | icon=icon_path,
441 | )
442 |
443 | # If the plugin is a Nuke file, we use the nodePaste() function
444 | elif plugin_type == "nk":
445 | magic_toolbar.addCommand(
446 | menu_name,
447 | "nuke.nodePaste('%s')" % file_path,
448 | icon=icon_path,
449 | )
450 |
451 | def __locate_plugins(self, plugins_directory):
452 | """This function will scan the specified folder for
453 | plugins, and build a list containing all necessary information
454 | to load the plugins"""
455 |
456 | # First we create an empty list where we will add al the plugins
457 | # we want to process to load
458 | plugins = []
459 |
460 | # Here we will determine the corresponding library extension
461 | # to the current operating system
462 | operating_system = self.operating_system
463 |
464 | if operating_system == "darwin":
465 | library_extension = ".dylib"
466 |
467 | elif operating_system == "win32":
468 | library_extension = ".dll"
469 |
470 | else:
471 | library_extension = ".so"
472 |
473 | # Now we will walk through the entire
474 | # specified directory to scan for plugins
475 | for root, dirs, files in os.walk(plugins_directory):
476 | for filename in files:
477 | # First we will build the path for the plugin we might
478 | # want to append to the list
479 | file_path = os.path.join(root, filename)
480 | # Small fix which is necessary for Windows systems
481 | file_path = file_path.replace(os.sep, "/")
482 |
483 | # If the file is a gizmo or a nk file, we just go ahead and
484 | # add it to our list
485 | if file_path.endswith((".gizmo", ".nk")):
486 | # Add the plugin to the list to load
487 | plugin_information = self.__collect_plugin(file_path)
488 | plugins.append(plugin_information)
489 |
490 | # If the file is a library file (.dll, .so or .dylib),
491 | # we need to be a little bit more careful because
492 | # every library file is build for a specific version of Nuke.
493 | # So we don't want to add a library file thats build for
494 | # Nuke 12.2 when we are in Nuke 13.2
495 | elif file_path.endswith(library_extension):
496 | # Here we will validate if we want to load this plugin
497 | if self.__validate_plugin(file_path):
498 | # We want to load this plugin! Let's add it
499 | # to the list.
500 | plugin_information = self.__collect_plugin(file_path)
501 | plugins.append(plugin_information)
502 |
503 | return plugins
504 |
505 | def __validate_plugin(self, file_path):
506 | """This function will check if the plugin
507 | is ready to be loaded, or if we don't want to load it.
508 |
509 | It will check if the .dll, .so or .dylib file upper folder
510 | matches the current Nuke version.
511 |
512 | If you want to add plugins to load, always make sure to create
513 | a folder for the Nuke version where the plugins are compiled for.
514 | Like if you want to use a plugin for version 13.1,
515 | create a folder called '13.1' where you add the plugins inside."""
516 |
517 | # We will firstly assume we won't validate the plugin, unless
518 | # the plugin is indeed correct.
519 | validated = False
520 |
521 | # If the upper folder of the plugin matches the
522 | # current Nuke version (e.g 13.2) we will let the validation pass.
523 | dirname = os.path.dirname(file_path)
524 | basename = os.path.basename(dirname)
525 |
526 | if basename == str(self.nuke_version):
527 | validated = True
528 |
529 | return validated
530 |
531 | def __collect_plugin(self, file_path):
532 | """In this function we create a dictionary item for the plugin
533 | provided the file path. At the end we will return
534 | a dictionary item like this:
535 |
536 | plugin_information = {
537 | plugin_type: "gizmo",
538 | file_path: "path/to/my/MagicTool.gizmo",
539 | plugin_name: "MagicTool",
540 | icon_path: "path/to/my/MagicTool.png"
541 | }
542 |
543 |
544 | """
545 |
546 | # Get the file extension for the file, so we can
547 | # check the plugin type
548 | plugin_extension = os.path.splitext(file_path)[1]
549 | plugin_type = plugin_extension.replace(".", "")
550 |
551 | # Get the plugin name without the extension
552 | plugin_name = os.path.basename(file_path)
553 | plugin_name = os.path.splitext(plugin_name)[0]
554 |
555 | # Build the dictionary with the required data
556 | plugin_information = {
557 | "plugin_type": plugin_type,
558 | "file_path": file_path,
559 | "plugin_name": plugin_name,
560 | "icon_path": None,
561 | }
562 |
563 | # If there is an icon available, lets add it to the dictionary.
564 | icon_path = file_path.replace(plugin_extension, ".png")
565 | if os.path.isfile(icon_path):
566 | plugin_information["icon_path"] = icon_path
567 |
568 | return plugin_information
569 |
570 | def __get_plugin_category(self, file_path):
571 | """This function will detect the plugin dictionary
572 | given the file path of a plugin. This is necessary to build the
573 | correct menu name.
574 |
575 | File path: //path/to/plugins/folder/plugin.gizmo
576 | plugins directory: //path/to/plugins
577 | Category: /plugins/folder/"""
578 |
579 | # We need the plugins directory, to calculate the length
580 | # we need to strip to build the category path
581 | plugins_directory = self.plugins_directory
582 |
583 | # And of course we need the menu name to add in front of the category
584 | # because we want to add the item to our created menu
585 | menu_name = self.menu_name
586 |
587 | # Calculate the length of the path, to strip from the file path
588 | plugins_directory_length = len(plugins_directory) + 1
589 |
590 | # Here we remove the extension from the file path
591 | # because we only want the category
592 | file_path_category = os.path.splitext(file_path)[0]
593 |
594 | # Now we will strip the first part of the file path
595 | # to only keep the category
596 | category = file_path_category[plugins_directory_length:]
597 |
598 | # Finally we have to add the menu name in front of the category
599 | # because we want to add items to menu we created
600 | category = os.path.join(menu_name, category)
601 | # Small fix for Windows based systems
602 | category = category.replace(os.sep, "/")
603 |
604 | return category
605 |
--------------------------------------------------------------------------------
/MagicPlugins/menu.py:
--------------------------------------------------------------------------------
1 | """
2 | MagicPlugins by Gilles Vink
3 |
4 | Menu.py script to automatically build Nuke menu
5 |
6 | """
7 |
8 | magic_plugins.build_menu()
9 |
--------------------------------------------------------------------------------
/MagicPlugins/plugins/Internet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gillesvink/MagicPlugins/bc68b0c9d0d8f341d262136e98cc3705d0d2f1d1/MagicPlugins/plugins/Internet.png
--------------------------------------------------------------------------------
/MagicPlugins/plugins/Internet/3D.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gillesvink/MagicPlugins/bc68b0c9d0d8f341d262136e98cc3705d0d2f1d1/MagicPlugins/plugins/Internet/3D.png
--------------------------------------------------------------------------------
/MagicPlugins/plugins/Internet/3D/placeholder:
--------------------------------------------------------------------------------
1 | Place .gizmo, .nk, .dll, .so or .dylib files here
--------------------------------------------------------------------------------
/MagicPlugins/plugins/Internet/Blink.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gillesvink/MagicPlugins/bc68b0c9d0d8f341d262136e98cc3705d0d2f1d1/MagicPlugins/plugins/Internet/Blink.png
--------------------------------------------------------------------------------
/MagicPlugins/plugins/Internet/Blink/placeholder:
--------------------------------------------------------------------------------
1 | Place .gizmo, .nk, .dll, .so or .dylib files here
--------------------------------------------------------------------------------
/MagicPlugins/plugins/Internet/Channel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gillesvink/MagicPlugins/bc68b0c9d0d8f341d262136e98cc3705d0d2f1d1/MagicPlugins/plugins/Internet/Channel.png
--------------------------------------------------------------------------------
/MagicPlugins/plugins/Internet/Channel/placeholder:
--------------------------------------------------------------------------------
1 | Place .gizmo, .nk, .dll, .so or .dylib files here
--------------------------------------------------------------------------------
/MagicPlugins/plugins/Internet/Color.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gillesvink/MagicPlugins/bc68b0c9d0d8f341d262136e98cc3705d0d2f1d1/MagicPlugins/plugins/Internet/Color.png
--------------------------------------------------------------------------------
/MagicPlugins/plugins/Internet/Color/placeholder:
--------------------------------------------------------------------------------
1 | Place .gizmo, .nk, .dll, .so or .dylib files here
--------------------------------------------------------------------------------
/MagicPlugins/plugins/Internet/Deep.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gillesvink/MagicPlugins/bc68b0c9d0d8f341d262136e98cc3705d0d2f1d1/MagicPlugins/plugins/Internet/Deep.png
--------------------------------------------------------------------------------
/MagicPlugins/plugins/Internet/Deep/placeholder:
--------------------------------------------------------------------------------
1 | Place .gizmo, .nk, .dll, .so or .dylib files here
--------------------------------------------------------------------------------
/MagicPlugins/plugins/Internet/Draw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gillesvink/MagicPlugins/bc68b0c9d0d8f341d262136e98cc3705d0d2f1d1/MagicPlugins/plugins/Internet/Draw.png
--------------------------------------------------------------------------------
/MagicPlugins/plugins/Internet/Draw/placeholder:
--------------------------------------------------------------------------------
1 | Place .gizmo, .nk, .dll, .so or .dylib files here
--------------------------------------------------------------------------------
/MagicPlugins/plugins/Internet/Filter.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gillesvink/MagicPlugins/bc68b0c9d0d8f341d262136e98cc3705d0d2f1d1/MagicPlugins/plugins/Internet/Filter.png
--------------------------------------------------------------------------------
/MagicPlugins/plugins/Internet/Filter/placeholder:
--------------------------------------------------------------------------------
1 | Place .gizmo, .nk, .dll, .so or .dylib files here
--------------------------------------------------------------------------------
/MagicPlugins/plugins/Internet/Keyer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gillesvink/MagicPlugins/bc68b0c9d0d8f341d262136e98cc3705d0d2f1d1/MagicPlugins/plugins/Internet/Keyer.png
--------------------------------------------------------------------------------
/MagicPlugins/plugins/Internet/Keyer/placeholder:
--------------------------------------------------------------------------------
1 | Place .gizmo, .nk, .dll, .so or .dylib files here
--------------------------------------------------------------------------------
/MagicPlugins/plugins/Internet/Merge.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gillesvink/MagicPlugins/bc68b0c9d0d8f341d262136e98cc3705d0d2f1d1/MagicPlugins/plugins/Internet/Merge.png
--------------------------------------------------------------------------------
/MagicPlugins/plugins/Internet/Merge/placeholder:
--------------------------------------------------------------------------------
1 | Place .gizmo, .nk, .dll, .so or .dylib files here
--------------------------------------------------------------------------------
/MagicPlugins/plugins/Internet/Other.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gillesvink/MagicPlugins/bc68b0c9d0d8f341d262136e98cc3705d0d2f1d1/MagicPlugins/plugins/Internet/Other.png
--------------------------------------------------------------------------------
/MagicPlugins/plugins/Internet/Other/placeholder:
--------------------------------------------------------------------------------
1 | Place .gizmo, .nk, .dll, .so or .dylib files here
--------------------------------------------------------------------------------
/MagicPlugins/plugins/Internet/Transform.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gillesvink/MagicPlugins/bc68b0c9d0d8f341d262136e98cc3705d0d2f1d1/MagicPlugins/plugins/Internet/Transform.png
--------------------------------------------------------------------------------
/MagicPlugins/plugins/Internet/Transform/placeholder:
--------------------------------------------------------------------------------
1 | Place .gizmo, .nk, .dll, .so or .dylib files here
--------------------------------------------------------------------------------
/MagicPlugins/resources/MagicPlugins.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gillesvink/MagicPlugins/bc68b0c9d0d8f341d262136e98cc3705d0d2f1d1/MagicPlugins/resources/MagicPlugins.png
--------------------------------------------------------------------------------
/MagicPlugins/resources/header.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gillesvink/MagicPlugins/bc68b0c9d0d8f341d262136e98cc3705d0d2f1d1/MagicPlugins/resources/header.png
--------------------------------------------------------------------------------
/MagicPlugins/resources/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gillesvink/MagicPlugins/bc68b0c9d0d8f341d262136e98cc3705d0d2f1d1/MagicPlugins/resources/icon.png
--------------------------------------------------------------------------------
/MagicPlugins/resources/install_plugin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gillesvink/MagicPlugins/bc68b0c9d0d8f341d262136e98cc3705d0d2f1d1/MagicPlugins/resources/install_plugin.png
--------------------------------------------------------------------------------
/MagicPlugins/resources/installing_plugin_library.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gillesvink/MagicPlugins/bc68b0c9d0d8f341d262136e98cc3705d0d2f1d1/MagicPlugins/resources/installing_plugin_library.png
--------------------------------------------------------------------------------
/MagicPlugins/resources/installing_plugin_menu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gillesvink/MagicPlugins/bc68b0c9d0d8f341d262136e98cc3705d0d2f1d1/MagicPlugins/resources/installing_plugin_menu.png
--------------------------------------------------------------------------------
/MagicPlugins/resources/installing_plugin_ui.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gillesvink/MagicPlugins/bc68b0c9d0d8f341d262136e98cc3705d0d2f1d1/MagicPlugins/resources/installing_plugin_ui.png
--------------------------------------------------------------------------------
/MagicPlugins/resources/open_folder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gillesvink/MagicPlugins/bc68b0c9d0d8f341d262136e98cc3705d0d2f1d1/MagicPlugins/resources/open_folder.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://www.python.org/)
2 | [](https://github.com/psf/black)
3 |
4 | 
5 |
6 | ## What does it do?
7 | It scans the MagicPlugins directory for plugins, and automatically adds them according to the folder/category created.
8 |
9 | ## Which versions of Nuke are supported?
10 | * Currently Nuke 11+ is supported, tested on Linux, Mac and Windows. But earlier versions should also just work fine.
11 |
12 | ## Which files are supported?
13 | * `.gizmo`
14 | * `.nk`
15 | * `.dll` (Windows), `.so` (Linux), `.dylib` (Mac OS)
16 |
17 | ## How to install
18 | * Copy the entire contents of this repository in your `.nuke` folder.
19 | * Add the line
20 | `nuke.pluginAddPath("./MagicPlugins")`
21 | to your `init.py` file in your `.nuke` folder.
22 | * Another option is to add the environment `NUKE_PATH` to your system with the value `your/MagicPlugins/installation/folder/`.
23 |
24 | ## How to use
25 | All folders in the MagicPlugins directory are at startup scanned. If you add a gizmo in the home directory, it will be added to the menu. If you add the gizmo in a folder somewhere, all the folders will be created accordingly. This allows you to create categories.
26 |
27 | ### Installing via the GUI
28 | When using the GUI installer, the plugin will be added inside the Internet folder/category.
29 |
30 | 1. Select the Install plugin option in the MagicPlugins menu.
31 |
32 | 
33 |
34 | 2. Select the plugin you want to install, and optionally you can select an icon as well.
35 |
36 | 
37 |
38 | 3. If the file is one of the library files (`.dll`, `.so`, `.dylib`) you also need to specify the Nuke version for the selected file.
39 |
40 | 
41 |
42 |
43 | ### Manually installing (multiple files)
44 | All the files that are loaded in startup are located in the plugins folder inside the MagicPlugin folder.
45 |
46 | Every item that is added inside this folder will be added automatically to Nuke. So if you want to add a gizmo or a Nuke script file, just copy and paste it into there.
47 |
48 | ### Installing library files (`.dll`, `.so`, `.dylib`)
49 | Because library files are compiled for every Nuke version specifically, you don't want to load a plugin compiled for Nuke 12.2 if you are in 13.0. Using MagicPlugins it's possible to load library files for the correct Nuke version, and skip the others.
50 | * When adding library files, create a folder named with the target Nuke version. So for example, if I want to add a plugin called myPlugin.dll for `Nuke 13.0`, it needs to be added like `myLibraryPluginsCategory/13.0/myPlugin.dll`.
51 | * If you want to add the plugin for `Nuke 12.2`, it needs to be added like `myLibraryPluginsCategory/12.2/myPlugin.dll`, and so on
52 |
--------------------------------------------------------------------------------
/init.py:
--------------------------------------------------------------------------------
1 | import nuke
2 |
3 | nuke.pluginAddPath("./MagicPlugins")
4 |
--------------------------------------------------------------------------------