Skip to main content

Track Merge Script for Vegas Pro: A Step-by-Step Collaboration with Gemini Pro 2.5

Introduction

I spend what feels like endless hours working on videos for multiple YouTube channels and their related social media. It's all my own work from the planning stage, to recording, and the editing. It's challenging to try to grow these multiple genres of content like photography, technology, food review, and even cosplay crafting. 

As I'm editing, I see tasks that can be sped up with scripting. Magix Vegas Pro has a pretty robust API system that can be interfaced with raw textual script files, or you can create Class Library DLLs with something like Visual Studio 2022 that I use. 

My project properties. Here you can see .NET Framework 4.8 is being used because Magix Vegas Pro isn't quite current in their own application so this has been the best option.

Early on, I was writing scripts for this by hand with the help of fantastic tutorials by JETDV for Vegas Pro. Although now I've been using Gemini Pro 2.5 because it's shockingly good at writing code for this task as long as it has enough context. In my past few projects to augment the scripts, I've had next to no errors, but this time there was a single line of code with an issue. 

The before (top) and after running this script on a video project timeline. 

I asked Gemini Pro to create a website article going over our collaborative development process for this merge script. I'm really enjoying this way of programming because it takes out the deep dive research into Magix's very loosely documented API in addition to saving me time in (re)understanding how all of their internal objects, structures, and other elements interact together. 


Phase 1: The Initial Concept and First Draft

The project began with a clear and detailed request for a new function.

I'd like to add to my Magix Vegas Pro 21 video editor scripts. The scripts are made in Visual Studio 2022 in the programming language C# and the .NET Framework 4.8 as the target. In this case I'd like a function I can call from the interface that will work like this. You can make as many additional functions and classes as needed to make the code maintainable:

  • The user must select two video tracks on the timeline for this process to run. Prompt the user if they did not without actually doing the rest of the task.
  • On the bottom video track, the script will split up and delete video clips where they are covered by video clips in the above track.
  • It will also take the clips from the top track and move them into the bottom track.
  • It will delete the top track that is now empty if there are track above it. If there are no track above it, the script will leave that top track alone.

Included are the cs files of my script project. Let me know if you need any configuration files or other ones from Visual Studio to make the development process easier. I also included the VEGASScriptAPI.html and VEGASScriptFAQ.html created by Magix for their Vegas Pro application that describes the video editor application's scripting API.

Does this make sense? Do you need any additional details on its function? If not, please create the function and use any existing code you know about in my Vegas Pro script application.

Based on the prompt, the initial implementation involved creating a new public function in VegasFunctions.cs containing the core logic and adding a new button and event handler in MainInterface.cs to execute it.

My idea was to have this as a single button function.

Initial Code: VegasFunctions.cs

/// <summary>
/// Merges two selected video tracks. Clips from the top track will overwrite corresponding
/// sections of the bottom track. All clips are consolidated onto the bottom track,
/// and the top track is removed if it's not the highest track in the project.
/// </summary>
/// <param name="vegas">The Vegas application object.</param>
public static void MergeTopTrackDown(Vegas vegas)
{
    try
    {
        // 1. Get selected video tracks
        List<VideoTrack> selectedTracks = vegas.Project.Tracks.OfType<VideoTrack>().Where(t => t.Selected).ToList();

        // 2. Validate selection
        if (selectedTracks.Count != 2)
        {
            MessageBox.Show("Please select exactly two video tracks to proceed.", "Selection Error", MessageBoxButtons.OK, MessageBoxIcon.Warning);
            return;
        }

        // 3. Identify top and bottom tracks based on their DisplayIndex
        VideoTrack topTrack = selectedTracks.OrderBy(t => t.DisplayIndex).First();
        VideoTrack bottomTrack = selectedTracks.OrderBy(t => t.DisplayIndex).Last();
        
        // ... (rest of the logic) ...
    }
    catch (Exception e)
    {
        MessageBox.Show("An error occurred during the merge process:\n\n" + e.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
}
    

Initial Code: MainInterface.cs


// This event handler was created for the new button
private void btnMergeTracks_Click(object sender, EventArgs e)
{
    try
    {
        VegasFunctions.MergeTopTrackDown(thisVegas);
        this.Close();
    }
    catch (Exception ex)
    {
        MessageBox.Show("An unexpected error occurred: \n\n" + ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
}
    

Phase 2: Bug Fix and Code Refinement

During testing of the first draft, the C# compiler identified a type error in the logic for calculating the midpoint of a video clip.

Your line Timecode midpoint = bottomClip.Start + (bottomClip.Length.Nanos / 2); has errors. Is this a correct code version of the code? Timecode midpoint = new Timecode(); midpoint.Nanos = bottomClip.Start.Nanos + (bottomClip.Length.Nanos / 2);

The user's analysis was correct. The + operator is not overloaded to add a long (nanosecond count) directly to a Timecode object. The proposed fix was valid, but a more idiomatic solution using the API's built-in methods was implemented. The Timecode.FromNanos() static method creates a new Timecode object from a duration, which can then be correctly added to another Timecode object.

This correction was integrated into the core function.

Corrected MergeTopTrackDown function in VegasFunctions.cs


public static void MergeTopTrackDown(Vegas vegas)
{
    try
    {
        // ... (previous logic) ...

        // 6. Remove bottom clips that are now fully covered by where top clips will be.
        List<TrackEvent> finalBottomClips = new List<TrackEvent>(bottomTrack.Events);
        foreach (TrackEvent bottomClip in finalBottomClips)
        {
            // Check if the midpoint of the bottom clip is inside any top clip's time range.
            // CORRECTED LINE:
            Timecode midpoint = bottomClip.Start + Timecode.FromNanos(bottomClip.Length.Nanos / 2); 
            bool isCovered = topClips.Any(topClip => midpoint >= topClip.Start && midpoint < topClip.End);

            if (isCovered)
            {
                bottomTrack.Events.Remove(bottomClip);
            }
        }

        // ... (rest of the logic) ...
    }
    catch (Exception e)
    {
        MessageBox.Show("An error occurred during the merge process:\n\n" + e.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
}


Phase 3: Enhancing User Interaction and Logic

With the bug fixed, the next step was to improve the script's architecture and user experience. The initial design mixed UI logic (validating user selections) with the core processing logic, which is not ideal. It also limited the function's use to video tracks only.

The check for track selection and related potential prompt to the user to select two tracks should happen on the interface thread and not close the script. Also, we don't likely need to differentiate between video or audio tracks if that's done because I could see using this on two audio tracks in the future. Then once selection is verified, it would send the two tracks to the merging function in VegasFunctions.cs... But I'd say checking if they are two of the same type of track is a good idea.

This was a critical suggestion. Refactoring to separate these concerns makes the code cleaner, more reusable, and provides a better experience. If the user makes a selection error, the script window should remain open, allowing them to correct their selection without having to re-run the script. (on a side note, I might have to change this in the future because of how Vegas Pro treats scripts running in the foreground. You technically can't select tracks if the script window is running as it locks the Vegas Pro interface in that situation. Though, logically I could see myself wanting to keep the window open in some situations.)

The code was restructured accordingly.

Refactored UI Logic in MainInterface.cs


private void btnMergeTracks_Click(object sender, EventArgs e) 
{
    // 1. Get all selected tracks from the project
    List<Track> selectedTracks = thisVegas.Project.Tracks.OfType<Track>().Where(t => t.Selected).ToList();

    // 2. Validate that exactly two tracks are selected.
    if (selectedTracks.Count != 2)
    {
        MessageBox.Show("Please select exactly two tracks to proceed.", "Selection Error", MessageBoxButtons.OK, MessageBoxIcon.Warning);
        return; // Keep the form open
    }

    // 3. Validate that both tracks are of the same type.
    if (selectedTracks[0].MediaType != selectedTracks[1].MediaType)
    {
        MessageBox.Show("Please select two tracks of the same type (e.g., two video tracks or two audio tracks).", "Type Mismatch", MessageBoxButtons.OK, MessageBoxIcon.Warning);
        return; // Keep the form open
    }
    
    // 4. If validation passes, execute the merge operation.
    try
    {
        // Call the processing function, passing the two validated tracks.
        VegasFunctions.MergeTopTrackDown(thisVegas, selectedTracks[0], selectedTracks[1]);
        this.Close();
    }
    catch (Exception ex)
    {
        MessageBox.Show("An unexpected error occurred during the merge operation: \n\n" + ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
}

Refactored Core Function in VegasFunctions.cs


//The function now accepts tracks as parameters and works with the generic 'Track' type.
public static void MergeTopTrackDown(Vegas vegas, Track trackA, Track trackB)
{
    try
    {
        // Identify top and bottom tracks based on their DisplayIndex
        Track topTrack = (trackA.DisplayIndex < trackB.DisplayIndex) ? trackA : trackB;
        Track bottomTrack = (trackA.DisplayIndex < trackB.DisplayIndex) ? trackB : trackA;

        // The rest of the processing logic remains here...
    }
    catch (Exception e)
    {
        MessageBox.Show("An error occurred during the merge process:\n\n" + e.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
}


Phase 4: Finalizing the User Experience

The script was now robust and architecturally sound, but its behavior could be further polished. The current version used excessive logging and displayed a success message, which is often unnecessary for a utility tool.

please use my logging system in the MergeTopTrackDown function

The logging system was integrated to trace the function's internal steps.

That feels like excessive use of the logging system. We want to state the process started, state successful completion, and alternatively log errors. Don't prompt the user that it was successful. Only prompt the user if there was an error. Then prevent the script window from closing.

This final request led to the definitive version of the code. A professional tool should be unobtrusive. Success is the expected outcome and shouldn't require user interaction. Errors, however, must be clearly communicated. To achieve this, the core function was modified to return a boolean (true for success, false for failure), allowing the UI to react accordingly. 

On a side note, I don't want unnecessary pop-up messages which I conveyed to Gemini. It's wild to me how smart it is over multiple prompts. I could have easily avoided some of our back and forth if I was more specific about what I needed, but it's cool to see how it implements things when there is a lot of choice involved like how specifically to implement my logging class in the function. 

Final Code: VegasFunctions.cs


public static bool MergeTopTrackDown(Vegas vegas, Track trackA, Track trackB)
{
    try
    {
        Logger.Log("", "", "", "MergeTopTrackDown process started.", ResultType.Success);

        // ... All processing logic ...

        Logger.Log("", "", "", "Merge process completed successfully.", ResultType.Success);
        return true; // Indicate success
    }
    catch (Exception e)
    {
        Logger.Log("", "", "", $"An error occurred during the merge process: {e.Message}", ResultType.Failure);
        MessageBox.Show("An error occurred during the merge process. Please check the log for details.\n\n" + e.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
        return false; // Indicate failure
    }
}

Final Code: MainInterface.cs


private void btnMergeTracks_Click(object sender, EventArgs e)
{
    // ... (Validation logic remains the same) ...
    
    // Execute the merge operation and check its result.
    bool wasSuccessful = VegasFunctions.MergeTopTrackDown(thisVegas, selectedTracks[0], selectedTracks[1]);
    
    // Only close the form if the operation succeeded.
    if (wasSuccessful)
    {
        this.Close();
    }
    // If it was not successful, the function has already shown an error message.
}


Conclusion

The user confirmed the final implementation was working as desired.

That runs, fantastic work!

This collaborative process, moving from a detailed concept to a polished and robust tool, highlights the power of iterative development. By starting with a functional draft and progressively refining it based on testing and usability feedback, we arrived at a final script that is not only effective but also provides a clean and professional user experience. This documented journey serves as a practical guide for others looking to extend the capabilities of Vegas Pro through scripting (and helps them understand how to use AI systems like Gemini Pro to assist in the development process...). 


Popular posts from this blog

ChatGPT is a new, and faster, way to do programming!

Currently ChatGPT is in a free “initial research preview” . One of its well known use cases at this point is generating software code. I’ve also just used it to write most of this article… Well, actually a future article about cleaning up SRT subtitle files of their metadata faster than I have been by hand with Notepad++ and its replace functionality. Update: I recorded a screencast of writing the SRT subtitle cleaner application loading and processing portion. I relied heavily on ChatGPT for code. It was a fun process! https://youtu.be/TkEW39OloUA ChatGPT, developed by OpenAI, is a powerful language model that can assist developers in a variety of tasks, including natural language processing and text generation. One such task that ChatGPT can help with is creating an SRT cleaner program. SRT, or SubRip Subtitle, files are commonly used to add subtitles to video files. However, these files can become cluttered with unnecessary information, such as timing lines or blank spaces. To clean...

Theme error in 2010s Android App after AppCompat Migration

I plan on releasing a lot of my old work as GPL open source, but most of it has aged to the point that it no longer functions, or if it does work it’s running in compatibility mode. Basically it’s no longer best practices. Not a good way to start off any new public GPL projects, in my opinion. The current project I’m working on is an Android app that calculates star trails meant to help photographers get or avoid that in their night time photos. For now I’m going to skip some of the import process because I didn’t document it exactly. It’s been mostly trial and error as I poke around Android Studio post import. The Android Studio import process… Removing Admob Google Play code before the project would run at all. After removing dependencies, it kind of worked, but when running it in the emulator it shows a pop-up message saying that the app was developed for an old version of Android. Going through the process of updating code to match current best practices… I had the IDE convert the ...

Printing to file in Linux WINE

I noticed that this post has been sitting as a draft since 2011. At this point I have no idea if it’s useful or what I was even doing, but I might as well make it public in case someone can find it helpful! So I’ve been trying to get one of those PDF print drivers working in WINE without success. I then came upon a process that might work. When printing you need to select the checkbox “Print to file” that creates a .prn file. Just Linux things... I was using a program that only has printing facilities, but I want to export around 100 pages of text and images. Once you have the .prn (postscript) file, you can do any number of things to it. In my case I want the postscript file to be converted to HTML. I am also considering PDF format because that has more conversion options to eventually get me to HTML or plain text. sudo apt-get install cups-pdf Or it looks like that package might have changed to this… sudo apt-get install printer-driver-cups-pdf Where PDFs would be generated in /home/...