From 227d951d6dc4c9a6de47951e7913bd5b6aedc6fd Mon Sep 17 00:00:00 2001 From: Zac Gaetano Date: Tue, 7 Apr 2026 22:05:47 -0400 Subject: [PATCH] Phase 2: services/premiere-plugin/jsx/premiere.jsx --- services/premiere-plugin/jsx/premiere.jsx | 412 ++++++++++++++++++++++ 1 file changed, 412 insertions(+) create mode 100644 services/premiere-plugin/jsx/premiere.jsx diff --git a/services/premiere-plugin/jsx/premiere.jsx b/services/premiere-plugin/jsx/premiere.jsx new file mode 100644 index 0000000..8d8e7d7 --- /dev/null +++ b/services/premiere-plugin/jsx/premiere.jsx @@ -0,0 +1,412 @@ +/** + * Wild Dragon MAM - Premiere Pro ExtendScript + * + * This file contains ExtendScript functions that interact with Premiere Pro + * to import media files and manage the timeline. + * + * ExtendScript is Adobe's implementation of JavaScript that runs in the + * Premiere Pro host application context. + */ + +// ============================================================================ +// Core Import Functions +// ============================================================================ + +/** + * Imports a media file into the active Premiere Pro project + * @param {string} filePath - Full path to the file to import + * @returns {string} JSON string with success status and message + */ +function importFileToProject(filePath) { + var result = { + success: false, + message: "", + itemID: null + }; + + try { + // Check if project is open + if (!app.project) { + result.message = "No active Premiere Pro project"; + return JSON.stringify(result); + } + + // Check if file exists + var file = new File(filePath); + if (!file.exists) { + result.message = "File does not exist: " + filePath; + return JSON.stringify(result); + } + + // Import the file into the project + app.project.importFiles([filePath]); + + result.success = true; + result.message = "File imported successfully"; + + return JSON.stringify(result); + } catch (error) { + result.message = "Error importing file: " + error.message; + return JSON.stringify(result); + } +} + +/** + * Gets the active sequence in the project + * @returns {string} JSON string with sequence name or null + */ +function getActiveSequence() { + var result = { + sequenceName: null, + sequenceID: null + }; + + try { + if (!app.project) { + return JSON.stringify(result); + } + + var activeSequence = app.project.activeSequence; + if (activeSequence) { + result.sequenceName = activeSequence.name; + result.sequenceID = activeSequence.sequenceID; + } + + return JSON.stringify(result); + } catch (error) { + return JSON.stringify(result); + } +} + +/** + * Inserts a clip into the active sequence at the playhead position + * @param {string} filePath - Full path to the media file + * @param {number} trackIndex - Video track index (1-based) + * @returns {string} JSON string with success status + */ +function insertClipToSequence(filePath, trackIndex) { + var result = { + success: false, + message: "" + }; + + try { + if (!app.project) { + result.message = "No active project"; + return JSON.stringify(result); + } + + var sequence = app.project.activeSequence; + if (!sequence) { + result.message = "No active sequence"; + return JSON.stringify(result); + } + + // Import the file + app.project.importFiles([filePath]); + + // Find the imported item in the project + var projectItem = null; + var file = new File(filePath); + var fileName = file.displayName; + + // Search through project items for the imported file + var rootBin = app.project.rootItem; + projectItem = findProjectItemByName(rootBin, fileName); + + if (!projectItem) { + result.message = "Could not find imported file in project"; + return JSON.stringify(result); + } + + // Get the track at the specified index + if (trackIndex < 1 || trackIndex > sequence.videoTracks.numTracks) { + result.message = "Invalid track index: " + trackIndex; + return JSON.stringify(result); + } + + var track = sequence.videoTracks[trackIndex - 1]; + var playheadTime = sequence.getTime(); + + // Create a clip from the project item + var clip = projectItem.getMedia(); + if (!clip) { + result.message = "Could not get media from project item"; + return JSON.stringify(result); + } + + // Insert at playhead position + // Note: This is a simplified version. Real timeline editing requires + // more sophisticated handling of clips and tracks + var trackItem = track.insertClip(projectItem, playheadTime); + + if (trackItem) { + result.success = true; + result.message = "Clip inserted at track " + trackIndex; + } else { + result.message = "Failed to insert clip into track"; + } + + return JSON.stringify(result); + } catch (error) { + result.message = "Error inserting clip: " + error.message; + return JSON.stringify(result); + } +} + +/** + * Gets the current project file path + * @returns {string} JSON string with project path + */ +function getProjectPath() { + var result = { + projectPath: null, + projectName: null + }; + + try { + if (!app.project) { + return JSON.stringify(result); + } + + var projectFile = app.project.path; + if (projectFile && projectFile.exists) { + result.projectPath = projectFile.fsName; + result.projectName = projectFile.displayName; + } + + return JSON.stringify(result); + } catch (error) { + return JSON.stringify(result); + } +} + +/** + * Gets information about all video tracks in the active sequence + * @returns {string} JSON string with track information + */ +function getSequenceTracks() { + var result = { + tracks: [] + }; + + try { + if (!app.project) { + return JSON.stringify(result); + } + + var sequence = app.project.activeSequence; + if (!sequence) { + return JSON.stringify(result); + } + + for (var i = 0; i < sequence.videoTracks.numTracks; i++) { + var track = sequence.videoTracks[i]; + result.tracks.push({ + index: i + 1, + name: track.name || ("V" + (i + 1)), + type: "video" + }); + } + + return JSON.stringify(result); + } catch (error) { + return JSON.stringify(result); + } +} + +/** + * Gets the current playhead position in the active sequence + * @returns {string} JSON string with playhead time in seconds + */ +function getPlayheadPosition() { + var result = { + timeInSeconds: 0, + timeCode: "" + }; + + try { + if (!app.project) { + return JSON.stringify(result); + } + + var sequence = app.project.activeSequence; + if (!sequence) { + return JSON.stringify(result); + } + + var time = sequence.getTime(); + result.timeInSeconds = time.seconds; + + // Format as timecode + var ticks = time.ticks; + var ticksPerFrame = sequence.timebase; + var frameNumber = Math.floor(ticks / ticksPerFrame); + var fps = sequence.timebase / 254016000000; + + var hours = Math.floor(frameNumber / (fps * 3600)); + var minutes = Math.floor((frameNumber % (fps * 3600)) / (fps * 60)); + var seconds = Math.floor((frameNumber % (fps * 60)) / fps); + var frames = frameNumber % Math.floor(fps); + + result.timeCode = pad(hours, 2) + ":" + pad(minutes, 2) + ":" + + pad(seconds, 2) + ":" + pad(frames, 2); + + return JSON.stringify(result); + } catch (error) { + return JSON.stringify(result); + } +} + +// ============================================================================ +// Helper Functions +// ============================================================================ + +/** + * Recursively searches for a project item by name + * @param {Bin} bin - The bin to search in + * @param {string} name - The name to search for + * @returns {ProjectItem} The found project item or null + */ +function findProjectItemByName(bin, name) { + if (!bin || !bin.children) { + return null; + } + + for (var i = 0; i < bin.children.numItems; i++) { + var item = bin.children[i]; + + // Check if this item matches + if (item.name === name) { + return item; + } + + // If it's a bin, search recursively + if (item.type === "bin") { + var found = findProjectItemByName(item, name); + if (found) { + return found; + } + } + } + + return null; +} + +/** + * Pads a number with leading zeros + * @param {number} num - The number to pad + * @param {number} digits - The number of digits to pad to + * @returns {string} The padded number + */ +function pad(num, digits) { + var str = "" + num; + while (str.length < digits) { + str = "0" + str; + } + return str; +} + +/** + * Gets basic project information + * @returns {string} JSON string with project info + */ +function getProjectInfo() { + var result = { + projectName: "", + projectPath: "", + videoSequenceCount: 0, + audioSequenceCount: 0 + }; + + try { + if (!app.project) { + return JSON.stringify(result); + } + + result.projectName = app.project.name; + + var projectFile = app.project.path; + if (projectFile) { + result.projectPath = projectFile.fsName; + } + + // Count sequences + var rootBin = app.project.rootItem; + if (rootBin && rootBin.children) { + for (var i = 0; i < rootBin.children.numItems; i++) { + var item = rootBin.children[i]; + if (item.type === "sequence") { + // Check sequence type by checking tracks + if (item.videoTracks && item.videoTracks.numTracks > 0) { + result.videoSequenceCount++; + } + if (item.audioTracks && item.audioTracks.numTracks > 0) { + result.audioSequenceCount++; + } + } + } + } + + return JSON.stringify(result); + } catch (error) { + return JSON.stringify(result); + } +} + +/** + * Exports the current sequence to a specified location + * @param {string} outputPath - The path where the file should be exported + * @param {string} presetName - The export preset name (e.g., "Apple ProRes 422 HQ") + * @returns {string} JSON string with export status + */ +function exportSequence(outputPath, presetName) { + var result = { + success: false, + message: "" + }; + + try { + if (!app.project) { + result.message = "No active project"; + return JSON.stringify(result); + } + + var sequence = app.project.activeSequence; + if (!sequence) { + result.message = "No active sequence"; + return JSON.stringify(result); + } + + var outputFile = new File(outputPath); + + // Get the export preset + // Note: This requires the preset to be available in Premiere Pro + var preset = app.getExportPreset(presetName); + + if (!preset) { + result.message = "Export preset not found: " + presetName; + return JSON.stringify(result); + } + + // Export the sequence + app.project.exportSequenceToFile(sequence, outputFile, preset); + + result.success = true; + result.message = "Export completed"; + + return JSON.stringify(result); + } catch (error) { + result.message = "Error exporting: " + error.message; + return JSON.stringify(result); + } +} + +// ============================================================================ +// Initialization +// ============================================================================ + +// Log that the script has loaded +if (typeof(alert) !== "undefined") { + // alert("Wild Dragon MAM ExtendScript loaded"); +}