Created
February 9, 2026 00:48
-
-
Save h8rt3rmin8r/8adf4d08ce9ddd87547810813d360461 to your computer and use it in GitHub Desktop.
Rename screenshot files to a standardized naming convention
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <# | |
| .SYNOPSIS | |
| Rename screenshot files to a standardized naming convention. | |
| .DESCRIPTION | |
| Rename screenshot files in a target directory to a standardized naming | |
| convention. The script searches for screenshot files matching one or more of | |
| the following patterns: | |
| *<String>.<Extension> | |
| *.<VideoExtension>.<Extension> | |
| *.<VideoExtension><String>.<Extension> | |
| Matching files are renamed to the following format: | |
| *.<VideoExtension><ScreenshotSubstitutionString>.<Extension> | |
| If no video file extension is detected in the screenshot filename, the | |
| VideoExtensionFallback value is used instead. | |
| For example, with default parameter values: | |
| Input: movie_s.jpg -> Output: movie.mp4_screen.jpg | |
| Input: movie.mkv.jpg -> Output: movie.mkv_screen.jpg | |
| Input: movie.mkv_s.jpg -> Output: movie.mkv_screen.jpg | |
| Input: movie.avi_s.jpg -> Output: movie.avi_screen.jpg | |
| .PARAMETER Directory | |
| The target directory containing the screenshot files to be renamed. | |
| If not provided, the current working directory is used. | |
| .PARAMETER String | |
| The substring present in screenshot filenames that identifies them as | |
| screenshots. This substring is replaced by ScreenshotSubstitutionString | |
| during renaming. | |
| Alias: s, substr, substring | |
| Default: _s | |
| .PARAMETER Verbosity | |
| If set to $true, additional information will be printed to the console | |
| during script execution. | |
| Alias: v | |
| Default: $true | |
| .PARAMETER Extension | |
| The file extension of all screenshot files to be renamed. | |
| Alias: e | |
| Default: jpg | |
| .PARAMETER VideoExtensions | |
| An array of video file extensions to consider when detecting video file | |
| associations in screenshot filenames. | |
| Alias: ve, videoext | |
| Default: @('avi', 'mkv', 'mp4', 'webm', 'wmv') | |
| .PARAMETER VideoExtensionFallback | |
| The fallback video file extension to use when no matching video file | |
| extension is found in a screenshot filename. | |
| Default: mp4 | |
| .PARAMETER Help | |
| Display the detailed help text for this script. | |
| Alias: h | |
| .EXAMPLE | |
| .\Rename-Screenshots.ps1 | |
| Rename all screenshot files in the current directory using default settings. | |
| .EXAMPLE | |
| .\Rename-Screenshots.ps1 -Directory "C:\Videos\Screenshots" | |
| Rename all screenshot files in the specified directory. | |
| .EXAMPLE | |
| .\Rename-Screenshots.ps1 -String "_thumb" -Extension "png" | |
| Rename screenshot files that use a custom substring and file extension. | |
| .EXAMPLE | |
| .\Rename-Screenshots.ps1 -Directory "D:\Media" -VideoExtensions @('mp4','mkv') -Verbosity $false | |
| Rename screenshots in D:\Media, considering only mp4 and mkv video | |
| extensions, with console output suppressed. | |
| #> | |
| [CmdletBinding(SupportsShouldProcess=$false,ConfirmImpact='None',DefaultParameterSetName='Default')] | |
| Param( | |
| # The target directory containing the screenshot files to be renamed | |
| [Parameter(Mandatory=$false,ParameterSetName='Default')] | |
| [System.String]$Directory, | |
| # Screenshot substring | |
| [Parameter(Mandatory=$false,ParameterSetName='Default')] | |
| [Alias("s","substr","substring")] | |
| [System.String]$String = '_s', | |
| # Script verbosity flag | |
| [Parameter(Mandatory=$false,ParameterSetName='Default')] | |
| [Parameter(Mandatory=$false,ParameterSetName='HelpText')] | |
| [Alias('v')] | |
| [System.Boolean]$Verbosity = $true, | |
| # The file extension of all screenshot files to be renamed | |
| [Parameter(Mandatory=$false,ParameterSetName='Default')] | |
| [Alias("e")] | |
| [System.String]$Extension = 'jpg', | |
| # Video file extensions to consider when renaming screenshots | |
| [Parameter(Mandatory=$false, ParameterSetName='Default')] | |
| [Alias("ve","videoext")] | |
| [System.Array]$VideoExtensions = @('avi', 'mkv', 'mp4', 'webm', 'wmv'), | |
| # Fallback video file extension to use when no matching video file extension is found in screenshot file names | |
| [System.String]$VideoExtensionFallback = 'mp4', | |
| # A switch parameter to display the script help text | |
| [Parameter(Mandatory=$true,ParameterSetName='HelpText')] | |
| [Alias("h")] | |
| [Switch]$Help | |
| ) | |
| #______________________________________________________________________________ | |
| ## Declare Functions | |
| function ValidateIsDirectory { | |
| <# | |
| .SYNOPSIS | |
| Test if a string is a valid directory reference | |
| .DESCRIPTION | |
| Test if a string is a valid directory reference. | |
| Outputs are exactly "true" or "false". | |
| .PARAMETER String | |
| The string to be tested. | |
| Alias: d,dir,directory,s,i,in,input,inputstring | |
| .PARAMETER Verbosity | |
| If set to $true, additional information will be printed to the console. | |
| Alias: v | |
| Default: $false | |
| .EXAMPLE | |
| ValidateIsDirectory -String "C:\bin\pslib" | |
| #> | |
| Param( | |
| [Alias("d","dir","directory","s","i","in","input","inputstring")] | |
| [System.String]$String, | |
| [Alias("v")] | |
| [System.Boolean]$Verbosity = $Verbosity | |
| ) | |
| $thisSubFunction = "{0}" -f $MyInvocation.MyCommand | |
| if (-Not($thisFunction)) {$thisFunction = $thisSubFunction} else {$thisFunction = -join("$thisFunction",":","$thisSubFunction")} | |
| $StringTest = -join("x", "$String") | |
| $StringTestLength = $StringTest.Length | |
| $output = $null | |
| if ($StringTestLength -eq 1) { | |
| Vbs -Caller "$thisFunction" -Status e -Message "No input detected" -Verbosity $Verbosity | |
| $output = $false | |
| } elseif ($StringTestLength -gt 260) { | |
| Vbs -Caller "$thisFunction" -Status e -Message "Input string is too long" -Verbosity $Verbosity | |
| $output = $false | |
| } else { | |
| $output = (Test-Path -LiteralPath "$String" -PathType Container) | |
| Vbs -Caller "$thisFunction" -Status i -Message "Validation of input ($String): $output" -Verbosity $Verbosity | |
| } | |
| return $output | |
| } | |
| function ValidateIsFile { | |
| <# | |
| .SYNOPSIS | |
| Test if a string is a valid file reference | |
| .DESCRIPTION | |
| Test if a string is a valid file reference. | |
| Outputs are exactly "true" or "false". | |
| .PARAMETER String | |
| The string to be tested. | |
| Alias: f,file,s,i,in,input,inputstring | |
| .PARAMETER Verbosity | |
| If set to $true, additional information will be printed to the console. | |
| Alias: v | |
| Default: $false | |
| .EXAMPLE | |
| ValidateIsFile -String "C:\bin\pslib\main.ps1" | |
| #> | |
| Param( | |
| [Alias("f","fil","s","i","in","input","inputstring")] | |
| [System.String] | |
| $String, | |
| [Alias("v")] | |
| [System.Boolean] | |
| $Verbosity = $Verbosity | |
| ) | |
| $thisSubFunction = "{0}" -f $MyInvocation.MyCommand | |
| if (-Not($thisFunction)) {$thisFunction = $thisSubFunction} else {$thisFunction = -join("$thisFunction",":","$thisSubFunction")} | |
| $StringTest = -join("x", "$String") | |
| $StringTestLength = $StringTest.Length | |
| $output = $null | |
| if ($StringTestLength -eq 1) { | |
| Vbs -Caller "$thisFunction" -Status e -Message "No input detected" -Verbosity $Verbosity | |
| $output = $false | |
| } elseif ($StringTestLength -gt 260) { | |
| Vbs -Caller "$thisFunction" -Status e -Message "Input string is too long" -Verbosity $Verbosity | |
| $output = $false | |
| } else { | |
| $output = (Test-Path -LiteralPath "$String" -PathType Leaf) | |
| Vbs -Caller "$thisFunction" -Status i -Message "Validation of input ($String): $output" -Verbosity $Verbosity | |
| } | |
| return $output | |
| } | |
| function Vbs { | |
| <# | |
| .SYNOPSIS | |
| Print structured and colorized verbosity and write formatted log messages | |
| .DESCRIPTION | |
| Print structured and colorized verbosity and write formatted log messages. | |
| This function is designed to be used as a logging function for other library | |
| functions and scripts. Care has been taken to ensure the portability of this | |
| function so it can be easily implemented in other scripts and functions | |
| unrelated to this library. | |
| To-Do: | |
| - Add a parameter to allow the inclusion of a process ID | |
| - Add more granular control over the verbosity of the output | |
| .PARAMETER Message | |
| The message to be printed to the console and written to the log file. | |
| .PARAMETER Caller | |
| The name of the calling function or script. If the function is a sub- | |
| function, then the name of the parent function should be included. | |
| Syntax: "<Script>:<Function>:<Sub-Function>" | |
| .PARAMETER Status | |
| The status of the message | |
| Allowed Values: INFO (i), ERROR (e), WARNING (w), DEBUG (d) | |
| Default: INFO (i) | |
| .PARAMETER LogDir | |
| The directory in which the log file will be written. Normally this shouldn't | |
| be changed. | |
| Default: C:\bin\pslib\logs | |
| .PARAMETER LibName | |
| The name of the library or script that is calling this function. This is | |
| used to identify the source of the log message. Normally this shouldn't be | |
| changed. | |
| Default: pslib | |
| .PARAMETER VbsSessionID | |
| The unique identifier for the current session. This is used to identify the | |
| session in which the log message was generated. Normally this shouldn't be | |
| changed. | |
| Default: $LibSessionID | |
| .PARAMETER Verbosity | |
| If set to $true, the message will be printed to the console and written to | |
| the log file. If set to $false, the message will be written to the log file | |
| only. | |
| Default: $true | |
| .EXAMPLE | |
| Vbs -Caller "$thisFunction" -Status info -Message "This is a test" -Verbosity $true | |
| #> | |
| Param( | |
| [System.String]$Message, | |
| [System.String]$Caller, | |
| [System.String]$Status, | |
| [Alias("LogDirectory")] | |
| [System.String]$LogDir = "C:\bin\pslib\logs", | |
| [Alias("LibraryName")] | |
| [System.String]$LibName = "pslib", | |
| [System.String]$VbsSessionID = $LibSessionID, | |
| [Alias("v")] | |
| [System.Boolean]$Verbosity = $Verbosity | |
| ) | |
| function VbsFunctionStackTotalDepth { | |
| # Calculate the total depth of the updated function stack | |
| # Each non-numbered function is counted as 1 and each numbered function is | |
| # counted as the number in the numeric suffix. | |
| Param( | |
| [System.String]$CurrentStack | |
| ) | |
| # Trim whitespace and colon characters from the end of the stack string | |
| $CurrentStack = $CurrentStack -replace "[:\s]+$", "" | |
| # Split the current stack into an array | |
| $CurrentStackArray = $CurrentStack -split ':' | |
| # Initialize a variable to hold the total depth of the stack | |
| $TotalDepth = [int]0 | |
| foreach ($function in $CurrentStackArray) { | |
| if ($function -match '\d+') { | |
| # If a numeric suffix is found, then increment the total depth by the numeric suffix | |
| $TotalDepth += [int]$matches[0] | |
| } else { | |
| # If no numeric suffix is found, then increment the total depth by 1 | |
| $TotalDepth += [int]1 | |
| } | |
| } | |
| return $TotalDepth | |
| } | |
| function VbsLogPath { | |
| # Derive the path to the current log file based on the current date | |
| Param( | |
| [System.String]$LogDir = "$LogDir" | |
| ) | |
| $LogName = (Get-Date -UFormat "%Y_%m") | |
| $LogFile = -join("$LogName", ".log") | |
| $LogPath = -join("$LogDir", '\', "$LogFile") | |
| return $LogPath | |
| } | |
| function VbsLogRealityCheck { | |
| # Check if the log directory and log file exist. If they don't, then create them. | |
| Param( | |
| [System.String]$LogPath, | |
| [System.String]$LogDir = "$LogDir" | |
| ) | |
| $DCheck = (Test-Path -LiteralPath "$LogDir" -PathType Container) | |
| $FCheck = (Test-Path -LiteralPath "$LogPath" -PathType Leaf) | |
| if ($DCheck -eq $false) {New-Item -ItemType Directory -Path "$LogDir" -Force > $null} | |
| if ($FCheck -eq $false) {New-Item -ItemType File -Path "$LogPath" -Force > $null} | |
| return | |
| } | |
| function VbsLogWrite { | |
| # Write the input string to the log file | |
| Param( | |
| [System.String]$LogPath, | |
| [System.String]$InputString | |
| ) | |
| Add-Content -Path "$LogPath" -Value "$InputString" | |
| return | |
| } | |
| function VbsUpdateFunctionStack { | |
| param ( | |
| [System.String]$CurrentStack, | |
| [System.String]$NewFunction | |
| ) | |
| function VbsUpdateFunctionStackExtractNumber { | |
| # This sub-function discovers existing numeric suffixes in function names and returns only those | |
| # values which are meaningful to assist in incrementing the current cycle count as needed. | |
| Param( | |
| [System.String]$String = $null | |
| ) | |
| if ($String -match '\d+') { | |
| $TempNumber = [int]$matches[0] | |
| if ($TempNumber -lt 2) { | |
| # If the numeric suffix is less than 2, then return 0 | |
| return [int]0 | |
| } else { | |
| # If the numeric suffix is 2 or greater, subtract 1 and return the result | |
| return [int]$TempNumber - [int]1 | |
| } | |
| } else { | |
| # If no numeric suffix is found, then return [int]0 | |
| return [int]0 | |
| } | |
| } | |
| # Split the current stack into an array | |
| $stackArray = $CurrentStack -split ':' | |
| # Initialize a new array to hold the updated stack | |
| $newStackArray = @() | |
| # Initialize variables to hold the previous function (starts out empty) and the count (starts at 1) | |
| $previousFunction = "" | |
| $count = [int]1 | |
| foreach ($function in $stackArray) { | |
| $CycleIncrement = [int]1 | |
| $NumericSuffix = [int](VbsUpdateFunctionStackExtractNumber -String "$function") | |
| # Check if the current function is the same as the previous function | |
| if ($function -match "^$previousFunction(\(\d+\))?$") { | |
| # The current function is the same as the previous function and may contain a numeric suffix | |
| # Add the CycleIncrement to the NumericSuffix and add that result to $count | |
| [int]$LastCount = [int]$count | |
| [int]$CycleCount = [int]$NumericSuffix + [int]$CycleIncrement | |
| [int]$count = [int]$CycleCount + [int]$LastCount | |
| # Update the last function in the updated stack with the new count | |
| $newStackArray[-1] = "$previousFunction($count)" | |
| } else { | |
| # The current function is different from the previous function | |
| # Add the numeric suffix of the current function to [int]1 (if a numeric suffix exists) and reset the count to the result | |
| $count = $NumericSuffix + $CycleIncrement | |
| # Add the current function to the updated stack | |
| $newStackArray += $function | |
| } | |
| $previousFunction = $function -replace "\(\d+\)$", "" | |
| } | |
| # Check if the new function is the same as the last function in the updated stack | |
| if ($newStackArray[-1] -match "^$NewFunction(\(\d+\))?$") { | |
| # The new function is the same as the last function in the updated stack and may contain a numeric suffix | |
| if ($newStackArray[-1] -match "\((\d+)\)$") { | |
| # If it does, increment the count and add the numeric suffix to $count | |
| $count = [int]$matches[1] + 1 | |
| $newStackArray[-1] = "$NewFunction($count)" | |
| } else { | |
| # If it doesn't, add the function to the stack with a numeric suffix of 2 | |
| $newStackArray[-1] = "$NewFunction(2)" | |
| } | |
| } else { | |
| # If the new function is not the same as the last function in the updated stack, add it to the stack | |
| $newStackArray += $NewFunction | |
| } | |
| # Join the updated stack array back into a string | |
| return ($newStackArray -join ':') | |
| } | |
| # Create the timestamp field | |
| $epochStart = Get-Date 01.01.1970 | |
| $tA = $([DateTimeOffset]::Now.ToUnixTimeMilliseconds()) | |
| $tAmsStamp = ($epochStart + ([System.TimeSpan]::frommilliseconds($tA))).ToLocalTime() | |
| $tB = $tAmsStamp.ToString("yyyy-MM-dd HH:mm:ss.fffzzz") | |
| $t = -join("$tA", "|", "$tB", "|") | |
| $tV = -join("$tB", " ") | |
| # Create the ID field | |
| $x = -join("$VbsSessionID", "|") | |
| $xV = -join("$VbsSessionID", " ") | |
| # Create the status field | |
| if (-Not($Status)) {$Status = "X"} | |
| $s = $Status.toUpper() | |
| if ($s -eq "X" -or $s -eq "U" -or $s -eq "UNKNOWN") { | |
| $s = "[INFO][UNKNOWN]" | |
| $sV = "[ UNKNOWN ] " | |
| $sColor = "DarkGray" | |
| } elseif ($s -eq "I" -or $s -eq "INF" -or $s -eq "INFO") { | |
| $s = "[INFO]" | |
| $sV = "[ INFO ] " | |
| $sColor = "Gray" | |
| } elseif ($s -eq "E" -or $s -eq "ERR" -or $s -eq "ERROR") { | |
| $s = "[ERROR]" | |
| $sV = "[ ERROR ] " | |
| $sColor = "DarkRed" | |
| } elseif ($s -eq "C" -or $s -eq "CRIT" -or $s -eq "CRITICAL") { | |
| $s = "[ERROR][CRITICAL]" | |
| $sV = "[ CRITICAL ] " | |
| $sColor = "Magenta" | |
| } elseif ($s -eq "W" -or $s -eq "WARN" -or $s -eq "WARNING") { | |
| $s = "[WARN]" | |
| $sV = "[ WARNING ] " | |
| $sColor = "DarkYellow" | |
| } elseif ($s -eq "D" -or $s -eq "DEB" -or $s -eq "DEBUG") { | |
| $s = "[DEBUG]" | |
| $sV = "[ DEBUG ] " | |
| $sColor = "DarkCyan" | |
| } elseif ($s -eq "S" -or $s -eq "SUCCESS" -or $s -eq "OK" -or $s -eq "DONE" -or $s -eq "COMPLETE" -or $s -eq "G" -or $s -eq "GOOD") { | |
| $s = "[INFO][SUCCESS]" | |
| $sV = "[ SUCCESS ] " | |
| $sColor = "DarkGreen" | |
| } else { | |
| $s = "[INFO]" | |
| $sV = "[ INFO ] " | |
| $sColor = "DarkGray" | |
| } | |
| # Create the caller field | |
| $c = "$Caller" | |
| $cTest = -join("x", "$c") | |
| if ("$cTest" -eq "x" -or $c -eq "$LibName") { | |
| ## The caller is empty or is $LibName | |
| $cUpdate = $LibName | |
| $c = -join("$LibName",":") | |
| } else { | |
| ## The caller isn't empty and isn't "$LibName". | |
| ## Before doing anything ... | |
| ## Make sure $c doesn't end with ":" | |
| if ($c -match ":$") { | |
| $c = $c -replace ":$", "" | |
| } | |
| ## Make sure $c doesn't begin with "$LibName(" and end with ")" | |
| $MatchString = -join("^","$LibName", "\(") | |
| if ($c -match "$MatchString" -and $c -match "\)$") { | |
| # Simply remove the "$LibName(" and ")" from the caller and continue processing as normal | |
| $c = $c -replace "$MatchString", "" -replace "\)$", "" | |
| } | |
| ## We need to know if it is a single caller or a sub-function (parse for ':') | |
| $cSplit = $c -split ':' | |
| ## Even if the caller contains no ':' characters, the final element of the split array will be the caller | |
| $cLatest = $cSplit[-1] | |
| ## Take action based on the number of elements in the split array | |
| if ($cSplit.Length -gt 1) { | |
| ### The caller is a sub-function. We need to update the stack | |
| ### To do this we must remove the final string from the stack (the current caller) | |
| ### but avoid removing any duplicates of the current caller which might exist in the stack | |
| $cOthers = $cSplit[0..($cSplit.Length - 2)] -join ':' | |
| ### Now we can update the stack using the function: VbsUpdateFunctionStack | |
| $cUpdate = (VbsUpdateFunctionStack -CurrentStack "$cOthers" -NewFunction "$cLatest") | |
| $c = -join("$LibName", '(', "$cUpdate", '):') | |
| } else { | |
| ### The caller is a single function so we don't need to update the stack | |
| $cUpdate = $c | |
| $c = -join("$LibName", '(', "$Caller", '):') | |
| } | |
| } | |
| $cDepth = (VbsFunctionStackTotalDepth -CurrentStack "$cUpdate") | |
| $c = -join("$c", " ") | |
| $cV = -join("$LibName",'(',"$cDepth",')',': ') | |
| # Create the message field | |
| $m = "$Message" | |
| $mTest = -join("x", "$m") | |
| if ("$mTest" -eq "x") {$m = '...'} | |
| $mV = "$m" | |
| # Create verbose and silent versions of the final output string | |
| $o = -join("$t","$x","$s","$c","$m") | |
| $oV = -join("$tV","$xV","$sV","$cV","$mV") | |
| # Derive the absolute path to the current log file and make sure it exists and then send outputs as needed | |
| $l = (VbsLogPath -LogDir "$LogDir") | |
| VbsLogRealityCheck -LogPath "$l" -LogDir "$LogDir" | |
| if ($Verbosity -eq $false) { | |
| # Write to the log only (no output to host) | |
| VbsLogWrite -LogPath "$l" -InputString "$o" | |
| } else { | |
| # Write to the log AND print to the host | |
| VbsLogWrite -LogPath "$l" -InputString "$o" | |
| Write-Host "$oV" -ForegroundColor $sColor | |
| } | |
| return | |
| } | |
| #______________________________________________________________________________ | |
| ## Declare Variables and Arrays | |
| $Sep = [IO.Path]::DirectorySeparatorChar | |
| $SessionToken = $([DateTimeOffset]::Now.ToUnixTimeMilliseconds()) | |
| $LibSessionID = [System.GUID]::NewGuid().ToString().Replace('-','') | |
| $HereNow = ($PWD | Select-Object -Expand Path) -join '`n' | |
| $ThisScriptPath = $MyInvocation.MyCommand.Path | |
| $ThisScriptName = $MyInvocation.MyCommand.Name | |
| $ThisScript = $ThisScriptPath | Split-Path -Leaf | |
| $thisFunction = "{0}" -f $MyInvocation.MyCommand | |
| $ScreenshotSubstitutionString = '_screen' | |
| #______________________________________________________________________________ | |
| ## Execute Operations | |
| # Catch help text requests | |
| if (($Help) -or ($PSCmdlet.ParameterSetName -eq 'HelpText')) { | |
| Get-Help $ThisScriptPath -Detailed | |
| exit | |
| } | |
| # If $Directory is not provided, use the current directory | |
| if ((-Not($Directory)) -or ($Directory -eq "")) { | |
| $Directory = $HereNow | |
| } | |
| # If $Directory does not exist, print an error message and exit | |
| if (-Not(ValidateIsDirectory -String "$Directory" -Verbosity $true)) { | |
| Vbs -Caller "$thisFunction" -Status e -Message "The provided directory does not exist: $Directory" -Verbosity $Verbosity | |
| exit | |
| } | |
| # Build search patterns for screenshot files | |
| $SearchPatterns = @() | |
| # Pattern 1: *<String>.<Extension> (e.g., *_s.jpg) | |
| $SearchPatterns += -join("*", "$String", ".", "$Extension") | |
| foreach ($ve in $VideoExtensions) { | |
| # Pattern 2: *.<VideoExtension>.<Extension> (e.g., *.mkv.jpg) | |
| $SearchPatterns += -join("*", ".", "$ve", ".", "$Extension") | |
| # Pattern 3: *.<VideoExtension><String>.<Extension> (e.g., *.mkv_s.jpg) | |
| $SearchPatterns += -join("*", ".", "$ve", "$String", ".", "$Extension") | |
| } | |
| # Remove duplicate patterns | |
| $SearchPatterns = $SearchPatterns | Select-Object -Unique | |
| Vbs -Caller "$thisFunction" -Status i -Message "Search patterns: $($SearchPatterns -join ', ')" -Verbosity $Verbosity | |
| Vbs -Caller "$thisFunction" -Status i -Message "Searching directory: $Directory" -Verbosity $Verbosity | |
| # Collect all matching files | |
| $ScreenshotFiles = @() | |
| foreach ($pattern in $SearchPatterns) { | |
| $PatternMatches = Get-ChildItem -LiteralPath "$Directory" -Filter "$pattern" -File -ErrorAction SilentlyContinue | |
| if ($PatternMatches) { | |
| $ScreenshotFiles += $PatternMatches | |
| } | |
| } | |
| # Remove duplicates (a file might match multiple patterns) | |
| $ScreenshotFiles = $ScreenshotFiles | Sort-Object -Property FullName -Unique | |
| if ($ScreenshotFiles.Count -eq 0) { | |
| Vbs -Caller "$thisFunction" -Status w -Message "No screenshot files found matching the search patterns" -Verbosity $Verbosity | |
| exit | |
| } | |
| Vbs -Caller "$thisFunction" -Status i -Message "Found $($ScreenshotFiles.Count) screenshot file(s) to process" -Verbosity $Verbosity | |
| # Process each screenshot file | |
| $RenameCount = 0 | |
| $SkipCount = 0 | |
| $ErrorCount = 0 | |
| foreach ($file in $ScreenshotFiles) { | |
| $OriginalName = $file.Name | |
| # Remove the screenshot file extension from the filename for parsing | |
| $WorkingName = $OriginalName -replace ([regex]::Escape(".$Extension") + '$'), '' | |
| # Attempt to detect a video extension and extract the base name | |
| $DetectedVideoExtension = $null | |
| $BaseName = $null | |
| foreach ($ve in $VideoExtensions) { | |
| $PatternWithString = [regex]::Escape(".$ve") + [regex]::Escape("$String") + '$' | |
| $PatternWithoutString = [regex]::Escape(".$ve") + '$' | |
| if ($WorkingName -match $PatternWithString) { | |
| # Matched Pattern 3: base.videoext<String> (e.g., movie.mkv_s) | |
| $DetectedVideoExtension = $ve | |
| $BaseName = $WorkingName -replace $PatternWithString, '' | |
| break | |
| } elseif ($WorkingName -match $PatternWithoutString) { | |
| # Matched Pattern 2: base.videoext (e.g., movie.mkv) | |
| $DetectedVideoExtension = $ve | |
| $BaseName = $WorkingName -replace $PatternWithoutString, '' | |
| break | |
| } | |
| } | |
| if (-not $DetectedVideoExtension) { | |
| # No video extension detected; check for Pattern 1: base<String> (e.g., movie_s) | |
| $StringPattern = [regex]::Escape("$String") + '$' | |
| if ($WorkingName -match $StringPattern) { | |
| $BaseName = $WorkingName -replace $StringPattern, '' | |
| $DetectedVideoExtension = $VideoExtensionFallback | |
| } else { | |
| # Filename does not match any expected pattern | |
| Vbs -Caller "$thisFunction" -Status w -Message "Could not parse filename pattern: $OriginalName" -Verbosity $Verbosity | |
| $SkipCount++ | |
| continue | |
| } | |
| } | |
| # Construct the new filename: base.videoext_screen.ext | |
| $NewName = -join("$BaseName", ".", "$DetectedVideoExtension", "$ScreenshotSubstitutionString", ".", "$Extension") | |
| # Skip if the name is already correct | |
| if ($NewName -eq $OriginalName) { | |
| Vbs -Caller "$thisFunction" -Status w -Message "Skipping (already renamed): $OriginalName" -Verbosity $Verbosity | |
| $SkipCount++ | |
| continue | |
| } | |
| # Skip if a file with the target name already exists | |
| $NewPath = Join-Path -Path "$Directory" -ChildPath "$NewName" | |
| if (Test-Path -LiteralPath "$NewPath") { | |
| Vbs -Caller "$thisFunction" -Status w -Message "Skipping (target exists): $OriginalName -> $NewName" -Verbosity $Verbosity | |
| $SkipCount++ | |
| continue | |
| } | |
| # Rename the file | |
| try { | |
| Rename-Item -LiteralPath $file.FullName -NewName "$NewName" -ErrorAction Stop | |
| Vbs -Caller "$thisFunction" -Status s -Message "Renamed: $OriginalName -> $NewName" -Verbosity $Verbosity | |
| $RenameCount++ | |
| } catch { | |
| Vbs -Caller "$thisFunction" -Status e -Message "Failed to rename: $OriginalName -> $NewName ($_)" -Verbosity $Verbosity | |
| $ErrorCount++ | |
| } | |
| } | |
| # Print summary | |
| Vbs -Caller "$thisFunction" -Status i -Message "Processing complete. Renamed: $RenameCount, Skipped: $SkipCount, Errors: $ErrorCount" -Verbosity $Verbosity | |
| #______________________________________________________________________________ | |
| ## End of script |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment