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
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.
My idea was to have this as a single button function. |
/// <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);
}
}
// 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
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);
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
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.
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);
}
}
//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
please use my logging system in the MergeTopTrackDown function
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.
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
}
}
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
That runs, fantastic work!