2

What is the variable for "Media created", using Powershell?

I'm trying to change many .mov files at one time, in a folder, from "IMG-3523" to their respective creation dates.

I was using the following code to do so, using LastWriteTime:

Get-ChildItem | Rename-Item -NewName {$_.LastWriteTime.ToString("yyyy-MM-dd hh.mm.ss ddd") + ($_.Extension)}

However for some reason there are many duplicate entries for that variable, and I run into an error stating so.

Is there any way I can edit this code to use the "Media created" variable? It's the only date without duplicates.

(I've tried editing the code to include microseconds and $_.CreationTime, however there are many duplicates with that variable too (actually all have the same CreationTime - the timestamp of when I copied the files over from an external disk).

sikorloa
  • 101
  • 13
  • 1
    All files you want to rename have the media created? You woul need COM for this afaik – Santiago Squarzon Feb 05 '23 at 19:18
  • You can use the DOS command RENAME (or REN). PS will take the DOS command. In PS you need both the old and new name : Rename-Item -Path "c:\logfiles\daily_file.txt" -NewName "monday_file.txt" – jdweng Feb 05 '23 at 19:33

3 Answers3

1

For all files within a directory:

$fldPath = "D:\FolderName";
$flExt = ".mov";
$attrName = "media created"

(Get-ChildItem -Path "$fldPath\*" -Include "*$flExt").FullName | % { 
    $path = $_
    $shell = New-Object -COMObject Shell.Application;
    $folder = Split-Path $path;
    $file = Split-Path $path -Leaf;
    $shellfolder = $shell.Namespace($folder);
    $shellfile = $shellfolder.ParseName($file);

    $a = 0..500 | % { Process { $x = '{0} = {1}' -f $_, $shellfolder.GetDetailsOf($null, $_); If ( $x.split("=")[1].Trim() ) { $x } } };
    [int]$num = $a | % { Process { If ($_ -like "*$attrName*") { $_.Split("=")[0].trim() } } };
    $mCreated = $shellfolder.GetDetailsOf($shellfile, $num);
    $mCreated;
};

For one specific File, you can use:

$flPath = "D:\FolderName\FileName.mov";
$attrName = "media created"

$path = $flPath;
$shell = New-Object -COMObject Shell.Application;
$folder = Split-Path $path;
$file = Split-Path $path -Leaf;
$shellfolder = $shell.Namespace($folder);
$shellfile = $shellfolder.ParseName($file);

$a = 0..500 | % { Process { $x = '{0} = {1}' -f $_, $shellfolder.GetDetailsOf($null, $_); If ( $x.split("=")[1].Trim() ) { $x } } };
[int]$num = $a | % { Process { If ($_ -like "*$attrName*") { $_.Split("=")[0].trim() } } };
$mCreated = $shellfolder.GetDetailsOf($shellfile, $num);
$mCreated;

This was answered in SuperUser by vomit-it-chunky-mess-style on Getting a Media Created via PS

Ranadip Dutta
  • 8,857
  • 3
  • 29
  • 45
  • 1
    You should not create a new COM object inside the loop every time and never release it from memory. This way you're bound to run out of system resources. Also be aware that `$mCreated` will most likely contain invisible (null) characters you need to get rid of before trying to use that in a file name – Theo Feb 06 '23 at 15:39
1

As "Media created" property of a .mov file might also be multiple, or undefined in many cases, use it may cause also file name duplication, even error. I'd propose another approach, we first check existence of both "Media created" and "Date taken", if nonexistent, we keep using $_.LastWriteTime as basic file name, adding a number to each file name if duplicated, like this:


#
# fnc001: get file media created, date taken or LastWriteTime:
#
function entGetMediaCreatedOrLastWriteTime($objFile) {
  $idxMediaCreated = 208

  $objShell = New-Object -COMObject Shell.Application
  $objShellFolder = $objShell.NameSpace($objFile.DirectoryName)

  $iState = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($objShell)

  $objShellFile = $objShellFolder.ParseName($objFile.Name)
  $mediaCreated = $objShellFolder.GetDetailsOf($objShellFile, $idxMediaCreated)


  #
  # if media created is empty, we check if we have Date taken:
  #
  if($mediaCreated -eq "") {
    #
    # canon cameras set Date taken for photos:
    #
    $idxDateTaken = 12
    $dateTaken = $objShellFolder.GetDetailsOf($objShellFile, $idxDateTaken)
    
    #
    # return LastWriteTime if neither media created, nor Date taken:
    #
    if($dateTaken -eq "") {
        return $objFile.LastWriteTime
    }
    #
    # otherwise return Date taken, removing non-ascii before:
    #
    else
    {   
        return [DateTime]($dateTaken -replace '\P{IsBasicLatin}')
    }
  }
  #
  # otherwise return valid media created, removing non-ascii before:
  #
  else {
    return [DateTime]($mediaCreated -replace '\P{IsBasicLatin}')
  }
}

#
# fnc001: increment filename if it already exists:
#
function entIncrementIfExistent($filename) {
  $fnew = $filename
  $ext = Split-Path $filename -Extension
  #
  # define prefix before file number:
  #
  $prefix = "-"
  $i = 0
  #
  # save file base length:
  #
  $lngbase = $fnew.length - $ext.length
  #
  # here $filename is like
  # 2023-02-05 08.33.00 Sun.mov,
  #
  # if it exists, we try to use first available of:
  #
  # 2023-02-05 08.33.00 Sun-1.mov
  # 2023-02-05 08.33.00 Sun-2.mov
  # ...
  # 2023-02-05 08.33.00 Sun-9.mov
  # ...
  # 2023-02-05 08.33.00 Sun-10.mov
  # 2023-02-05 08.33.00 Sun-11.mov
  # ...
  #
  while (Test-Path $fnew)
  {
    $i++
    $fnew = $fnew.Substring(0, $lngbase) + $prefix + $i + $ext
  }

  return $fnew
}

Get-ChildItem *.mov | Rename-Item -NewName {
  $xDateTime = entGetMediaCreatedOrLastWriteTime $_
  $fnew = $xDateTime.ToString("yyyy-MM-dd hh.mm.ss ddd") + $_.Extension
  entIncrementIfExistent $fnew
}

I've tried this code, it gets something like this:

enter image description here

jacouh
  • 8,473
  • 5
  • 32
  • 43
  • Thank you @jacouh - You're right, that some don't have the "media created" variable (only about 10 files out of 400 though, from looking at my test folder). The thing is though - I'm trying to cross reference my notes on these video files in other places (notebooks, audio files, etc) - and my reference is the "Media created" date - so for this application I'm cemented to that particular variable. How could I modify your code to use the 'Media created' variable, but switch to the 'Lastwritetime' if there is no 'Media created' attribute? – sikorloa Feb 09 '23 at 20:29
  • @sikorloa, I've modified the code to use Media created or LastWriteTime. – jacouh Feb 10 '23 at 21:14
  • thank you @jacouh - I tried running that code however I get the following error, something wrong with the $_.Extension, I couldn't figure out what it was though: https://imgur.com/vbQVU01 – sikorloa Feb 13 '23 at 20:12
  • Please copy the code as a whole, and test it in a temporary folder with some fake .mov files. As I tested just a while, working. – jacouh Feb 13 '23 at 20:29
1

Using the Shell.Application object you can get this value from the file, but unfortunately, the property names are all Localized, so if you are looking for a property named Media created on a non-english machine, you will not find it..

For .MOV files, the index of this property in numbered 4. You can use below function to get that value for all the files in the path and then use that to rename them:

function Get-MetaData {
    [CmdletBinding()]
    [OutputType([Psobject[]])]
    Param (
        # Path can be the path to a folder or the full path and filename of a single file
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0)]
        [string]$Path,

        # Filter is unused if Path is pointing to a single file
        [Alias('Pattern')]
        [string]$Filter = '*.*',

        [Alias('Indices')]
        [int[]]$Properties = 1..500,

        # Recurse is unused if Path is pointing to a single file
        [switch]$Recurse,
        [switch]$IncludeEmptyProperties
    )
    $item = Get-Item -Path $Path -ErrorAction SilentlyContinue
    if (!$item) { Write-Error "$Path could not be found."; return }
    if (!$item.PSIsContainer) {
        # it's a file
        $files = @($item)
        $Path  = $item.DirectoryName
    }
    else {
        # it's a folder
        $files = Get-ChildItem -Path $Path -Filter $Filter -File -Recurse:$Recurse
    }
    $shell  = New-Object -ComObject "Shell.Application"
    $objDir = $shell.NameSpace($Path)

    foreach($file in $files) {
        $objFile    = $objDir.ParseName($file.Name)
        $mediaFile  = $objDir.Items()

        foreach($index in $Properties) {
            $name  = $objDir.GetDetailsOf($mediaFile, $index)
            if (![string]::IsNullOrWhiteSpace($name)) { 
                # to be on the safe side, remove any control character (ASCII < 32) that may be in there
                $value = $objDir.GetDetailsOf($objFile, $index) -replace '[\x00-\x1F]+'
                if (![string]::IsNullOrWhiteSpace($value) -or $IncludeEmptyProperties) {
                    [PsCustomObject]@{
                        Path     = $file.FullName
                        Index    = $index
                        Property = $name
                        Value    = $value
                    }
                }
            }
        }
    }
    # clean-up Com objects
    $null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($objFile)
    $null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($objDir)
    $null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($shell)
    [System.GC]::Collect()
    [System.GC]::WaitForPendingFinalizers()
}

# property index 3 --> Last Modified
# property index 4 --> Date Created   # we'll use this one
# property index 5 --> Last Accessed
$SourceFolder = 'X:\Somewhere'
Get-MetaData -Path $SourceFolder -Filter '*.mov' -Properties 4 |
ForEach-Object {
    # HH for 24-hour clock; hh for 12-hour clock
    $dateCreated = '{0:yyyy-MM-dd HH.mm.ss ddd}' -f ([datetime]$_.Value)
    $directory   = [System.IO.Path]::GetDirectoryName($_.Path)
    $extension   = [System.IO.Path]::GetExtension($_.Path)
    # test if the suggested new name is already in use, append a sequence 
    # number to the basename until we have a unique name
    $newName = '{0}{1}' -f $dateCreated, $extension
    $index   = 1
    while (Test-Path -Path (Join-Path -Path $directory -ChildPath $newName) -PathType Leaf) {
        $newName = '{0}({1}){2}' -f $dateCreated, $index++, $extension
    }
    Rename-Item -Path $_.Path -NewName $newName -WhatIf
}

The -WhatIf is a safety measure. It will make the code only show in the console what would happen. No file is actually renamed. If you are convinced that output is correct, then remove the -WhatIf switch and run the code again

Theo
  • 57,719
  • 8
  • 24
  • 41
  • Thank you @Theo - I ran it with the -WhatIf included, and it seemed to provide the correct output, however when I ran it without the -WhatIf, it only renamed about 5 files out of 670, and gave me the following error: Rename-Item : Cannot create a file when that file already exists. At line:70 char:5 + Rename-Item -Path $_.Path -NewName ('{0}{1}' -f $dateCreated, $ex ... + ~~~~~~~ + CategoryInfo : WriteError: (C:\Location\IMG_6368.MOV:String) [Rename-Item], IOException + FullyQualifiedErrorId : RenameItemIOError,Microsoft.PowerShell.Commands.RenameItemCommand – sikorloa Feb 09 '23 at 20:47
  • @sikorloa It looks like you are dealing with files that were taken of one original movie. Perhaps these are chapters or cut-outs of one larger file. Editing or cutting parts does NOT set the Created date a new, so you will end up with multiple files all having the same creation date. I have now added a loop that ensures the filenames will be unique by appending a sequence index number between brackets. – Theo Feb 10 '23 at 12:11
  • @Theo, Index 4 = File Created Date, but Index 208 = Media created, using Shell.Application. – jacouh Feb 10 '23 at 19:58
  • @Theo Thank you for the suggestions, I tried the new code, however it renames all the files to the same date (2023-02-29). All of these videos (sans maybe 5 of the 650) are original videos taken separately on an iPhone. I took them myself, I'm using many of the videos as references to other written / audio media I have from the same dates, to use them in conjunction. (The video is a reference on how to do a certain task, which is time consuming to write down on paper and describe). Image of results: https://imgur.com/3HROtCD – sikorloa Feb 10 '23 at 21:03
  • @sikorloa The files are all from 2023-02-09 (there is no February the 29th in 2023). Either the calendar in your iPhone is screwed up, or you have processed these files on that day with some software that changed the date properties – Theo Feb 10 '23 at 21:57
  • @sikorloa You could try with `Get-MetaData -Path $SourceFolder -Filter '*.mov' -Properties 208` as [jacouh](https://stackoverflow.com/questions/75354785/variable-for-media-created-powershell/75364038?noredirect=1#comment133068352_75364038) commented – Theo Feb 11 '23 at 12:01
  • @Theo thank you for the suggestions, I ran the code again, still get the same error (all files are renamed to the same date, the "Date Created" date, however this date will always be the same (the day I moved the files off my phone, or then off my external drive to my hard drive, etc). Still trying to get it to rename to the "Media created" date, which I believe is indeed the "Properties 208" code, however when I add in that line you mention I get the following error: https://imgur.com/a/TejyM12 . Odd that it's not recognizing the date/time string, what do you think could be causing it? – sikorloa Feb 13 '23 at 19:55
  • @sikorloa your image shows there are characters outside the ascii range in there. Apparently we need a more strict regex replacement string. Please copy one of these error messages and add it to your post as formatted text (not as image) so I can hopefully see what characters they are – Theo Feb 13 '23 at 21:02
  • @sikorloa You can try with `-replace '[^\x20-\x7E]+'` – Theo Feb 13 '23 at 21:08