Un petit projet personnel pour rendre service à madame qui avait bien besoin de renommer en série un gros gros paquet de photos. Elles portent toutes des noms différents en fonction de l'appareil utilisé, et elle souhaitait avoir un nom sur chaque image qui renvoyait la date, l'heure, et un incrément en cas de doublon. Je me suis donc mis au travail, et j'ai tout d'abord recherché la manière d'obtenir les informations des métadonnées des photos.

Exemple de métadonnées d'un fichier

1

Après bien des recherches, il s'avère que pour récupérer ces données il faut créer un objet Shell.Application, qui n'est ni plus ni moins qu'une représentation Objet des applications dans l'interpréteur de commande.

A l'aide de ce type d'outil, vous pouvez effectuer des manipulations diverses comme l'arrêt d'un service, l'arrêt de l'ordinateur, la réduction de toutes les fenêtres ouvertes, l'ouverture de la commande "Exécuter" et bien d'autres choses. Et parmi ces "bien d'autres choses" : Obtenir la liste des fichiers d'un répertoire, en récupérant chez ceux-ci l'intégralité des informations qui gravitent autour (dont les métadonnées).

$Objshell = New-Object -ComObject Shell.Application
#Crée l'objet Shell.Application
$ObjFolder = $Objshell.NameSpace("C:\users\Nicolas\Videos")
#Crée un objet correspondant à un repertoire en particulier
#A partir de la, nous avons la méthode Items() qui nous permet de récupérer les objets dans le répertoire

Nous récupérons donc une liste de fichiers avec certaines propriétés... mais pas toutes!

2

Là est la subtilité.

$Objfolder permet non seulement de récupérer les fichiers, mais aussi leurs informations à l'aide de la méthode GetDetailsOf(). Nous allons donc dans un premier temps enregistrer le retour de la méthode Items() de $Objfolder dans une autre variable, puis prendre un des fichiers au hasard et tenter d'en extraire des détails, et enfin tenter la même chose sur $Objfolder.Items() (qui représente je le rappelle l'intégralité des fichiers trouvés dans le répertoire ).

3

Et oui! Nous obtenons les informations que nous cherchons. En appliquant une boucle, nous découvrons que la date de prise de vue pour les photos est le détail numéro 12! Maintenant que nous savons cela, il nous suffit de créer un petit forms qui permettra de choisir des fichiers et de filtrer ce que nous voulons garder de ce que $ObjFolder nous retourne... car oui, le bougre nous renvoie TOUS les fichiers sans distinction.

[System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms") 
#Création de l'objet de selection de la boite de dialoguqe
$filepick = New-Object System.Windows.Forms.OpenFileDialog
#On précise qu'on peut choisir plusieurs fichiers
$filepick.Multiselect = $true
#On filtre uniquement sur les images
$filepick.Filter = "Images (*.BMP;*.JPG;*.GIF)|*.BMP;*.JPG;*.GIF"
#On définit un repertoire par défaut
$filepick.InitialDirectory = "C:\"
$filepick.Title = "Selectionnez une ou plusieurs photos"
$filepickreturn = $filepick.ShowDialog()

Maintenant que nous avons un moyen de récupérer les informations de prise de vue, un moyen de choisir certains fichiers ... Qu'attendons nous pour faire un script?!

Ca tombe bien, c'est la suite de l'article, commentée et expliquée!

Vous pouvez télécharger le script depuis mon répertoire git personnel.

[System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms") 
#Création de l'objet de selection de la boite de dialogue
$filepick = New-Object System.Windows.Forms.OpenFileDialog
#On précise qu'on peut choisir plusieurs fichiers
$filepick.Multiselect = $true
#On filtre uniquement sur les images
$filepick.Filter = "Images (*.BMP;*.JPG;*.GIF)|*.BMP;*.JPG;*.GIF"
#On définit un repertoire par défaut
$filepick.InitialDirectory = "C:\"
$filepick.Title = "Selectionnez une ou plusieurs photos"
#On affiche le Forms en enregistrant le bouton cliqué dans une variable
$filepickreturn = $filepick.ShowDialog()
#Un tableau récapitulatif
$array = New-Object System.Collections.ArrayList
#Utilisé à la fin pour calculer le temps pris
$start = Get-Date
if ($filepickreturn -eq "OK")
{
    #Cette ligne renvoie la longueur de la string du nombre de fichier. Utilisé ensuite pour l'incrément. Ex : 3 fichiers, longueur du chiffre 3 : 1 caractère => incrémentera jusque 9 au maximum. 15 fichiers, longueur de 15 : 2 caractères => incrémentera jusque 99 au maximum, etc.
    $filesize = $filepick.FileNames.Count.ToString().length 
    #récupère le repertoire en allant du début jusqu'au dernier "\"
    $repertoire = $filepick.FileName.substring(0,$($filepick.FileName.lastindexof("\"))) 
    #On crée l'objet Shell
    $objShell = New-Object -ComObject Shell.Application 
    #On selectionne le répertoire
    $objfolder = $objShell.NameSpace($repertoire)
    #On filtre les objets pour ne prendre que ceux selectionnés par le Forms
    $objectlist = $objfolder.Items() | Where-Object {$_.path -in $filepick.FileNames}
    #Utilisé pour l'affichage de la progression
    $a = 0
    foreach ($file in $objectlist)
    {
        #On tente d'obtenir les détails de l'image. En cas d'erreur, on passe à la suivante
        try
        {
            $picturedate = $objfolder.GetDetailsOf($file,12)
        }
        catch
        {
            Write-Error "Impossible de récuperer la date sur $($file.Path)"
            continue
        }
        #On récupère l'objet afin de pouvoir avoir l'extension. En cas d'erreur, on passe à l'image suivante.
        try 
        {
            $item = Get-Item $($file.Path)
        }
        catch
        {
            Write-Error "Erreur sur $($file.Path)"
            continue
        }
        $i = 0
        $extension = $item.Extension
        #L'incrément se fait sur ce format : (001) . Le nombre de 0 change selon le nombre de photos selectionnées
        $increment = $("({0:D$filesize})" -f $i)
        #On retire les caractères inadéquat pour la création d'un nom de fichier
        $newfilename = (get-date -UFormat $picturedate).Replace("/","-").Replace(" ","-").Replace(":","-") + $extension

        $a++
        do
        {
            #On reconstitue le nom complet du fichier : Repertoire + nouveau nom
            $fullfilename = Join-Path $repertoire $newfilename
            #On regarde si le fichier existe déja...
            $testpathreturn = (Test-Path $fullfilename)
            #Et si oui, on ajoute 1 à l'incrément et que l'on ajoute au nom du fichier. On incrémente le nom du fichier jusqu'a ce que l'on tombe sur aucun fichier ne portant ce nom.
            if ($testpathreturn -eq $true)
            {
                $i++
                $increment = $("({0:D$filesize})" -f $i)
                #Format : jj-mm-yyyy-HH-MM (increment).extension
                $newfilename = (get-date -UFormat $picturedate).Replace("/","-").Replace(" ","-").Replace(":","-") + " " + $increment + $extension
            }
        #Une barre de progression, toujours utile pour savoir ou on en est
        Write-Progress -Activity "Renommage des fichiers en cours" -CurrentOperation "$($file.Name) => $newfilename" -PercentComplete ($a / $objectlist.Count * 100)
        }while($testpathreturn -eq $true)
        #Les objets fichier obtenus par Shell.Application ont une méthode permettant de leur définir aisément un nouveau nom.
        $file.Name = $newfilename
        $array.Add([PSCUSTOMOBJECT]@{
        "Nom_initial"= $file.Name
        "Nouveau_nom" = $newfilename}) | Out-Null

    }
}
#Fin du chrono
$end = Get-Date
#On calcule le temps passé
$temps = $end - $start
#On affiche un récap de toutes les modifications faites
$array | ft
#Ainsi que le temps passé et le nombre de fichiers!
write-host "$($objectlist.Count) photos en $([math]::round($temps.TotalSeconds,0)) secondes"

Voila! Ce script fonctionne très bien, et chez moi, sur mon NAS (donc moins rapide qu'en local) j'ai pu renommer 110 photos en 27 secondes. En espérant qu'il vous soit utile ;)

Ajouter un commentaire

Article précédent Article suivant