Compare commits
20 Commits
main
...
maintenanc
Author | SHA1 | Date |
---|---|---|
Jack | fbe1a339e1 | |
Jack | d1171829dc | |
Jack | 571a600964 | |
Jack | 98dbfd4603 | |
Jack | b58b2ef422 | |
Jack | 796cff48c8 | |
Jack | b37a541ce3 | |
Jack | 6ee4da15fb | |
Jack | b70649665c | |
Jack | 5c2b05f415 | |
Jack | 3b40f1fc9a | |
Jack Christensen | 1197b3a71c | |
Jack Christensen | 6792704469 | |
Jack Christensen | 52ceabda0c | |
Jack | 92c724e3f2 | |
Jack | 356b2db051 | |
Jack Christensen | 6965e5867e | |
Jack Christensen | 2ee110aee2 | |
Jack Christensen | 4ab72559a8 | |
Jack | 185632063b |
14
LICENSE
14
LICENSE
|
@ -672,3 +672,17 @@ may consider it more useful to permit linking proprietary applications with
|
|||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||
|
||||
---------------------------------------
|
||||
|
||||
Third-Party Licenses
|
||||
|
||||
This project includes or relies on the following third-party libraries, which are licensed separately:
|
||||
|
||||
- FFmpeg: This project uses code from FFmpeg, licensed under the GPLv3 (https://ffmpeg.org/legal.html). FFmpeg’s source code is available at https://ffmpeg.org/download.html.
|
||||
- Qt6: This project uses Qt, which is licensed under the LGPLv3 (https://www.qt.io/licensing).
|
||||
- PySide6: The Python bindings for Qt, PySide6, are licensed under the LGPLv3 (https://doc.qt.io/qtforpython/licenses.html).
|
||||
- Shiboken6: Shiboken, used to create Python bindings, is licensed under the LGPLv3 (https://doc.qt.io/qtforpython/shiboken6/).
|
||||
|
||||
For more details, see the respective licenses included with these libraries.
|
||||
|
||||
|
|
17
README.md
17
README.md
|
@ -1,9 +1,13 @@
|
|||
# Tinyblast v1.0.0
|
||||
# Tinyblast v1.0.1
|
||||
|
||||
# **Important!**
|
||||
|
||||
FFmpeg is no longer bundled with Tinyblast directly in the source. In order to download the plugin with FFmpeg please go to the [release page on this repo](https://git.jackmchristensen.com/jack/tinyblast/-/releases/v1.0.0) and download tinyblast_v1.0.0.zip or download it [here](https://drive.proton.me/urls/JVV34W2H3R#VOJzkesz0Cgq). If you only download the source, the plugin **will not work**.
|
||||
|
||||
## Version 1.0.1 Hotfix
|
||||
|
||||
There was an issue in v1.0.0 that would cause Maya to hang on 'Loading plugins (lookdevXmaya)' when booting. Removing Tinyblast from the plug-ins directory temporarily would fix it for a time, but it would inevitably happen again. v1.0.1 aims to remove the issue entirely. If the issue still arises please contact me and let me know and I'll keep working on it.
|
||||
|
||||
## Overview
|
||||
|
||||
Tinyblast is a lightweight plugin designed to enhance Maya’s playblast workflow by converting uncompressed `.avi` files to compressed `.mp4` files using the efficient H.264 codec. It adds a custom button to Maya's Playblast Options window, enabling users to save playblasts in the more storage-friendly `.mp4` format, significantly reducing file sizes without sacrificing quality.
|
||||
|
@ -33,8 +37,13 @@ Tinyblast is a lightweight plugin designed to enhance Maya’s playblast workflo
|
|||
|
||||
This project is licensed under the GPLv3 license.
|
||||
|
||||
It also includes FFmpeg, which is licensed under GPLv3. You can download the FFmpeg source code from [https://ffmpeg.org/download.html](https://ffmpeg.org/download.html).
|
||||
## Acknowledgments
|
||||
|
||||
## FFmpeg Usage and Legal Compliance
|
||||
This project uses the following third-party libraries:
|
||||
|
||||
Tinyblast uses the FFmpeg library to handle the conversion of `.avi` files to `.mp4`. FFmpeg is licensed under the GPLv3 license, and its source code is available for download at [FFmpeg Source](https://ffmpeg.org/download.html).
|
||||
- **FFmpeg**: Licensed under the [GNU General Public License (GPL)](https://ffmpeg.org/legal.html). FFmpeg is a powerful multimedia framework used in this project for video conversion.
|
||||
- **Qt6**: Licensed under the [GNU Lesser General Public License (LGPL) v3](https://www.qt.io/licensing). Qt is used for building the user interface of this project.
|
||||
- **PySide6**: Licensed under the [GNU Lesser General Public License (LGPL) v3](https://doc.qt.io/qtforpython/licenses.html). PySide6 provides Python bindings for the Qt framework.
|
||||
- **Shiboken6**: Licensed under the [GNU Lesser General Public License (LGPL) v3](https://doc.qt.io/qtforpython/shiboken6/). Shiboken6 is used for generating Python bindings for Qt.
|
||||
|
||||
Please refer to the respective licenses for each library for more information.
|
||||
|
|
84
tinyblast.py
84
tinyblast.py
|
@ -1,13 +1,16 @@
|
|||
import maya.OpenMaya as om
|
||||
import maya.OpenMayaMPx as ompx
|
||||
import maya.OpenMayaUI as omui
|
||||
import maya.cmds as cmds
|
||||
import maya.utils as utils
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
# Global variable to store the scriptJob ID
|
||||
from shiboken6 import wrapInstance
|
||||
from PySide6 import QtWidgets
|
||||
|
||||
playblast_job_id = None
|
||||
original_playblast = cmds.playblast
|
||||
|
||||
def get_plugin_directory():
|
||||
# Get the path of the currently loaded plugin
|
||||
|
@ -23,7 +26,7 @@ def custom_playblast(*args, **kwargs):
|
|||
kwargs['quality'] = 100
|
||||
kwargs['widthHeight'] = (1920, 1080)
|
||||
|
||||
result = original_playblast(*args, **kwargs)
|
||||
result = cmds.playblast(*args, **kwargs)
|
||||
print(f"{result}")
|
||||
|
||||
if result:
|
||||
|
@ -36,10 +39,11 @@ def custom_playblast(*args, **kwargs):
|
|||
input_file = result # 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))
|
||||
scene_name = os.path.basename(cmds.file(query=True, sceneName=True))
|
||||
input_filename = os.path.basename(result) # Get the filename with extension
|
||||
|
||||
# Change the extension to .mp4
|
||||
output_filename = os.path.splitext(input_filename)[0] + ".mp4"
|
||||
output_filename = os.path.splitext(scene_name)[0] + ".mp4"
|
||||
|
||||
# Define the full path for the converted output file
|
||||
output_file = os.path.join(output_directory, output_filename)
|
||||
|
@ -59,6 +63,15 @@ def custom_playblast(*args, **kwargs):
|
|||
except subprocess.CalledProcessError as e:
|
||||
print(f"Error during FFmpeg conversion: {e}")
|
||||
|
||||
def get_playblast_options_window():
|
||||
# Check if the Playblast Options window is open
|
||||
windows = cmds.lsUI(windows=True)
|
||||
for window in windows:
|
||||
if cmds.window(window, query=True, title=True) == "Playblast Options": # Exact title match
|
||||
window_ptr = omui.MQtUtil.findWindow(window)
|
||||
return wrapInstance(int(window_ptr), QtWidgets.QWidget)
|
||||
return None
|
||||
|
||||
class WindowWatcher:
|
||||
""" A class to watch for a particular window in Maya """
|
||||
|
||||
|
@ -70,17 +83,35 @@ class WindowWatcher:
|
|||
|
||||
def check_for_window_open(self):
|
||||
if not self.window_opened:
|
||||
window = self.get_window_by_title(self.window_title)
|
||||
window = get_playblast_options_window()
|
||||
if window:
|
||||
self.on_open_callback()
|
||||
self.window_opened = True
|
||||
else:
|
||||
window = self.get_window_by_title(self.window_title)
|
||||
window = get_playblast_options_window()
|
||||
if not window:
|
||||
self.window_opened = False
|
||||
if self.on_close_callback:
|
||||
self.on_close_callback()
|
||||
|
||||
def find_button_by_label(window, label_text):
|
||||
for widget in window.findChildren(QtWidgets.QPushButton):
|
||||
if widget.text() == label_text:
|
||||
return widget
|
||||
return None
|
||||
|
||||
def override_playblast_button():
|
||||
playblast_window = get_playblast_options_window()
|
||||
tinyblast_button = find_button_by_label(playblast_window, 'Playblast')
|
||||
|
||||
if tinyblast_button:
|
||||
tinyblast_button.clicked.disconnect()
|
||||
tinyblast_button.setText('Tinyblast')
|
||||
tinyblast_button.clicked.connect(custom_playblast)
|
||||
|
||||
|
||||
def setup_script_job():
|
||||
global playblast_job_id
|
||||
def get_window_by_title(self, title):
|
||||
# Check all open windows and return the one that matches the title
|
||||
windows = cmds.lsUI(windows=True)
|
||||
|
@ -107,31 +138,34 @@ def add_custom_button_to_playblast():
|
|||
else:
|
||||
print("Playblast Options window not found.")
|
||||
|
||||
|
||||
def custom_button_action(*args):
|
||||
cmds.playblast()
|
||||
|
||||
custom_playblast()
|
||||
|
||||
def get_playblast_options_window():
|
||||
# Check if the Playblast Options window is open
|
||||
windows = cmds.lsUI(windows=True)
|
||||
for window in windows:
|
||||
if cmds.window(window, query=True, title=True) == "Playblast Options": # Exact title match
|
||||
return window
|
||||
if cmds.window(window, query=True, title=True) == "Playblast Options":
|
||||
window_ptr = omui.MQtUtil.findWindow(window)
|
||||
return wrapInstance(int(window_ptr), QtWidgets.QWidget)
|
||||
return None
|
||||
|
||||
def open_script_job():
|
||||
cmds.scriptJob(event=["SceneOpened", setup_script_job])
|
||||
|
||||
def setup_script_job():
|
||||
global playblast_job_id
|
||||
|
||||
# Kill any previously running scriptJob
|
||||
if playblast_job_id is not None and cmds.scriptJob(exists=playblast_job_id):
|
||||
cmds.scriptJob(kill=playblast_job_id, force=True)
|
||||
print(f"Killed previous scriptJob with ID: {playblast_job_id}")
|
||||
|
||||
print("Created scriptjob")
|
||||
|
||||
# Watch for the Playblast Options window by title
|
||||
playblast_watcher = WindowWatcher(
|
||||
window_title="Playblast Options", # Exact window title to look for
|
||||
on_open_callback=add_custom_button_to_playblast
|
||||
on_open_callback=override_playblast_button
|
||||
)
|
||||
|
||||
# Set up a new scriptJob
|
||||
|
@ -142,29 +176,35 @@ class Tinyblast(ompx.MPxCommand):
|
|||
ompx.MPxCommand.__init__(self)
|
||||
|
||||
def doIt(selfself, args):
|
||||
print("Executing custom playblast command.")
|
||||
cmds.playblast()
|
||||
print("So I started blastin'.")
|
||||
custom_playblast()
|
||||
|
||||
@staticmethod
|
||||
def cmdCreator():
|
||||
return ompx.asMPxPtr(Tinyblast())
|
||||
def tinyblast_cmd():
|
||||
print("Executing custom playblast command.")
|
||||
custom_playblast()
|
||||
|
||||
def tinyblastCmd():
|
||||
return ompx.asMPxPtr(Tinyblast())
|
||||
|
||||
def initializePlugin(mobject):
|
||||
global playblast_job_id
|
||||
try:
|
||||
mplugin = ompx.MFnPlugin(mobject, "Jack Christensen", "1.0.0", "Any")
|
||||
mplugin.registerCommand("tinyblast", Tinyblast.cmdCreator)
|
||||
mplugin = ompx.MFnPlugin(mobject, "Jack Christensen", "1.1.0", "Any")
|
||||
mplugin.registerCommand("tinyblast", tinyblast_cmd)
|
||||
om.MGlobal.displayInfo("Tinyblast plugin loaded.")
|
||||
setup_script_job()
|
||||
cmds.playblast = custom_playblast
|
||||
open_script_job()
|
||||
except Exception as e:
|
||||
om.MGlobal.displayError(f"Failed to initialize plugin: {str(e)}")
|
||||
raise
|
||||
|
||||
def uninitializePlugin(mobject):
|
||||
global playblast_job_id
|
||||
try:
|
||||
mplugin = ompx.MFnPlugin(mobject)
|
||||
mplugin.deregisterCommand("tinyblast")
|
||||
om.MGlobal.displayInfo("Tinyblast plugin unloaded.")
|
||||
cmds.scriptJob(kill=playblast_job_id, force=True)
|
||||
print("Killed script job")
|
||||
except Exception as e:
|
||||
om.MGlobal.displayError(f"Failed to uninitialize plugin: {str(e)}")
|
||||
raise
|
Loading…
Reference in New Issue