2

I'll be the first to admit that PowerShell isn't my strong suit, but I've pieced together the following after an evening of digging around on the internet. The end goal is to organize a huge drive of images by the DateTaken, as well as sidecar XMP files if they exist. It's probably not the most elegant code, but it almost works.

The last issue, which I can't figure out, is that the foreach loop only executes once per filename, regardless of extension. For example, only DSC00001.arw, DSC00001.xmp, or DSC00001.jpg would be processed.

Any points in the right direction would be appreciated.

Thanks!

$Folders = (Get-ChildItem -Path F:\ -Recurse -Directory -Force).FullName

$objShell  = New-Object -ComObject Shell.Application
$Folders | % {

    $objFolder = $objShell.namespace($_)
    foreach ($File in $objFolder.items()) { 
            if (($File | Split-Path -Extension) -in ".arw",".gif",".tiff",".jpg",".png",".nef") {
                Write-Host $File.Name`t -NoNewline -ForegroundColor Green
                try {
                    $DateTaken = ($objFolder.getDetailsOf($File,12) -replace [char]8206)  -replace [char]8207
                    $DateTaken = [DateTime]::ParseExact($DateTaken, "g", $null)
                    $Year = $DateTaken.ToString('yyyy')
                    $Date = $DateTaken.ToString('yyyy-MM-dd')
                    Write-Host $Date`t -ForegroundColor Blue -NoNewline
                }
                catch {
                    $Year = 'Other'
                    $Date = 'Other'
                    Write-Host $Date`t -ForegroundColor DarkRed -NoNewline
                }
                finally {
                    $DatePath = (Join-Path (Join-Path F:\ $Year ) $Date)
                }
                write-Host $File.Path -> (Join-Path $DatePath ($File | Split-Path -Leaf))-NoNewline
                #Move-Item $File.Path (Join-Path $DatePath ($File | Split-Path -Leaf))
                Write-Host Completed`n -NoNewline

                if (Test-Path (Join-Path ($File | Split-Path) (($File | Split-Path -LeafBase) + '.xmp'))) {
                    Write-Host XMP:`t -ForegroundColor Magenta -NoNewLine
                    Write-Host (Join-Path ($File | Split-Path) (($File | Split-Path -LeafBase) + '.xmp')) -> (Join-Path ($DatePath) (($File | Split-Path -LeafBase) + '.xmp'))
                    #Move-Item (Join-Path ($File | Split-Path) (($File | Split-Path -LeafBase) + '.xmp')) (Join-Path ($DatePath) (($File | Split-Path -LeafBase) + '.xmp'))
                }
            }else {
                Write-Host $File.Name is not an image `n -NoNewline -ForegroundColor DarkYellow    
            }
    }

}
Michael
  • 23
  • 3
  • 1
    Have you verified that `$objShell.namespace("C:\folder\with\multiple\file\extensions").items()` actually return all expected items? – Mathias R. Jessen Sep 10 '20 at 21:22
  • 1
    you are doing a huge number of splits & joins ... and repeating them fairly often. try doing all that _once per item_ and see if you can see where the error is. ///// my 1st suspicion is that you are munging your files somewhere in that overly complex set of splits & joins. [*grin*] – Lee_Dailey Sep 10 '20 at 22:52
  • @MathiasR.Jessen is on the right track. $objShell.Folder.items() doesn't return these items in the first place. I used the same loop and found that only one file is returned no matter how many times the name is used with different extensions. – Michael Sep 11 '20 at 22:14

1 Answers1

1

I'm not sure why $objFolder.items() doesn't actually return all expected files, but I would suggest using PowerShell's built-in file system provider to discover the actual files in each folder and then use $objFolder.ParseName() to obtain a reference you can pass to GetDetailsOf():

$Folders = (Get-ChildItem -Path F:\ -Recurse -Directory -Force).FullName

$objShell  = New-Object -ComObject Shell.Application
$Folders | % {

    $objFolder = $objShell.NameSpace($_)

    foreach ($FileInfo in Get-ChildItem -LiteralPath $_ -File -Force) { # <-- trick is right here, use Get-ChildItem to discover the files in the folder
        if($FileInfo.Extension -in ".arw",".gif",".tiff",".jpg",".png",".nef"){
            $File = $objFolder.ParseName($FileInfo.Name)                # <-- here we resolve the corresponding file item with shell app
    
            # ... resolve and parse dateTaken here, just like before

            # *.xmp file path might be easier to construct like this
            $xmpPath = $FileInfo.FullName -replace "$($FileInfo.Extension)`$",'.xmp'
            if(Test-Path -LiteralPath $xmpPath){
                 # ...
            }
        }
    }
}
Mathias R. Jessen
  • 157,619
  • 12
  • 148
  • 206
  • This worked perfectly. Funny enough, I originally wrote the code using a second Get-ChildItem, but couldn't figure out how to pass that into ``GetDetailsOf()``. – Michael Sep 11 '20 at 23:55