Skip to content

Various Changes and Improvements, Additional Flags #23

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 133 additions & 26 deletions ConvertTo-Jpeg.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

Param (
[Parameter(
Mandatory = $true,
Mandatory = $false,
Position = 1,
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true,
Expand All @@ -13,12 +13,35 @@ Param (
[String[]]
$Files,

[Parameter(
HelpMessage = "Output folder for converted files")]
[String]
[Alias("o")]
$OutputFolderPath,

[Parameter(
HelpMessage = "Fix extension of JPEG files without the .jpg extension")]
[Switch]
[Alias("f")]
$FixExtensionIfJpeg,

[Parameter(
HelpMessage = "Opens dialogs for input files and output folder if not supplied")]
[Switch]
[Alias("t")]
$InteractiveMode,

[Parameter(
HelpMessage = "Also copy unconverted image files to the output folder path")]
[Switch]
[Alias("u")]
$OutputUnconverted,

[Parameter(
HelpMessage = "Do not fail on conversion error, only write exception")]
[Switch]
$NoFailOnConvert,

[Parameter(
HelpMessage = "Remove existing extension of non-JPEG files before adding .jpg")]
[Switch]
Expand Down Expand Up @@ -53,6 +76,43 @@ Begin

Process
{
# If no files were passed and interactive mode, open a file dialog to select them
if (!$Files -and $InteractiveMode)
{
Add-Type -AssemblyName System.Windows.Forms
$FileBrowser = New-Object System.Windows.Forms.OpenFileDialog -Property @{
InitialDirectory = [Environment]::GetFolderPath('Desktop')
Title = "Select Files to Convert"
Multiselect = $true
# filter selection to supported filetypes
Filter = "Image Files (*.*)|*.BMP;*.DIB;*.RLE;*.CUR;*.DDS;*.DNG;*.GIF;*.ICO;*.ICON;*.EXIF;*.JFIF;*.JPE;*.JPEG;*.JPG;*.ARW;*.CR2;*.CRW;*.DNG;*.ERF;*.KDC;*.MRW;*.NEF;*.NRW;*.ORF;*.PEF;*.RAF;*.RAW;*.RW2;*.RWL;*.SR2;*.SRW;*.AVCI;*.AVCS;*.HEIC;*.HEICS;*.HEIF;*.HEIFS;*.WEBP;*.PNG;*.TIF;*.TIFF;*.JXR;*.WDP|All files (*.*)|*.*"
}
$null = $FileBrowser.ShowDialog()
$Files = $FileBrowser.FileNames
$FileBrowser.Dispose()
}

# If no output folder selected and interactive mode, select a folder with dialog
if (!$OutputFolderPath -and $InteractiveMode)
{
Add-Type -AssemblyName System.Windows.Forms
$OutputFolderBrowser = New-Object System.Windows.Forms.FolderBrowserDialog -Property @{
Description = "Select Folder to Place Converted Files"
}
$null = $OutputFolderBrowser.ShowDialog()
$OutputFolderPath = $OutputFolderBrowser.SelectedPath
$OutputFolderBrowser.Dispose()

# If no OutputFolderPath was selected, Throw Error
if (!$OutputFolderPath)
{
Throw "Output Folder Not Selected."
}
}

# Files that failed to be converted
$FailedFiles = New-Object -TypeName "System.Collections.ArrayList"

# Summary of imaging APIs: https://docs.microsoft.com/en-us/windows/uwp/audio-video-camera/imaging
foreach ($file in $Files)
{
Expand All @@ -61,10 +121,27 @@ Process
{
try
{
# Get SoftwareBitmap from input file
# Get SoftwareBitmap from input file, determine output path
$file = Resolve-Path -LiteralPath $file
$inputFile = AwaitOperation ([Windows.Storage.StorageFile]::GetFileFromPathAsync($file)) ([Windows.Storage.StorageFile])
$inputFolder = AwaitOperation ($inputFile.GetParentAsync()) ([Windows.Storage.StorageFolder])
$inputExtension = $inputFile.FileType
$outputFolder = $inputFolder
# Determine output file name
# Get name of original file, including extension
$fileName = $inputFile.Name
if ($RemoveOriginalExtension)
{
# If removing original extension, get the original file name without the extension
$fileName = $inputFile.DisplayName
}
# Add .jpg to the file name
$outputFileName = $fileName + ".jpg"

if ($OutputFolderPath)
{
$outputFolder = AwaitOperation ([Windows.Storage.StorageFolder]::GetFolderFromPathAsync($OutputFolderPath)) ([Windows.Storage.StorageFolder])
}
$inputStream = AwaitOperation ($inputFile.OpenReadAsync()) ([Windows.Storage.Streams.IRandomAccessStreamWithContentType])
$decoder = AwaitOperation ([Windows.Graphics.Imaging.BitmapDecoder]::CreateAsync($inputStream)) ([Windows.Graphics.Imaging.BitmapDecoder])
}
Expand All @@ -74,51 +151,71 @@ Process
Write-Host " [Unsupported]"
continue
}
# Check if image is already a jpg
if ($decoder.DecoderInformation.CodecId -eq [Windows.Graphics.Imaging.BitmapDecoder]::JpegDecoderId)
{
$extension = $inputFile.FileType
if ($FixExtensionIfJpeg -and ($extension -ne ".jpg") -and ($extension -ne ".jpeg"))
# Check if jpg files should have their extensions fixed
$ExtensionRequiresFix = $FixExtensionIfJpeg -and ($inputExtension -ne ".jpg") -and ($inputExtension -ne ".jpeg")
if ($ExtensionRequiresFix)
{
# Rename JPEG-encoded files to have ".jpg" extension
$newName = $inputFile.Name -replace ($extension + "$"), ".jpg"
AwaitAction ($inputFile.RenameAsync($newName))
Write-Host " => $newName"
$outputFileName = $inputFile.DisplayName + ".jpg"
}
else
{
# Skip JPEG-encoded files
Write-Host " [Already JPEG]"
$outputFileName = $inputFile.Name
}

# If OutputUnconverted and there is an OutputFolderPath
# Copy the existing file to the output folder
if ($OutputUnconverted -and $OutputFolderPath)
{
# Copy input file to output folder
Copy-Item -path $inputFile.Path -Destination $(Join-Path $outputFolder.Path $outputFileName)
Write-Host " => $(Join-Path $outputFolder.Path $outputFileName)"
continue
}
else
{
if ($ExtensionRequiresFix)
{
# Rename JPEG-encoded files to have ".jpg" extension
AwaitAction ($inputFile.RenameAsync($outputFileName))
Write-Host " => $(Join-Path $inputFolder.Path $outputFileName)"
}
else
{
# Skip JPEG-encoded files
Write-Host " [Already JPEG]"
}
continue
}
continue
}
$bitmap = AwaitOperation ($decoder.GetSoftwareBitmapAsync()) ([Windows.Graphics.Imaging.SoftwareBitmap])

# Determine output file name
# Get name of original file, including extension
$fileName = $inputFile.Name
if ($RemoveOriginalExtension)
{
# If removing original extension, get the original file name without the extension
$fileName = $inputFile.DisplayName
}
# Add .jpg to the file name
$outputFileName = $fileName + ".jpg"

# Write SoftwareBitmap to output file
$outputFile = AwaitOperation ($inputFolder.CreateFileAsync($outputFileName, [Windows.Storage.CreationCollisionOption]::ReplaceExisting)) ([Windows.Storage.StorageFile])
$outputFile = AwaitOperation ($outputFolder.CreateFileAsync($outputFileName, [Windows.Storage.CreationCollisionOption]::ReplaceExisting)) ([Windows.Storage.StorageFile])
$outputStream = AwaitOperation ($outputFile.OpenAsync([Windows.Storage.FileAccessMode]::ReadWrite)) ([Windows.Storage.Streams.IRandomAccessStream])
$encoder = AwaitOperation ([Windows.Graphics.Imaging.BitmapEncoder]::CreateAsync([Windows.Graphics.Imaging.BitmapEncoder]::JpegEncoderId, $outputStream)) ([Windows.Graphics.Imaging.BitmapEncoder])
$encoder.SetSoftwareBitmap($bitmap)
$encoder.IsThumbnailGenerated = $true

# Do it
AwaitAction ($encoder.FlushAsync())
Write-Host " -> $outputFileName"
Write-Host " -> $(Join-Path $outputFolder.Path $outputFileName)"
}
catch
{
# Report full details
throw $_.Exception.ToString()
if ($NoFailOnConvert)
{
# Report full details and add file to list
Write-Error $_.Exception
$FailedFiles.Add($file)
}
else
{
# Report full details
throw $_.Exception.ToString()
}
}
finally
{
Expand All @@ -127,4 +224,14 @@ Process
if ($outputStream -ne $null) { [System.IDisposable]$outputStream.Dispose() }
}
}

if ($FailedFiles.Count -gt 0)
{
Write-Host "The following files failed to convert."
Write-Host "You may lack the required extensions or the files may be corrupt."
foreach ($file in $FailedFiles)
{
Write-Host $file
}
}
}
64 changes: 54 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,16 @@ Passing parameters:

```PowerShell
PS C:\T> .\ConvertTo-Jpeg.ps1 C:\T\Pictures\IMG_1234.HEIC C:\T\Pictures\IMG_5678.HEIC
C:\T\Pictures\IMG_1234.HEIC -> IMG_1234.HEIC.jpg
C:\T\Pictures\IMG_5678.HEIC -> IMG_5678.HEIC.jpg
C:\T\Pictures\IMG_1234.HEIC -> C:\T\Pictures\IMG_1234.HEIC.jpg
C:\T\Pictures\IMG_5678.HEIC -> C:\T\Pictures\IMG_5678.HEIC.jpg
```

Pipeline via `dir`:

```PowerShell
PS C:\T> dir C:\T\Pictures | .\ConvertTo-Jpeg.ps1
C:\T\Pictures\IMG_1234.HEIC -> IMG_1234.HEIC.jpg
C:\T\Pictures\IMG_5678.HEIC -> IMG_5678.HEIC.jpg
C:\T\Pictures\IMG_1234.HEIC -> C:\T\Pictures\IMG_1234.HEIC.jpg
C:\T\Pictures\IMG_5678.HEIC -> C:\T\Pictures\IMG_5678.HEIC.jpg
C:\T\Pictures\Kitten.jpg [Already JPEG]
C:\T\Pictures\Notes.txt [Unsupported]
```
Expand All @@ -41,10 +41,41 @@ Pipeline via `Get-ChildItem`:

```PowerShell
PS C:\T> Get-ChildItem C:\T\Pictures -Filter *.HEIC | .\ConvertTo-Jpeg.ps1
C:\T\Pictures\IMG_1234.HEIC -> IMG_1234.HEIC.jpg
C:\T\Pictures\IMG_5678.HEIC -> IMG_5678.HEIC.jpg
C:\T\Pictures\IMG_1234.HEIC -> C:\T\Pictures\IMG_1234.HEIC.jpg
C:\T\Pictures\IMG_5678.HEIC -> C:\T\Pictures\IMG_5678.HEIC.jpg
```

Dialog via `InteractiveMode` (`-t`) flag:

```PowerShell
PS C:\T> .\ConvertTo-Jpeg.ps1 -t
C:\T\Pictures\IMG_1234.HEIC -> C:\T\Pictures\IMG_1234.HEIC.jpg
C:\T\Pictures\IMG_5678.HEIC -> C:\T\Pictures\IMG_5678.HEIC.jpg
```


### Output Folder

Choosing an output folder path for converted files.
If no output path is supplied, each converted file will be placed in the same
folder as the original.
If an output path is supplied, it will be included in the file conversion log.

Paramater via `OutputFolderPath` (`-o`) flag:

```PowerShell
PS C:\T> .\ConvertTo-Jpeg.ps1 -o C:\T\Documents C:\T\Pictures\IMG_1234.HEIC
C:\T\Pictures\IMG_1234.HEIC -> C:\T\Documents\IMG_1234.HEIC.jpg
```

Dialog via `InteractiveMode` (`-t`) flag:

```PowerShell
PS C:\T> .\ConvertTo-Jpeg.ps1 -t C:\T\Pictures\IMG_1234.HEIC
C:\T\Pictures\IMG_1234.HEIC -> C:\T\Documents\IMG_1234.HEIC.jpg
```


### Renaming Files

Sometimes files have the wrong extension.
Expand All @@ -54,18 +85,31 @@ switch (alias `-f`).

```PowerShell
PS C:\T> dir C:\T\Pictures\*.HEIC | .\ConvertTo-Jpeg.ps1 -FixExtensionIfJpeg
C:\T\Pictures\IMG_1234 (Edited).HEIC => IMG_1234 (Edited).jpg
C:\T\Pictures\IMG_1234.HEIC -> IMG_1234.HEIC.jpg
C:\T\Pictures\IMG_ABCD.HEIC => C:\T\Pictures\IMG_ABCD.jpg
C:\T\Pictures\IMG_1234.HEIC -> C:\T\Pictures\IMG_1234.HEIC.jpg
```

### Output Unconverted Files

Also outputs existing files that don't require conversion but are images if an output path is specified by `-o` flag or InteractiveMode (`-t`).
Flag is `-OutputUnconverted` (`-u`.)
This includes existing JPEG-encoded files.

```PowerShell
PS C:\T> .\ConvertTo-Jpeg.ps1 -u -o C:\T\Documents C:\T\Pictures\IMG_1234.HEIC C:\T\Pictures\IMG_ABCD.jpg
C:\T\Pictures\IMG_1234.HEIC -> C:\T\Documents\IMG_1234.HEIC.jpg
C:\T\Pictures\IMG_ABCD.jpg => C:\T\Documents\IMG_5678.jpg
```


### Removing existing extensions

To remove the existing extension of a file, use the `-RemoveOriginalExtension` switch (alias `-r`).

```PowerShell
PS C:\T> dir C:\T\Pictures\*.HEIC | .\ConvertTo-Jpeg.ps1 -RemoveOriginalExtension
C:\T\Pictures\IMG_1234.HEIC -> IMG_1234.jpg
C:\T\Pictures\IMG_5678.HEIC -> IMG_5678.jpg
C:\T\Pictures\IMG_1234.HEIC -> C:\T\Pictures\IMG_1234.jpg
C:\T\Pictures\IMG_5678.HEIC -> C:\T\Pictures\IMG_5678.jpg
```

## Formats
Expand Down