My Original - Read date from a file and rename two files using the date
github if you want to see behind the scenes.
I like taking pictures of birds. Lots of pictures of birds. My camera counts 0001 to 9999 and then starts over. So, if I have over 10K photos in a single folder and haven't changed the prefix on the camera, there are duplicates.
I shoot in RAW (Nikon). I edit in LightRoom. When I edit the .NEF (RAW), a XMP (Sidecar) with metadata is created and saved alongside the raw file. These have the same filename so when the NEF is opened, the program knows which metadata file to load with it.
I created this to go through the files of a folder and read the metadata, appending the date to the filename ensuring that the raw file and the metadata file remain with matching names.. It evolved from there.
References
I've only imported System.
namespace.
UI
I tried my hand at making a UI and having controls handle arguments, which I've not done before.
It's not the best design, I know, but this is how it looks so the code behind it has context.
Code behind UI
Public Class UserInputForm
Private Sub DirectoryBrowseButton_Click(sender As Object, e As EventArgs) Handles DirectoryBrowseButton.Click
If (FolderBrowserDialog1.ShowDialog()) Then
DirectoryDisplayField.Text = FolderBrowserDialog1.SelectedPath & "\"
End If
End Sub
Private Sub RenameCheckBox_CheckedChanged(sender As Object, e As EventArgs) Handles RenameCheckBox.CheckedChanged
If RenameCheckBox.Checked Then
RenameTextBox.Visible = True
Else
RenameTextBox.Visible = False
End If
End Sub
Private Sub AppendComboBox_SelectedIndexChanged(sender As Object, e As EventArgs) Handles AppendComboBox.SelectedIndexChanged
If AppendComboBox.Text = "Renumber" Then
DigitLabel.Visible = True
DigitInput.Visible = True
Else
DigitLabel.Visible = False
DigitInput.Visible = False
End If
End Sub
Public Sub BeginButton_Click(sender As Object, e As EventArgs) Handles BeginButton.Click
Dim startingNumber As Integer = -1
Dim baseName As String = Nothing
Dim useDate As Boolean = True
If Not DirectoryDisplayField.Text = Nothing Then
If AppendComboBox.Text = "Renumber" Then useDate = False
If RenameCheckBox.Checked Then baseName = RenameTextBox.Text
If AppendComboBox.Text = "Renumber" Then startingNumber = DigitInput.Value
UserFormSubmit(DirectoryDisplayField.Text, useDate, RenameTextBox.Text, startingNumber)
End If
End Sub
Private Sub UserInputForm_Load(sender As Object, e As EventArgs) Handles MyBase.Load
End Sub
End Class
I'm not sure if I should provide the default states of any of the controls?
The Process
Option Explicit On
Option Strict On
Option Infer On
Option Compare Text
Imports System.IO
Module ImageRenaming
Public Const IMAGE_EXTENTION As String = ".NEF"
Public Const SIDECAR_EXTENTION As String = ".xmp"
Public Sub UserFormSubmit(ByVal targetDirectory As String, ByVal UseDate As Boolean, Optional ByVal newName As String = Nothing, Optional ByVal startingNumber As Integer = -1)
If Not Directory.Exists(targetDirectory) Then Exit Sub
Dim arrayOfFiles() As String = RetrieveFileNames(targetDirectory)
If arrayOfFiles.Length = 0 Then Exit Sub
Dim fileName As String
Dim numberOfFiles As Integer
Dim countOfRenames As Integer = startingNumber
If startingNumber = -1 Then countOfRenames = 1
'WHAT IF A FILE OF THAT NAME EXISTS? HANDLE THAT
For index As Integer = 0 To arrayOfFiles.Length - 1
fileName = arrayOfFiles(index)
If Path.GetExtension(fileName) = IMAGE_EXTENTION Then
If arrayOfFiles.Contains(Path.GetFileNameWithoutExtension(fileName) & SIDECAR_EXTENTION) Then
numberOfFiles = 2
Else
numberOfFiles = 1
End If
countOfRenames = ProcessFile(targetDirectory, Path.GetFileNameWithoutExtension(fileName), numberOfFiles, UseDate, countOfRenames, newName)
End If
Next
End Sub
Private Function ProcessFile(ByVal targetDirectory As String, ByVal fileName As String, ByVal numberOfFiles As Integer, ByVal useDate As Boolean, ByVal countOfRenames As Integer, Optional ByVal newFileNameBase As String = Nothing) As Integer
Dim newFileName As String
If Not newFileNameBase = Nothing And useDate Then
newFileName = newFileNameBase & countOfRenames
ElseIf newFileName = Nothing Then
newFileName = newFileNameBase
Else
newFileName = fileName
End If
newFileName += "_"
Dim appendString As String = countOfRenames.ToString
If numberOfFiles = 2 Then
If useDate Then appendString = GetFileDate(targetDirectory, Path.ChangeExtension(fileName, SIDECAR_EXTENTION))
newFileName += appendString
End If
RenamePhotoFiles(targetDirectory, fileName, newFileName, numberOfFiles)
countOfRenames += 1
Return countOfRenames
End Function
Private Function RetrieveFileNames(ByVal targetPath As String) As String()
Dim fileArray() As String = Directory.GetFiles(targetPath)
For i As Integer = 0 To fileArray.Length - 1
fileArray(i) = Path.GetFileName(fileArray(i))
Next
Return fileArray
End Function
Private Sub RenamePhotoFiles(ByVal targetDirectory As String, ByVal oldName As String, ByVal newName As String, ByVal numberOfFiles As Integer)
If numberOfFiles = 2 Then
File.Move(targetDirectory & oldName & SIDECAR_EXTENTION, targetDirectory & newName & SIDECAR_EXTENTION)
End If
File.Move(targetDirectory & oldName & IMAGE_EXTENTION, targetDirectory & newName & IMAGE_EXTENTION)
End Sub
Private Function GetFileDate(ByVal targetDirectory As String, ByVal sidecarFile As String) As String
Const LINE_START As String = " exif:DateTimeOriginal="
Const DATE_STRING_LENGTH As Integer = 10
Const CHAR_TO_REMOVE As String = "-"
sidecarFile = targetDirectory & sidecarFile
Dim lines = File.ReadAllLines(sidecarFile)
Dim targetLine As String = Nothing
For i As Integer = 0 To lines.Length - 1
If lines(i).StartsWith(LINE_START) Then
targetLine = lines(i)
Exit For
End If
Next
If targetLine.Length = 0 Then Return Nothing
targetLine = targetLine.Substring(LINE_START.Length + 1, DATE_STRING_LENGTH)
Return targetLine.Replace(CHAR_TO_REMOVE, "")
End Function
End Module
Main issues
I don't think I have used the most precise naming for some of the variables and functions, so maybe there's a better way to refactor it?
I also don't know if I've anticipated the possible errors. I know I haven't addressed a file already existing.
I also put optional arguments in the userform submit, but I don't know how to make the submit control determine what arguments to send and still execute, so there's duplication on those.
I also would like to make sure I'm doing it the ".NET way" instead of falling back on VBA/VB6 style.