Designer Plugins
Plugins for Disguise Designer software.
Plugins for Disguise Designer software.
This guide will help you get started with the Designer Plugin system by creating a simple “Hello World” plugin. This plugin will include HTML metatags for window size and a button that sends a POST request to the execute
endpoint with a simple Python command.
First, within the project {project_root}/plugins
directory, create a directory for your plugin. For this example, we’ll name it hello_world_plugin
.
Inside the hello_world_plugin
directory, create an index.html
file. This file will define the plugin’s user interface.
<!DOCTYPE html>
<html>
<head>
<title>Hello World Plugin</title>
<!-- Window size metadata -->
<meta name="disguise-plugin-window-size" content="512,512">
<meta name="disguise-plugin-window-min-size" content="200,200">
<meta name="disguise-plugin-window-resizable" content="true">
<style>
body {
font-family: Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
}
.container {
text-align: center;
}
button {
padding: 10px 20px;
font-size: 16px;
}
</style>
</head>
<body>
<script>
async function executeCommand() {
const urlParams = new URLSearchParams(window.location.search);
const director = urlParams.get('director') || "localhost";
const apiUrl = `http://${director}/api/session/python/execute`;
try {
const response = await fetch(apiUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
script: `
layer = guisystem.track.addNewLayer(TextModule, trackTime(), 60, 'Text - Hello World')
layer.findSequence('text').sequence.setString(0, "Hello world")
`
})
});
const result = await response.json();
console.log('Response:', result);
} catch (error) {
console.error('Error:', error);
}
}
</script>
<div class="container">
<h1>Hello World Plugin</h1>
<button onclick="executeCommand()">Add Hello World layer</button>
</div>
</body>
</html>
Let’s break down the key components of this plugin:
<meta name="disguise-plugin-window-size" content="512,512">
<meta name="disguise-plugin-window-min-size" content="200,200">
<meta name="disguise-plugin-window-resizable" content="true">
These meta tags control the plugin window’s size and behaviour in Designer:
window-size
: Sets the initial window size to 512x512 pixelswindow-min-size
: Sets the minimum window size to 200x200 pixelswindow-resizable
: Allows the window to be resized by the userThe plugin communicates with Designer through the /api/session/python/execute
endpoint. The request body contains:
{
"script": "Python code here"
}
The example uses a simple command from the useful snippets to add a text layer at the current track time, and set its first keyframe to the string Hello World
:
layer = guisystem.track.addNewLayer(TextModule, trackTime(), 60, 'Text - Hello World')
layer.findSequence('text').sequence.setString(0, "Hello world")
hello_world_plugin
directoryThis is a basic example that demonstrates the core concepts of Designer plugins. You can expand this by:
For more advanced examples and API documentation, check out the Function Calling and Useful Python Snippets pages.
While the basic HTML/JavaScript approach works well for simple plugins, using a modern framework like Vue.js can significantly improve your development experience. Disguise provides several official libraries to help you build more sophisticated plugins:
npm create vite@latest my-designer-plugin -- --template vue
cd my-designer-plugin
npm install
# Install the designer-pythonapi directly from GitHub
npm install disguise-one/designer-pythonapi
# Install the Vue.js Live Update composable
npm install disguise-one/vue-liveupdate
vite.config.js
:import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { designerPythonLoader } from '@disguise-one/designer-pythonapi/vite-loader'
export default defineConfig({
plugins: [
vue(),
designerPythonLoader()
],
})
index.html
:<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- Designer Plugin meta tags -->
<meta name="disguise-plugin-window-size" content="512,512">
<meta name="disguise-plugin-window-min-size" content="200,200">
<meta name="disguise-plugin-window-resizable" content="true">
<title>Designer Plugin</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>
src/hello_world.py
):__all__ = ["addTextLayer"]
def addTextLayer():
layer = guisystem.track.addNewLayer(TextModule, LocalState.localState().currentTransport.player.tCurrent, 60, 'Text')
layer.findSequence('text').sequence.setString(0, "Hello world")
return True
Note : This Python is slightly different to the HTML example above where the Python is run with the execute
endpoint giving access to the utility method trackTime()
which is not available when registering modules.
src/components/TextLayerControl.vue
which will handle Python execution:<template>
<div class="text-layer-section">
<h2>Text Layer Control</h2>
<button @click="handleAddTextLayer">Add Hello World</button>
</div>
</template>
<script setup>
import { hello_world } from '../hello_world.py'
// Extract the director endpoint from the URL query parameters
const urlParams = new URLSearchParams(window.location.search)
const directorEndpoint = urlParams.get('director') || 'localhost:80' // Fallback for development
// Initialize the Python bindings composable
const module = hello_world(directorEndpoint)
// Feedback about registration
module.registration.then((reg) => {
console.log('Hello World module registered', reg)
}).catch((error) => {
console.error('Error registering Hello World module:', error)
})
async function handleAddTextLayer() {
try {
await module.addTextLayer()
} catch (error) {
console.error('Error:', error)
}
}
</script>
<style scoped>
.text-layer-section {
margin: 1rem;
padding: 1rem;
border: 1px solid #ccc;
border-radius: 4px;
}
button {
padding: 8px 16px;
font-size: 14px;
cursor: pointer;
}
</style>
src/components/PlayheadDisplay.vue
which will handle real-time updates from Designer:<template>
<div class="playhead-section">
<h2>Playhead Position</h2>
<div class="playhead-value">{{ player_tRender !== undefined ? player_tRender.toFixed(2) : '0.00' }}s</div>
</div>
</template>
<script setup>
// Define the liveUpdate prop
const props = defineProps({
liveUpdate: {
type: Object,
required: true
}
})
// Auto-subscribe to playhead position
const { player_tRender } = props.liveUpdate.autoSubscribe('transportManager:default', ['object.player.tRender'])
</script>
<style scoped>
.playhead-section {
margin: 1rem;
padding: 1rem;
border: 1px solid #ccc;
border-radius: 4px;
}
.playhead-value {
font-size: 1.2rem;
font-weight: bold;
}
</style>
src/App.vue
to use these components:<template>
<div class="app">
<h1>Designer Plugin</h1>
<!-- Text Layer Control Component -->
<TextLayerControl />
<!-- Playhead Display Component -->
<PlayheadDisplay :liveUpdate="liveUpdate" />
<!-- Display connection status -->
<LiveUpdateOverlay :liveUpdate="liveUpdate" />
</div>
</template>
<script setup>
import { useLiveUpdate, LiveUpdateOverlay } from '@disguise-one/vue-liveupdate'
import TextLayerControl from './components/TextLayerControl.vue'
import PlayheadDisplay from './components/PlayheadDisplay.vue'
// Extract the director endpoint from the URL query parameters
const urlParams = new URLSearchParams(window.location.search)
const directorEndpoint = urlParams.get('director') || 'localhost:80' // Fallback for development
// Initialize the live update composable for the overlay
const liveUpdate = useLiveUpdate(directorEndpoint)
</script>
<style>
.app {
max-width: 800px;
margin: 0 auto;
padding: 1rem;
}
</style>
This example demonstrates several key features of the Python Execution and Live Update system:
Python registration and execution: This involves registering the plugin with the Designer and executing Python scripts through the provided API endpoints. The example demonstrates how we use the composable supplied by Disguise to send a POST request to the /api/session/python/execute
endpoint with a Python script to add a text layer.
Real-time Value Subscription: Using the autoSubscribe
function to listen to the playhead position. The autoSubscribe
function automatically handles property name mapping, so object.player.tRender
becomes player_tRender
in the returned object.
Automatic Resource Management: The autoSubscribe
function handles subscription cleanup automatically when the component is unmounted, making the code cleaner and less error-prone.
Connection Status Display: The LiveUpdateOverlay
component provides visual feedback about the WebSocket connection status and allows users to reconnect if needed.
Director Endpoint Configuration: The example shows how to extract the director endpoint from URL parameters, which is the recommended way to configure the connection.
The Live Update system is particularly useful for:
For guidance while exploring Live Update, check out the Live Update plugin repository.
These tools provide a more robust foundation for building Designer plugins, with better type safety, real-time updates, and a more maintainable codebase. Check out the Useful Links page for more resources and examples.