I'd like to write a simple program to extract the windows spotlight images from the system directory where they live to a folder where I can more easily get at them. So far I have this:
@ECHO OFF
if not exist "%homedrive%%homepath%\Downloads\Spotlight" mkdir %homedrive%%homepath%\Downloads\Spotlight
xcopy /s %homedrive%%homepath%\AppData\Local\Packages\Microsoft.Windows.ContentDeliveryManager_cw5n1h2txyewy\LocalState\Assets\ %homedrive%%homepath%\Downloads\Spotlight
ren %homedrive%%homepath%\Downloads\Spotlight\* *.jpg
Which successfully copies the contents of the spotlight directory into a new folder, then renames all the files to make them JPGs. However, a number of miscellaneous icons come too, as well as the 'portrait mode' spotlight images. I'd like to delete them by checking each .jpg in the output folder to see if its width is less than 1920 pixels, and deleting the image if so.
I went to AI to try and solve my problem, but failed:
set "SpotlightFolder=%USERPROFILE%\Downloads\Spotlight"
:: Iterate over all .jpg files and delete those with width smaller than 1920 pixels
for %%F in ("%SpotlightFolder%\*.jpg") do (
for /f %%W in ('powershell -command "& {(Get-ImageDimensions '%%~F').Width}"') do set "Width=%%W"
if !Width! lss 1920 (
echo Deleting: %%~nxF
del "%%F"
) else (
echo Keeping: %%~nxF
)
)
:: PowerShell script to get image dimensions
<#
function Get-ImageDimensions {
param([string]$filePath)
$image = [System.Drawing.Image]::FromFile($filePath)
$width = $image.Width
$image.Dispose()
$width
}
#>
This returns a nebulous 'The syntax of the command is incorrect' error, and deletes all the images in the folder, regardless of dimension. Any help would be appreciated.
The solution is marked below, but for anyone with the same question, this is the complete script I've created to solve my problem; since the answer ended up being in Powershell script I rewrote the beginning of my code in that language.
$minWidth = 1900
$SpotlightFolder = "${env:USERPROFILE}\Downloads\Spotlight"
If(!(test-path -PathType container $SpotlightFolder))
{
New-Item -ItemType Directory -Path $SpotlightFolder
}
xcopy /s "$env:USERPROFILE\AppData\Local\Packages\Microsoft.Windows.ContentDeliveryManager_cw5n1h2txyewy\LocalState\Assets\" $SpotlightFolder
#Get-ChildItem -Path $SpotlightFolder | Rename-Item -NewName { $_.Name -replace '$.*', '.jpg' }
#Rename all items to make them JPGs, accounting for the possibility that they already exist (avoid 'cannot overwrite via rename' error)
Get-ChildItem -Path $SpotlightFolder | ForEach-Object {
$newName = $_.BaseName + ".jpg"
$newPath = Join-Path -Path $_.DirectoryName -ChildPath $newName
if (-not (Test-Path $newPath)) {
Rename-Item -Path $_.FullName -NewName $newName
}
}
#Delete all items which do not have an extension (those which already existed and didn't get renamed)
Get-ChildItem -Path $SpotlightFolder | Where-Object { -not $_.Extension } | Remove-Item -Force
$widthColumnNum = 176 # or 165 or some other value, see below for details
$shell = New-Object -COMObject Shell.Application
$shellfolder = $shell.Namespace($SpotlightFolder)
#Uncomment the Write-Output code and run to see Windows' list of attributes. Find the number corresponding to 'Width'. Replace '176' if it is different for you.
$objFolder = (New-Object -ComObject Shell.Application).Namespace('c:\')
for ($columnNumber = 0; $columnNumber -lt 500; ++$columnNumber)
{
$columnName = $objFolder.GetDetailsOf($objFolder.Items, $columnNumber)
if ($columnName)
{
#Write-Output "$(([string]$columnNumber).PadLeft(3)) $columnName"
}
}
Get-ChildItem -Filter *.jpg $SpotlightFolder | Where-Object {
$shellfile = $shellfolder.ParseName($_.Name)
$w = $shellfolder.GetDetailsOf($shellfile, $widthColumnNum)
[int]([regex]::Match($w, "\d+").Value) -lt $minWidth
} | Remove-Item #-WhatIf
Thanks for your help.
Using System.Drawing.Image
might not be very efficient because it actually decodes and loads the whole image. The Windows Shell can read those metadata just by reading the header and other related sections so performance should be better. In fact it must be better, otherwise the details view in Windows Explorer will suffer. They probably even cache the info for reuse the next time
The metadata can be accessed directly using Shell COM object's Folder.GetDetailsOf()
method so to get the images' widths cheaply you can use this simple PowerShell script:
$minWidth = 1920
$SpotlightFolder = "${env:USERPROFILE}\Downloads\Spotlight"
$widthColumnNum = 176 # or 165 or some other value, see below for details
$shell = New-Object -COMObject Shell.Application
$shellfolder = $shell.Namespace($SpotlightFolder)
Get-ChildItem -Filter *.jpg $SpotlightFolder | Where-Object {
$shellfile = $shellfolder.ParseName($_.Name)
$w = $shellfolder.GetDetailsOf($shellfile, $widthColumnNum)
[int]([regex]::Match($w, "\d+").Value) -lt $minWidth
} | Remove-Item -WhatIf
Basically this lists all *.jpg files in the specified path, filter the ones with Width
less than $minWidth
to pass to Remove-Item
. Remove the -WhatIf
option to do actual removing
The column number 165 is magic which is taken from here, or 176 based on this. You can run the PowerShell script in What options are available for Shell32.Folder.GetDetailsOf(..,..)? to find the actual value on your system in case it's different:
$objFolder = (New-Object -ComObject Shell.Application).Namespace('c:\')
$(for ($columnNumber = 0; $columnNumber -lt 500; ++$columnNumber)
{
$columnName = $objFolder.GetDetailsOf($objFolder.Items, $columnNumber)
if ($columnName)
{
[PsCustomObject]@{
ColumnNumber = ([string]$columnNumber).PadLeft(3);
ColumnName = $columnName
}
}
}) | where { $_.ColumnName -like "*width*" -or $_.ColumnName -like "*len*" }
I've already tested it on my PC1 and it works, the image width is field 176 as expected. Of course you can also automate the method to get that magic number but I'm keeping it simple here. You can remove the where {}
command to list all the available columns
For more information about metadata reading check
Because it uses COM objects, it can be called from any languages that support COM objects, for example a JS, VBS or hybrid VBS/hybrid JS
1Here's the sample output on my PC:
ColumnNumber ColumnName
------------ ----------
27 Length
165 Filename
176 Width
262 Focal length
263 35mm focal length
265 Lens maker
266 Lens model
322 Frame width