Behind the PowerShell Pipeline logo

Behind the PowerShell Pipeline

Subscribe
Archives
April 11, 2025

Control Scripting

I've spent a few issues reviewing how I built a PowerShell tool to insert text into a graphic image. The function I wrote and demonstrated last time, will work with any graphic file, even though I created the function with a specific use case in mind. I want to take an image file associated with one of my musical compositions, insert the name, and use the image in the MP3 file. I also want to make this an easy to invoke repeatable process. When I update a score, I'll end up with a new MP3 file which will need to be updated again.

I have taken the functions I wrote for manage my score files into a module. The module is not in a PSModulePath location, so I import it specifying the path.

Import-Module c:\scripts\MuseScoreTools -force

I have created a test folder to use during development so that I don't pollute my original score files.

The MP3 file has already been updated with tags.

PS C:\temp\violin-nocturne> Get-MuseScoreMp3 '.\Nocturne for Violin.mp3' | Tee -Variable a

Title       : Nocturne for Violin
Subtitle    : in G Minor
Length      : 00:05:40
Composer    : {Jeffery Hicks}
Album       :
Track       : 0
Year        : 2023
Genre       : {Classical}
Description : https://musescore.com/user/26698536/scores/7210278
Comment     : This is a computer generated performance. Created with MuseScore 3.6.2
Copyright   : c. 2021-2023 Jeffery D. Hicks
Size        : 5488208
Path        : C:\temp\violin-nocturne\Nocturne for Violin.mp3

The original cover image is stored in the MP3.

Raw MP3 cover image
figure 1

What I need is a PowerShell solution that merges my stand-alone Add-TextToImage function with tools in the MuseScoreTools module. I could add the function to the module and update the module commands. But if I wanted to use the function for something else, I'd have to remember it is in the MuseScoreTools module. What I don't want is to have the same function in two locations because that means twice as much maintenance.

Instead, I think I will create a stand-alone script file. I can load the function and import the module in the script file. Given that every image will be different, each cover image will have a different font style and color. My initial design consideration is that I will manually invoke the script on a single folder.

This gives me a starting point for the control script.

#requires -version 7.5
#requires -module pwshSpectreConsole

#C:\Scripts\UpdateScoreMPCover.ps1

Param(
    [Parameter(HelpMessage = 'The path to the MuseScore files')]
    [string]$Path = "."
)

. C:\scripts\Add-ImageText.ps1
Import-Module C:\scripts\MuseScoreTools -Force

Organize

When approaching a control script, I think you'll find it helpful if you can verbalize each step in the process. Unlike a function which is designed to do one task, a control script is an orchestration of tasks. Take the time up front to organize your thoughts. Even better, write them in your script file as comments.

#convert the path to a full file system path

#Get the MP3 file in the location. There should only be one.

#Validate that the MP3 file tags have been updated.

#Update the MP3 file if missing information.

#Test for cover image settings

#import cover image settings if found

#otherwise use parameter values

#Create a preview image file of the cover image with imported or parameter settings

#update the MP3 file with preview cover image

#show the new image

#export the cover image settings

Since I want to be able to quickly refresh a cover image with text, I intend to save settings like font and color in a JSON file. My comments layout a workflow. Looking at the comments and visualizing the process also ensure I have steps in the proper order. All I need to do now is write code to implement the steps. When I'm finished, they will already be documented!

Getting paths is simple enough.

#convert the path to a full file system path
$Path = Convert-Path $Path

#Get the MP3 file in the location. There should only be one.
$mp3Path = (Get-ChildItem -Path $Path -Filter *.mp3).FullName

As is validating that the MP3 file has been updated.

#Validate that the MP3 file tags have been updated.
$mp3Tags = Get-MuseScoreMp3 -Path $mp3Path
if (-Not $mp3Tags.Title) {
    #Update the MP3 file if missing information.
    Write-Host "Updating MP3 tags" -ForegroundColor Yellow
    Update-MuseScoreMp3 -Path $mp3path
    Write-Host "MP3 tags updated" -ForegroundColor Green
    $mp3Tags = Get-MuseScoreMp3 -Path $mp3Path
}

After writing the code, I realized I need to move one of the workflow comments up. I should also explain that I am manually executing each block of code interactively in my test folder. If the code works, I copy and paste it into the control script. If I mess things up, I can re-copy the source files into the test folder.

Want to read the full issue?
GitHub Bluesky LinkedIn About Jeff
Powered by Buttondown, the easiest way to start and grow your newsletter.