Ad Space here

Direct Link: 3/2/2020Windows Spotlight Images, cool photos for backgrounds and more


Windows Spotlight Images - cool photos for backgrounds and more.

Example Spotlight Image

They're already on your computer, so how can you use them?


You've been there -- you walk into your home office and sit down at your computer, or maybe you open your laptop up sitting on the couch, or you arrive to work and go to your desk & work computer, and staring back at you from the locked/logon screen is a crisp, beautiful image. This image could be the same as yesterday, or maybe it's a new today; it might have even changed since the last time you saw it that day. If you have or use a Windows 10 PC, you've likely encountered this or a similar scenario. This feature is referred to as Windows Spotlight. In Microsoft's own words, "Windows Spotlight is an option for the lock screen background that displays different background images and occasionally offers suggestions on the lock screen. Windows Spotlight is available in all desktop editions of Windows 10." [1]


Now, that being said, many of these photos are quite stunning and could also make for ideal desktop backgrounds. They're already downloaded to your computer, you should just be able to use them, right? Sure! So... where are they? Well they're sort of hidden. Not hidden in the sense of you "can't" or "shouldn't" see them, but rather they're buried down in folders generated from automatic downloads for Windows 10 content. Fortunately you can just go there and get them. That is, if you can pick them out among other content files.

​​​​​​​

Where Spotlight Images live



The names of these file are hashes, or alphanumeric IDs, that are given to the image at the time of download. The other files in location are things like icons, adverts, and thumbnails. All of the files have no file extension, so they don't appear to be image files right away, but don't worry, they are. You can copy these files out to a different folder, and rename them all to include a .jpg extension so Windows recognizes them as picture files and sort through them, pulling out the larger resolution pictures. If there aren't a lot of files, or you only do this occasionally it's not overly difficult. But what if you want to grab all the backgrounds, and any new ones that are downloaded quicker and more easily?

.

Well thanks to the wonders of scripting, this can be done fairly easily. Each step in the (loosely defined) process above is fairly straightforward from a scripting perspective, so putting it all together shouldn't be that hard.



The Windows Spotlight images are actually stored in your user profile, so we're going to look there. If by chance you have multiple profiles on the machine, like for instance this is a shared machine or a work computer, you might want to check all of the profiles. This would require you to have adminstrative privileges on the machine. For the scope of this article, we will assume that you only want to look at YOUR profile and that you do not have adminstrative privileges on this machine. There will be a follow-up article covering the instance that you DO have admin privileges and wish to scan the entire (and possibly other) computers. 


Ok, so we don't want to mess with the actual Windows Spotlight files in their normal location, because that could cause issues. Instead, we will be copying the files out to another folder and work with the copies. So you need to have a folder ready to store these images. It can be a new folder or one that already exists.

In our script, let's define some variables:

Code:
$LocalImageStore = "C:\SpotlightImgs"
$UserPath = $ENV:UserProfile
$WindowsSpotlightStore = "AppData\Local\Packages\Microsoft.Windows.ContentDeliveryManager_cw5n1h2txyewy\LocalState\Assets"

LocalImageStore is what we'll call the folder where we plan to save our Spotlight images. I used C:\SpotlightImgs for this example. UserPath is the path to our user directory, this is retrieved from the UserProfile environment variable, which is automatically defined when you log in to the machine. WindowsSpotlightStore is the location of where the Windows Spotlight images are stored in the user profile. Fortunately this location is the same for everyone. 

Next, we'll make a subdirectory inside of our LocalImageStore path to store the final images.

Code:
[void](MKDIR "$LocalImageStore\_Done" -ErrorAction SilentlyContinue)
 

This makes a folder '_Done' inside of our LocalImageStore directory defined earlier. The '-ErrorAction SilentlyContinue' portion suppresses any error messages and the [void] suppresses any normal output. We'll add a little error checking later, but for now let's move on. .

Next we need to copy the files in the WindowsSpotlightStore to our LocalImageStore directory, rename them all to .JPG files and see which ones are actually the cool backgrounds and which ones are... not. To do the last step, we're going to use a little bit of .NET functionality to examine the picture dimensions from a script. The Spotlight images come in two resolutions, 1920x1080 and 1080x1920, likely to handle a rotated display. We're only going to grab the 1920x1080 images for this, but you should be able to tell what pieces to change when we're all done to grab the 1080x1920 versions if you also wanted those.

Code:
COPY "$UserPath\$WindowsSpotlightStore\*" $LocalImageStore -Force -ErrorAction SilentlyContinue
cmd /c REN $LocalImageStore\*.* *.jpg
Add-Type -AssemblyName System.Drawing
FOREACH ($CapturedFile IN (Get-ChildItem -File $LocalImageStore))
{
    $TestImg = New-Object System.Drawing.BitMap $CapturedFile.FullName
    IF (($TestImg.Width -eq 1920) -and ($TestImg.Height -eq 1080))
    {
        $TestImg.Dispose()
        Move-Item $CapturedFile.FullName "$LocalImageStore\_Done\" -ErrorAction SilentlyContinue
    }
    ELSE { $TestImg.Dispose() } 
}
 

First line here copies every file from the WindowsSpotlightStore directory to our LocalImageStore directory. The '-Force' causes the copies to overwrite existing files of the same name without prompting. The machine generated hash named files are for the most part unique, so you'd just be overwriting a file with itself. While it is technically possible to get duplicate named hashes, the likelihood is extremely low on the same computer in the same user profile. Now if you looked at ALL the user profiles over a large number of computers, you may run into a few different files that happen to be named the same. The '-ErrorAction SilentlyContinue' portion suppresses any error messages, which may be access denied errors, disk space errors, or other transient errors. You can remove this option if you need to see these error messages to troubleshoot a problem, but for now let's hide any of those nasty red error messages

The second line actually uses the old Windows Command shell to quickly rename every file in our folder to the same name but with the .jpg extension. While I could have done this in pure PowerShell, this is much simpler as it only takes one line and it's not overly hard to see what it's doing. Rename every file (*.*) to the same name, but with .jpg (*.jpg). 

The third line adds the .Net library we need to look at the detailed image attributes so we can find the right resolution. The next part is a FOREACH loop that goes through each file that we've copied and tries to process it as an image file. Now don't worry about it saying BitMap and not JPG, it's just how the .NET functions are named. After each file is opened as an image, we look at it's resolution as "Width" and "Height" (since the resolution is denoted as WxH) and see if it equals 1920x1080. If it DOES match, we close the file and then move it into the '_Done' directory we made earlier. The .Dispose() function closes the file and releases the file lock on it, which is what allows us to be able to move the file. If we did not have that line, the Move-Item would fail because it would say that the file is already in use. Now if the file did NOT match our desired resolution, we close the file and do nothing, moving on to the next file in the folder. .


After this loop has finished, every glorious 1920x1080 Windows Spotlight image that was in your user profile is now in the '_Done' folder in the LocalImageStore directory. So we should probably clean up the other files that didn't meet our criteria.

Code:
FOREACH ($Straggler IN (Get-ChildItem -File $LocalImageStore -Filter *.jpg)) { Remove-Item $Straggler.FullName -Force }
 .

This line loops through all of the JPG files in the LocalImageStore directory (that weren't moved by the previous loop) and deletes them.

***One very important thing to note here: If for any reason $LocalImageStore is not defined, this line will remove every JPG file from whatever the execution directory is. There is a level of user/scripter responsibility here in that you need to make sure that $LocalImageStore is not set to a bad location or one with other picture files in it, because they will be deleted. You can leave this line out entirely if you'd prefer to not have the script do any automatic cleanup and would rather clean up the leftover files by hand.


So our entire script so far looks like this:

Code:
$LocalImageStore = "C:\SpotlightImgs"
$UserPath = $ENV:UserProfile
$WindowsSpotlightStore = "AppData\Local\Packages\Microsoft.Windows.ContentDeliveryManager_cw5n1h2txyewy\LocalState\Assets"

[void](MKDIR "$LocalImageStore\_Done" -ErrorAction SilentlyContinue)

COPY "$UserPath\$WindowsSpotlightStore\*" $LocalImageStore -Force -ErrorAction SilentlyContinue
cmd /c REN $LocalImageStore\*.* *.jpg
Add-Type -AssemblyName System.Drawing
FOREACH ($CapturedFile IN (Get-ChildItem -File $LocalImageStore))
{
    $TestImg = New-Object System.Drawing.BitMap $CapturedFile.FullName
    IF (($TestImg.Width -eq 1920) -and ($TestImg.Height -eq 1080))
    {
        $TestImg.Dispose()
        Move-Item $CapturedFile.FullName "$LocalImageStore\_Done\" -ErrorAction SilentlyContinue
    }
    ELSE { $TestImg.Dispose() } 
}

FOREACH ($Straggler IN (Get-ChildItem -File $LocalImageStore -Filter *.jpg)) { Remove-Item $Straggler.FullName -Force }
 

You can copy and paste that into a PowerShell window and it will run or you can save it as a .ps1 and run it when you choose. 

​​​​​​​

Script run



While this is functional, it doesn't feel complete to me. I like to polish up my scripts a bit and add some presentation to them. I'm going to work from the bottom up, going from the simple to more complex additions to this script. 

First, at the bottom, we can add a friendly success message.

​​​​​​​

Code:
Write-Host "Windows Spotlight pictures acquired!" -ForegroundColor Cyan
Write-Host "Images saved to '" -ForegroundColor Cyan -NoNewLine
Write-Host "$LocalImageStore\_Done" -ForegroundColor Green -NoNewLine
Write-Host "'" -ForegroundColor Cyan
 

In PowerShell, Write-Host prints to the screen and not standard output, which means the text isn't redirected or suppressed as might happen with normal text output. It also allows us the option to add text colors, and as shown above, multiple colors on the same line. The first line prints a line of text in a bright Cyan color. The second line does the same, but with the -NoNewLine option, it does not move the cursor to the next line. This means that the next thing printed to the screen will be on the same line. So the third line prints a message, in this case a folder location, in a different color on the same line. Not functionally required, but visually appealing. The last line just finishes the line with a single quote of the original color. 

Moving up in the script, let's look at the error checking surround the '_Done' folder and LocalImageStore that I skipped over earlier in this article. Let's start with our original line:


Code:
[void](MKDIR "$LocalImageStore\_Done" -ErrorAction SilentlyContinue)
 

This makes a few assumptions. First, there is no checking to see if the folder exists already. Second, there is no verification that the folder was successfully created after this command. Lastly, as a result of no verification, the script is allowed to continue if this crucial step fails. .

The following block of code solves these 3 problems:

Code:
IF (!(TEST-PATH "$LocalImageStore\_Done"))
{
    [void](MKDIR "$LocalImageStore\_Done" -ErrorAction SilentlyContinue)
    IF (!(TEST-PATH "$LocalImageStore\_Done"))
    {
        Write-Host "Error! '_Done' folder cannot be created in image store '$LocalImageStore'" -ForegroundColor RED
        Write-Host "Check permissions, or make folder manually and try again." -ForegroundColor RED
        EXIT 5
    }
}
 

First, we test to see if the folder already exists, and if it does just skip and continue on. Then we see our original line in there, but after that line we check again to see if the folder exists. This will show us whether or not the previous step worked even though we suppressed any errors. If the folder still does not exist, this will create problems for the rest of the script, so we print an error message in red text to the console and exit, passing a return code of 5. In Windows-land, a return code of 5 means "Access denied", which is most likely the error encountered. We could delve more into exactly which error code it should be and handle different cases, but that's starting to get too deep for a script just meant to gather up pretty pictures. 

Now we move up to the top of the script, specifically this line:


Code:
$LocalImageStore = "C:\SpotlightImgs"
 

The thing with this line is that it hardcodes the script to a specific directory. If you wanted to use a different folder, or run this on another computer, you might have to edit the script each time. What if you accidentally made other changes? Or more aptly, do you really want to have to modify the script each time to be able to use it? If we just change this logic to simply ASK what folder you wanted to save the files to, then you'd never have to change the script. It would be dynamic enough that you just pick a folder, and whatever the folder is, gets the pictures. The behaviour would be the same every time you ran it on any computer you run it on. Sounds good, right? So how do we do this? Well with a little more .NET and Windows Forms (but really basic). We don't need anything fancy, just one of those built-in Windows "Choose a folder" type dialogs. Fortuntately this is only a few more lines of code than we already had. 

Behold the following:

Code:
[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") > $NUL

$FolderSelectDialog = New-Object Windows.Forms.FolderBrowserDialog
$FolderSelectDialog.Description = "Select Location to save Spotlight images"

$DialogResult = $FolderSelectDialog.ShowDialog()

IF ($DialogResult -eq "OK") { $LocalImageStore = $FolderSelectDialog.SelectedPath }
ELSE { EXIT 0 }
 

In the first line we just load up the ability to use Windows Forms and related items into PowerShell. The next line is creating a new instance of the Folder Browser Dialog window. The next line puts in a little text to display what it is we'd be looking for when we run this. Next we have the line that actually pops up this dialog. We set that line equal to its own variable DialogResult to capture the result of what we click in the dialog box. The script effectively pauses here while the Windows Folder Browser Dialog box is open. Here is what it will look like: 

Browse Folder Dialog box



You select a folder and either click OK or Cancel. This button click result is saved in the DialogResult variable. We then check to see if the result was the OK button or not. If it WAS the OK button, we grab the folder selected from the actual dialog box itself and continue. If the Cancel button was clicked instead, we stop the script with no errors, just a clean exit. 

Now if we take these recent changes into account, the final version of the script should look like the following:

Code:
[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") > $NUL

$FolderSelectDialog = New-Object Windows.Forms.FolderBrowserDialog
$FolderSelectDialog.Description = "Select Location to save Spotlight images"

$DialogResult = $FolderSelectDialog.ShowDialog()

IF ($DialogResult -eq "OK") { $LocalImageStore = $FolderSelectDialog.SelectedPath }
ELSE { EXIT 0 }

$UserPath = $ENV:UserProfile
$WindowsSpotlightStore = "AppData\Local\Packages\Microsoft.Windows.ContentDeliveryManager_cw5n1h2txyewy\LocalState\Assets"

IF (!(TEST-PATH "$LocalImageStore\_Done"))
{
    [void](MKDIR "$LocalImageStore\_Done" -ErrorAction SilentlyContinue)
    IF (!(TEST-PATH "$LocalImageStore\_Done"))
    {
        Write-Host "Error! '_Done' folder cannot be created in image store '$LocalImageStore'" -ForegroundColor RED
        Write-Host "Check permissions, or make folder manually and try again." -ForegroundColor RED
        EXIT 5
    }
}

COPY "$UserPath\$WindowsSpotlightStore\*" $LocalImageStore -Force -ErrorAction SilentlyContinue

cmd /c REN $LocalImageStore\*.* *.jpg

Add-Type -AssemblyName System.Drawing

FOREACH ($CapturedFile IN (Get-ChildItem -File $LocalImageStore))
{
    $TestImg = New-Object System.Drawing.BitMap $CapturedFile.FullName
    IF (($TestImg.Width -eq 1920) -and ($TestImg.Height -eq 1080))
    {
        $TestImg.Dispose()
        Move-Item $CapturedFile.FullName "$LocalImageStore\_Done\" -ErrorAction SilentlyContinue
    }
    ELSE { $TestImg.Dispose() } 
}

FOREACH ($Straggler IN (Get-ChildItem -File $LocalImageStore -Filter *.jpg)) { Remove-Item $Straggler.FullName -Force }

Write-Host "Windows Spotlight pictures acquired!" -ForegroundColor Cyan
Write-Host "Images saved to '" -ForegroundColor Cyan -NoNewLine
Write-Host "$LocalImageStore\_Done" -ForegroundColor Green -NoNewLine
Write-Host "'" -ForegroundColor Cyan


Just 47 little lines (including whitespace/blanklines) of code. The above code will run as is, but you can always make additions or changes to it if you'd like. Add comments to the code for notes, or explanation of steps, remove messages to reduce lines, add more detailed messages if desired. Stylistic changes can be made too to put the opening { of each block on the previous line, reducing the number of lines needed. It doesn't affect function, just style. I've personally always preferred having my curly braces on their own lines (unless I need to condense code) so they visually line up, but that's just me. 

Unfortunately, you'll have to rename the final output picture files on your own, unless you don't mind keeping their long hash name. There is no property or context of the picture file to be able to rename it to anything meaningful in the script. I just keep them as the same name and put them in a folder for a SlideShow   background, so the names aren't a big deal for me. Also this way I don't end up with multiple copies of the same file because I renamed one and then when I ran the script again it re-copied the file with the original hash name. 


I hope this article has been helpful and shown you where the Spotlight images are and a little PowerShell along with it. There will be a follow-up article based on this where I modify the script to look at all user profiles on a machine and also work for remote machines, which is more applicable to adminsitrators in business network environments. That one will be a little more code, a little less talk.



 Let me know your thoughts, comments, suggestions, or issues.

Please share this article and this site if you found it helpful.

Have a great day!



--Mike Merola


​​​​​​​ ​​​​​​​

Sources:

[1] https://docs.microsoft.com/en-us/windows/configuration/windows-spotlight

First two comments:



Available Blog posts:






If you like this site, help us out.
Spread the word and share it with others!