ShellOfPower
ShellOfPower

Reputation: 163

Get Attributes listed in the "Details" Tab with Powershell

I'm trying to retrieve some extended file attributes (mp3 files to be precise) listed in the "Details" tab, section "Media" with PowerShell.

There are additional attributes like bitrate, genre, album, etc.

Sadly Get-ItemProperty -Path $pathtofile | Format-List does not return those.

Get-ItemProperty -Path $pathtofile | Get-Member does not list them either.

Strange enough, Get-ItemProperty returns the same output as Get-ChildItem.
I've tried different files (even non mp3s) and PowerShell does not list the "detail" attributes anywhere.

Where does windows store these? Also how can one list them?

Upvotes: 16

Views: 44473

Answers (4)

MAZ
MAZ

Reputation: 121

EDIT: I merged my original code snippets below, into one function, as suggested by RiverHeart.

<#
.SYNOPSIS
    Get a list of details with columns [ ID | Name | Value ] of a given file or directory (Name and Value are in system language)
    By default, it selects all fields with ID -1 to 320 (inclusive) that have a Value (same as `Get-Details . false $(-1..320)`)
.DESCRIPTION
    Get a list of details with columns [ ID | Name | Value ] of a given file or directory (Name and Value are in system language)
    By default, it selects all fields with ID -1 to 320 (inclusive) that have a Value (same as `Get-Details . false $(-1..320)`)
    - ID -1 is the "info tip information" of the item
    - Negative IDs except -1 don't exist (when tested with file and folder including empty fields)
    - IDs 37, 38, 39, 40, 41, 59, 170, 171, 205, 206, 207, and 218 don't exist (when tested with file and folder including empty fields)
    - ID 296 has no Name but can have a Value (when tested with file and folder including empty fields)
    - Value of ID -1 of an empty textfile might be "Type: Text Document \n Size: 0 Bytes \n Date modified: 10/21/2023 00:00" (every " \n " indicates a line break)
    - Value of ID -1 of a non-empty directory with mixed files might be "Date created: 10/21/2023 00:00"
.Parameter Item
    An existing and accessible file or directory on this computer (must be within a folder, so no drives)
.Parameter IncludeEmptyFields
    If true, also includes fields with empty Value (never includes fields that have no Name and no Value)
    Default false
.Parameter FieldIDs
    Select only fields that are in this list of IDs (but also according to IncludeEmptyFields)
    Default null (selects fields with IDs -1 to 320 inclusive)
.INPUTS
    Item (file or directory) can be taken from the pipeline
.OUTPUTS
    A list of { ID, Name, Value } for each field selected (where Name and Value are in system language)
.EXAMPLE
    PS> # [file: test.txt] [do include empty fields] [only get fields: file name, file size, media authors, media title]
    PS> Get-Details ".\test.txt" $true 0, 1, 20, 21
    ID Name    Value
    -- ----    -----
     0 Name    test.txt
     1 Size    0 Bytes
    20 Authors
    21 Title
.EXAMPLE
    PS> # same as the previous example, but remove the ID column and sort by the Name column (ascending)
    PS> Get-Details ".\test.txt" $true 0, 1, 20, 21 | Select-Object -ExcludeProperty ID | Sort-Object Name
    Name    Value
    ----    -----
    Authors
    Size    0 Bytes
    Name    test.txt
    Title
.LINK
    https://learn.microsoft.com/en-us/windows/win32/shell/folder-getdetailsof
.LINK
    https://stackoverflow.com/questions/22382010/what-options-are-available-for-shell32-folder-getdetailsof/62279888#62279888
.LINK
    https://stackoverflow.com/questions/49214905/get-attributes-listed-in-the-details-tab-with-powershell/77252825#77252825
#>
function Get-Details{
    [CmdletBinding()]
    param(
        [Parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true, HelpMessage = "An existing and accessible file or directory on this computer (must be within a folder, so no drives)")]
        [AllowNull()]
        $Item,
        [Parameter(Position = 1, ValueFromPipelineByPropertyName = $true, HelpMessage = "If true, also includes fields with empty Value (never includes fields that have no Name and no Value)")]
        [bool]
        $IncludeEmptyFields = $false,
        [Parameter(Position = 2, ValueFromPipelineByPropertyName = $true, HelpMessage = "Select only fields that are in this list of IDs (but also according to IncludeEmptyFields)")]
        [int[]]
        $FieldIDs = $null
    )
    if($null -eq $Item){ throw "Received null" }
    if($Item -eq ""){ throw "Received an empty string" }
    $fileOrFolder = Get-Item $Item -ErrorAction SilentlyContinue
    if($null -eq $fileOrFolder -or -Not $fileOrFolder.Exists){ $fileOrFolder = Get-Item -LiteralPath $Item -ErrorAction SilentlyContinue }
    if($null -eq $fileOrFolder -or -Not $fileOrFolder.Exists){ throw "Couldn't find item: $Item" }
    $folder = $fileOrFolder.Directory
    if($null -eq $folder){ $folder = $fileOrFolder.Parent }
    if($null -eq $folder){ throw "Item must be within a folder" }
    $objShell = New-Object -ComObject Shell.Application
    $objFolder = $objShell.Namespace($folder.FullName)
    $objFile = $objFolder.ParseName($fileOrFolder.Name)
    [PSCustomObject[]] $list = @()
    if($null -ne $FieldIDs){
        $FieldIDs | Sort-Object | Get-Unique | ForEach-Object {
            $FieldValue = $objFolder.GetDetailsOf($objFile, $_)
            $FieldName = $objFolder.GetDetailsOf($null, $_)
            if($FieldValue -or ($IncludeEmptyFields -and $FieldName)){ $list += [PSCustomObject]@{ ID = $_; Name = $($FieldName); Value = $($FieldValue) } }
        }
    }else{
        foreach($id in -1..320){
            $FieldValue = $objFolder.GetDetailsOf($objFile, $id)
            $FieldName = $objFolder.GetDetailsOf($null, $id)
            if($FieldValue -or ($IncludeEmptyFields -and $FieldName)){ $list += [PSCustomObject]@{ ID = $id; Name = $($FieldName); Value = $($FieldValue) } }
        }
    }
    return $list
}

Note that Name and Value columns are in system language.

Also found this answer on a similar question, that lists all field names (in english) with the ids, except for 296 which has no name and -1 that also has no name (The "info tip information" according to the very limited documentation).

EDIT 2: fixed edge case in above code where file/folder names include [] which are wildcards...
→ Try to get the file/folder normal (with wildcard support) first then with -LiteralPath to ignore wildcards, to get the file/folder item in either case (no wildcards/wildcards/accidental wildcards) and only fail if neither can find the file/folder.


Based on JefNet answer.

If you want the info for just one specific file, it's too much to iterate over every file in the directory tree (takes ages for large directories), so here is this instead.

Get all information (incl. empty fields):

$objShell = New-Object -ComObject Shell.Application
$objFolder = $objShell.Namespace((Get-Item .).FullName)

$filenameWithExtension = "example.mp3"

$objFile = $objFolder.ParseName($filenameWithExtension)
$fileMeta = [ordered]@{}
for($id = 0; $id -le 266; $id++){
    $fileMeta[ $($objFolder.GetDetailsOf($objFolder, $id)) ] = $(
        if($objFolder.GetDetailsOf($objFile, $id)){
            $($objFolder.GetDetailsOf($objFile, $id))
        }else{ "" }
    )
}

# print ordered hashtable
$fileMeta

Get only non-empty fields:

$objShell = New-Object -ComObject Shell.Application
$objFolder = $objShell.Namespace((Get-Item .).FullName)

$filenameWithExtension = "example.mp3"

$objFile = $objFolder.ParseName($filenameWithExtension)
$fileMeta = [ordered]@{}
for($id = 0; $id -le 266; $id++){
    if($objFolder.GetDetailsOf($objFile, $id)){
        $fileMeta[
            $($objFolder.GetDetailsOf($objFolder, $id))
        ] = $($objFolder.GetDetailsOf($objFile, $id))
    }
}

# print ordered hashtable
$fileMeta

PS.: the $($objFolder.GetDetailsOf($objFolder, $id)) prints the field name in the system language and not always English, so use $id as the key if you need it to be consistent with different languages.

PPS.: If you only want one field value, replace the hashtable and for-loop with this

$fileMetaAuthors = $(
    # 20 is the "Authors" field
    if($objFolder.GetDetailsOf($objFile, 20)){
        $($objFolder.GetDetailsOf($objFile, 20))
    }else{ "" }
)

# print "Authors" of the file
$fileMetaAuthors

PPPS.: all of these expect the file to be in the current folder. If this is not the case, modify the Get-Item . to point to the correct folder.

Upvotes: 4

JefNet
JefNet

Reputation: 41

I had a look at Ed Wilson's post. I can see he wrote it back in the day on Windows Vista. Powershell has changed a bit since then. I've tweaked the code he had written to work on Win 10. Hope this helps!

 # Converted from Ed Wilson: https://devblogs.microsoft.com/scripting/hey-scripting-guy-how-can-i-find-files-metadata/

param($folder = "C:\Test") #end param

function funLinestrIN($strIN)
{
     $strLine = "=" * $strIn.length
     Write-Host "`n$strIN" -ForegroundColor Yellow 
     Write-Host $strLine -ForegroundColor Cyan
} #end funline

function funMetaData
{
    foreach($sFolder in $folder)
         {
              $a = 0
              $objShell = New-Object -ComObject Shell.Application
              $objFolder = $objShell.namespace($sFolder)

              foreach ($strFileName in $objFolder.items())
                   { 
                       funLinestrIN( "$($strFileName.name)")
                       for ($a ; $a  -le 266; $a++)
                          { 
                                if($objFolder.getDetailsOf($strFileName, $a))
                                  {
                                        $hash += @{ `
                                              $($objFolder.getDetailsOf($objFolder.items, $a)) = $($objFolder.getDetailsOf($strFileName, $a)) 
                                              } #end hash
                                       $hash
                                       $hash.clear()
                                  } #end if
                            } #end for 
                        $a=0
                    } #end foreach
        } #end foreach
} #end funMetadata
funMetaData # run function

Upvotes: 4

Naptha
Naptha

Reputation: 141

This answer is just to fix the link in Nick W's answer. Microsoft killed TechNet but the script is on PowerShell Gallery.

I googled the following to find it: PowerShell "Get-FileMetaData"

The location is: https://www.powershellgallery.com/packages/FC_SysAdmin/5.0.0/Content/public%5CGet-FileMetaData.ps1

Unfortunately, I can't get it to work, lol.

Upvotes: 1

Nick W.
Nick W.

Reputation: 1614

Update 3; I found a better script that should do exactly what you want provided by the incredible "Hey! Scripting Guy" blog. They already built a function that lets you view all of the details of music/mp3 files.

Blog post
https://blogs.technet.microsoft.com/heyscriptingguy/2014/02/05/list-music-file-metadata-in-a-csv-and-open-in-excel-with-powershell/

Function
https://gallery.technet.microsoft.com/scriptcenter/get-file-meta-data-function-f9e8d804

Upvotes: 2

Related Questions