Reputation: 163
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
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
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
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
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.
Function
https://gallery.technet.microsoft.com/scriptcenter/get-file-meta-data-function-f9e8d804
Upvotes: 2