Tinyblast GUI is functional
Still needs some work, .mov is giving me issues with ProRes, DNxHD, and DNxHR, and some settings currently don't do anything. But it works for simple playblasts!!!
This commit is contained in:
parent
0b6f65becc
commit
936e914ffb
196
tinyblast.py
196
tinyblast.py
|
@ -18,6 +18,7 @@ import shiboken6
|
||||||
# Global variable to store the scriptJob ID
|
# Global variable to store the scriptJob ID
|
||||||
playblast_job_id = None
|
playblast_job_id = None
|
||||||
original_playblast = cmds.playblast
|
original_playblast = cmds.playblast
|
||||||
|
tinyblast_instance = None
|
||||||
|
|
||||||
def get_plugin_directory():
|
def get_plugin_directory():
|
||||||
# Get the path of the currently loaded plugin
|
# Get the path of the currently loaded plugin
|
||||||
|
@ -25,49 +26,49 @@ def get_plugin_directory():
|
||||||
plugin_path = cmds.pluginInfo(plugin_name, query=True, path=True)
|
plugin_path = cmds.pluginInfo(plugin_name, query=True, path=True)
|
||||||
return os.path.dirname(plugin_path)
|
return os.path.dirname(plugin_path)
|
||||||
|
|
||||||
def custom_playblast(*args, **kwargs):
|
# def custom_playblast(*args, **kwargs):
|
||||||
print("Running tinyblast...")
|
# print("Running tinyblast...")
|
||||||
|
#
|
||||||
kwargs['format'] = 'avi'
|
# kwargs['format'] = 'avi'
|
||||||
kwargs['percent'] = 100
|
# kwargs['percent'] = 100
|
||||||
kwargs['quality'] = 100
|
# kwargs['quality'] = 100
|
||||||
kwargs['widthHeight'] = (1920, 1080)
|
# kwargs['widthHeight'] = (1920, 1080)
|
||||||
|
#
|
||||||
result = original_playblast(*args, **kwargs)
|
# result = original_playblast(*args, **kwargs)
|
||||||
print(f"{result}")
|
# print(f"{result}")
|
||||||
|
#
|
||||||
if result:
|
# if result:
|
||||||
try:
|
# try:
|
||||||
ffmpeg_path = os.path.join(get_plugin_directory(), 'ffmpeg.exe')
|
# ffmpeg_path = os.path.join(get_plugin_directory(), 'ffmpeg.exe')
|
||||||
print(f"ffmpeg path: {ffmpeg_path}")
|
# print(f"ffmpeg path: {ffmpeg_path}")
|
||||||
if not os.path.exists(ffmpeg_path):
|
# if not os.path.exists(ffmpeg_path):
|
||||||
raise FileNotFoundError(f"FFmpeg binary not found at {ffmpeg_path}")
|
# raise FileNotFoundError(f"FFmpeg binary not found at {ffmpeg_path}")
|
||||||
|
#
|
||||||
input_file = result # The file output by playblast
|
# input_file = result # The file output by playblast
|
||||||
#output_directory = os.path.dirname(result) # Get the directory path
|
# #output_directory = os.path.dirname(result) # Get the directory path
|
||||||
output_directory = os.path.dirname(cmds.file(query=True, sceneName=True))
|
# output_directory = os.path.dirname(cmds.file(query=True, sceneName=True))
|
||||||
input_filename = os.path.basename(result) # Get the filename with extension
|
# input_filename = os.path.basename(result) # Get the filename with extension
|
||||||
|
#
|
||||||
# Change the extension to .mp4
|
# # Change the extension to .mp4
|
||||||
output_filename = os.path.splitext(input_filename)[0] + ".mp4"
|
# output_filename = os.path.splitext(input_filename)[0] + ".mp4"
|
||||||
|
#
|
||||||
# Define the full path for the converted output file
|
# # Define the full path for the converted output file
|
||||||
output_file = os.path.join(output_directory, output_filename)
|
# output_file = os.path.join(output_directory, output_filename)
|
||||||
|
#
|
||||||
# Run FFmpeg conversion
|
# # Run FFmpeg conversion
|
||||||
subprocess.run([ffmpeg_path,
|
# subprocess.run([ffmpeg_path,
|
||||||
'-i', input_file,
|
# '-i', input_file,
|
||||||
'-vcodec', 'libx264',
|
# '-vcodec', 'libx264',
|
||||||
'-pix_fmt', 'yuv420p',
|
# '-pix_fmt', 'yuv420p',
|
||||||
'-strict', 'experimental',
|
# '-strict', 'experimental',
|
||||||
'-b:v', '1m',
|
# '-b:v', '1m',
|
||||||
output_file,
|
# output_file,
|
||||||
'-y'], check=True, shell=True)
|
# '-y'], check=True, shell=True)
|
||||||
print(f"Video conversion to {output_file} successful!")
|
# print(f"Video conversion to {output_file} successful!")
|
||||||
# os.remove(input_file) # Running into permission issues trying to delete from AppData
|
# # os.remove(input_file) # Running into permission issues trying to delete from AppData
|
||||||
# print(f"Original playblast deleted: {input_file}")
|
# # print(f"Original playblast deleted: {input_file}")
|
||||||
except subprocess.CalledProcessError as e:
|
# except subprocess.CalledProcessError as e:
|
||||||
print(f"Error during FFmpeg conversion: {e}")
|
# print(f"Error during FFmpeg conversion: {e}")
|
||||||
|
|
||||||
class WindowWatcher:
|
class WindowWatcher:
|
||||||
""" A class to watch for a particular window in Maya """
|
""" A class to watch for a particular window in Maya """
|
||||||
|
@ -150,6 +151,44 @@ def setup_script_job():
|
||||||
class Tinyblast(ompx.MPxCommand):
|
class Tinyblast(ompx.MPxCommand):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
ompx.MPxCommand.__init__(self)
|
ompx.MPxCommand.__init__(self)
|
||||||
|
self.format = 'mp4'
|
||||||
|
self.codec = 'libx265'
|
||||||
|
self.bitrate = '5m'
|
||||||
|
self.pixel_format = 'yuv420p'
|
||||||
|
self.resolution = (cmds.getAttr("defaultResolution.width"), cmds.getAttr("defaultResolution.height"))
|
||||||
|
self.percent = '100'
|
||||||
|
self.path = ''
|
||||||
|
|
||||||
|
def apply_settings(self, format, codec, quality, resolution, scale, file_path):
|
||||||
|
self.format = format
|
||||||
|
self.codec = self.get_codec(codec)
|
||||||
|
self.bitrate = f"{round(8 * (resolution[0] * resolution[1]) / (1920 * 1080) * (float(quality) / 100), 1)}m"
|
||||||
|
self.resolution = resolution
|
||||||
|
self.percent = int(scale * 100)
|
||||||
|
self.path = file_path
|
||||||
|
|
||||||
|
def get_codec(self, pretty_name):
|
||||||
|
if pretty_name == 'HEVC (H.265)':
|
||||||
|
return 'libx265'
|
||||||
|
if pretty_name == 'H.264':
|
||||||
|
return 'libx264'
|
||||||
|
if pretty_name == 'AV1':
|
||||||
|
return 'libaom-av1'
|
||||||
|
if pretty_name == 'MPEG-4':
|
||||||
|
return 'mpeg4'
|
||||||
|
if pretty_name == 'P8':
|
||||||
|
return 'libvpx'
|
||||||
|
if pretty_name == 'VP9':
|
||||||
|
return 'libvpx-vp9'
|
||||||
|
if pretty_name == 'Theora':
|
||||||
|
return 'libtheora'
|
||||||
|
if pretty_name == 'DNxHD':
|
||||||
|
return 'dnxhd'
|
||||||
|
if pretty_name == 'DNxHR':
|
||||||
|
return 'dnxhr'
|
||||||
|
if pretty_name == 'Motion JPEG':
|
||||||
|
return 'mjpeg'
|
||||||
|
return 'Not a codec'
|
||||||
|
|
||||||
def doIt(selfself, args):
|
def doIt(selfself, args):
|
||||||
print("Tinyblasting...")
|
print("Tinyblasting...")
|
||||||
|
@ -159,6 +198,54 @@ class Tinyblast(ompx.MPxCommand):
|
||||||
def cmdCreator():
|
def cmdCreator():
|
||||||
return ompx.asMPxPtr(Tinyblast())
|
return ompx.asMPxPtr(Tinyblast())
|
||||||
|
|
||||||
|
def custom_playblast(self, *args, **kwargs):
|
||||||
|
print("Running tinyblast...")
|
||||||
|
|
||||||
|
kwargs['format'] = 'avi'
|
||||||
|
kwargs['percent'] = int(self.percent)
|
||||||
|
kwargs['quality'] = 100
|
||||||
|
kwargs['widthHeight'] = self.resolution
|
||||||
|
|
||||||
|
result = original_playblast(*args, **kwargs)
|
||||||
|
print(f"{result}")
|
||||||
|
|
||||||
|
if result:
|
||||||
|
self.blastIt(result)
|
||||||
|
|
||||||
|
def blastIt(self, input_path):
|
||||||
|
try:
|
||||||
|
ffmpeg_path = os.path.join(get_plugin_directory(), 'ffmpeg.exe')
|
||||||
|
print(f"ffmpeg path: {ffmpeg_path}")
|
||||||
|
if not os.path.exists(ffmpeg_path):
|
||||||
|
raise FileNotFoundError(f"FFmpeg binary not found at {ffmpeg_path}")
|
||||||
|
|
||||||
|
input_file = input_path # The file output by playblast
|
||||||
|
#output_directory = os.path.dirname(result) # Get the directory path
|
||||||
|
output_directory = os.path.dirname(cmds.file(query=True, sceneName=True))
|
||||||
|
input_filename = os.path.basename(input_path) # Get the filename with extension
|
||||||
|
|
||||||
|
# Change the extension to .mp4
|
||||||
|
output_filename = os.path.splitext(input_filename)[0] + ".mp4"
|
||||||
|
|
||||||
|
# Define the full path for the converted output file
|
||||||
|
output_file = self.path
|
||||||
|
print(f"Output path: {output_file}")
|
||||||
|
|
||||||
|
# Run FFmpeg conversion
|
||||||
|
subprocess.run([ffmpeg_path,
|
||||||
|
'-i', input_file,
|
||||||
|
'-vcodec', self.codec,
|
||||||
|
'-pix_fmt', self.pixel_format,
|
||||||
|
'-strict', 'experimental',
|
||||||
|
'-b:v', self.bitrate,
|
||||||
|
output_file,
|
||||||
|
'-y'], check=True, shell=True)
|
||||||
|
print(f"Video conversion to {output_file} successful!")
|
||||||
|
# os.remove(input_file) # Running into permission issues trying to delete from AppData
|
||||||
|
# print(f"Original playblast deleted: {input_file}")
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
print(f"Error during FFmpeg conversion: {e}")
|
||||||
|
|
||||||
def get_maya_window():
|
def get_maya_window():
|
||||||
import maya.OpenMayaUI as omui
|
import maya.OpenMayaUI as omui
|
||||||
from PySide6 import QtWidgets
|
from PySide6 import QtWidgets
|
||||||
|
@ -228,9 +315,10 @@ class TinyblastOptionsWindow(QMainWindow):
|
||||||
# MOV
|
# MOV
|
||||||
if index == 2:
|
if index == 2:
|
||||||
self.ui.encodingComboBox.addItems([
|
self.ui.encodingComboBox.addItems([
|
||||||
QCoreApplication.translate("TinyblastOptions", u"Apple ProRes", None),
|
|
||||||
QCoreApplication.translate("TinyblastOptions", u"HEVC (H.265)", None),
|
QCoreApplication.translate("TinyblastOptions", u"HEVC (H.265)", None),
|
||||||
QCoreApplication.translate("TinyblastOptions", u"H.264", None),
|
QCoreApplication.translate("TinyblastOptions", u"H.264", None),
|
||||||
|
QCoreApplication.translate("TinyblastOptions", u"DNxHD", None),
|
||||||
|
QCoreApplication.translate("TinyblastOptions", u"DNxHR", None),
|
||||||
QCoreApplication.translate("TinyblastOptions", u"MPEG-4", None)
|
QCoreApplication.translate("TinyblastOptions", u"MPEG-4", None)
|
||||||
])
|
])
|
||||||
|
|
||||||
|
@ -242,8 +330,6 @@ class TinyblastOptionsWindow(QMainWindow):
|
||||||
self.ui.encodingComboBox.addItems([
|
self.ui.encodingComboBox.addItems([
|
||||||
QCoreApplication.translate("TinyblastOptions", u"H.264", None),
|
QCoreApplication.translate("TinyblastOptions", u"H.264", None),
|
||||||
QCoreApplication.translate("TinyblastOptions", u"MPEG-4", None),
|
QCoreApplication.translate("TinyblastOptions", u"MPEG-4", None),
|
||||||
QCoreApplication.translate("TinyblastOptions", u"DivX", None),
|
|
||||||
QCoreApplication.translate("TinyblastOptions", u"Xvid", None),
|
|
||||||
QCoreApplication.translate("TinyblastOptions", u"Motion JPEG", None),
|
QCoreApplication.translate("TinyblastOptions", u"Motion JPEG", None),
|
||||||
])
|
])
|
||||||
|
|
||||||
|
@ -264,10 +350,20 @@ class TinyblastOptionsWindow(QMainWindow):
|
||||||
|
|
||||||
def tinyblast(self):
|
def tinyblast(self):
|
||||||
print("Tinyblasting...")
|
print("Tinyblasting...")
|
||||||
cmds.playblast()
|
self.apply_settings()
|
||||||
|
tinyblast_instance.custom_playblast()
|
||||||
|
|
||||||
def apply_settings(self):
|
def apply_settings(self):
|
||||||
print("TODO")
|
# Directly call the `apply_settings` method of the Tinyblast instance
|
||||||
|
if tinyblast_instance is not None:
|
||||||
|
tinyblast_instance.apply_settings(
|
||||||
|
self.ui.formattingComboBox.currentText(), # Use the selected format
|
||||||
|
self.ui.encodingComboBox.currentText(), # Use the selected codec
|
||||||
|
self.ui.qualitySpinBox.value(), # Quality as a percentage
|
||||||
|
(self.ui.widthSpinBox.value(), self.ui.heightSpinBox.value()), # Resolution
|
||||||
|
self.ui.scaleSpinBox.value(), # Scale
|
||||||
|
self.ui.filePathTextBox.text() # File path
|
||||||
|
)
|
||||||
|
|
||||||
def quit_window(self):
|
def quit_window(self):
|
||||||
tb_window.close()
|
tb_window.close()
|
||||||
|
@ -342,13 +438,15 @@ def cmdCreator():
|
||||||
return ompx.asMPxPtr(MyPluginCommand())
|
return ompx.asMPxPtr(MyPluginCommand())
|
||||||
|
|
||||||
def initializePlugin(mobject):
|
def initializePlugin(mobject):
|
||||||
|
global tinyblast_instance
|
||||||
|
tinyblast_instance = Tinyblast()
|
||||||
try:
|
try:
|
||||||
mplugin = ompx.MFnPlugin(mobject, "Jack Christensen", "1.0.0", "Any")
|
mplugin = ompx.MFnPlugin(mobject, "Jack Christensen", "1.0.0", "Any")
|
||||||
mplugin.registerCommand("tinyblast", Tinyblast.cmdCreator)
|
mplugin.registerCommand("tinyblast", Tinyblast.cmdCreator)
|
||||||
mplugin.registerCommand("myPluginCommand", cmdCreator)
|
mplugin.registerCommand("myPluginCommand", cmdCreator)
|
||||||
om.MGlobal.displayInfo("Tinyblast plugin loaded.")
|
om.MGlobal.displayInfo("Tinyblast plugin loaded.")
|
||||||
setup_script_job()
|
setup_script_job()
|
||||||
cmds.playblast = custom_playblast
|
cmds.playblast = tinyblast_instance.custom_playblast
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
om.MGlobal.displayError(f"Failed to initialize plugin: {str(e)}")
|
om.MGlobal.displayError(f"Failed to initialize plugin: {str(e)}")
|
||||||
raise
|
raise
|
||||||
|
|
Loading…
Reference in New Issue