class SourceDirs { static $MixxxConfig = "$env:USERPROFILE\AppData\Local\Mixxx" static $Test = "C:\tmp\Test With Spaces" static $Desktop_ITunes = "$env:USERPROFILE\Music\iTunes\iTunes Media\Music" static $Music_MixxxConfig = "M:\Config\Mixxx" static $Music_ITunes = "M:\Music\ITUNES" static $Music_ZipDJ = "M:\Music\ZipDJ Tracks" static $Music_Music = "M:\Music" static $Drive_MixxxConfig = "D:\Config\Mixxx" static $Drive_ITunes = "D:\Music\ITUNES" static $Drive_ZipDJ = "D:\Music\ZipDJ Tracks" static $Drive_Music = "D:\Music" } class Choice { [string] $DisplayName [string] $Source [string] $Dest [boolean] $MakeBackup [boolean] $IgnoreTimestamps Choice($displayName, $source, $dest, $makeBackup, $ignoreTimestamps) { $this.Source = $source $this.Dest = $dest $this.DisplayName = $displayName $this.MakeBackup = $makeBackup $this.IgnoreTimestamps = $ignoreTimestamps } } $CHOICES = @( ############ # Mixxx [Choice]::new( "Mixxx: Desktop --> Server", [SourceDirs]::MixxxConfig, [SourceDirs]::Music_MixxxConfig, $false, # Make Backup $false # Ignore Timestamps ), [Choice]::new( "Mixxx: Desktop --> Drive", [SourceDirs]::MixxxConfig, [SourceDirs]::Drive_MixxxConfig, $false, # Make Backup $false # Ignore Timestamps ), [Choice]::new( "Mixxx: Drive --> Laptop", [SourceDirs]::Music_MixxxConfig, [SourceDirs]::MixxxConfig, $true, # Make Backup $false # Ignore Timestamps ), [Choice]::new( "Mixxx: Drive --> Desktop", [SourceDirs]::Drive_MixxxConfig, [SourceDirs]::MixxxConfig, $true, # Make Backup $false # Ignore Timestamps ), [Choice]::new( "Mixxx: Laptop --> Drive", [SourceDirs]::MixxxConfig, [SourceDirs]::Music_MixxxConfig, $false, # Make Backup $false # Ignore Timestamps ), ########## # iTunes [Choice]::new( "iTunes: Desktop --> Server", [SourceDirs]::Desktop_ITunes, [SourceDirs]::Music_ITunes, $false, # Make Backup $true # Ignore Timestamps ), [Choice]::new( "iTunes: Drive --> Server", [SourceDirs]::Drive_ITunes, [SourceDirs]::Music_ITunes, $false, # Make Backup $true # Ignore Timestamps ), [Choice]::new( "iTunes: Server --> Desktop", [SourceDirs]::Music_ITunes, [SourceDirs]::Desktop_ITunes, $false, # Make Backup $true # Ignore Timestamps ), ########## # Music [Choice]::new( "Music: Server --> Drive", [SourceDirs]::Music_MixxxConfig, [SourceDirs]::MixxxConfig, $false, # Make Backup $true # Ignore Timestamps ), ########## # Zip DJ [Choice]::new( "ZipDJ: Drive --> Server", [SourceDirs]::Drive_ZipDJ, [SourceDirs]::Music_ZipDJ, $false, # Make Backup $true # Ignore Timestamps ) ) # Print the given message preceded by the name of the calling function. function out($message) { $caller = (Get-PSCallStack)[1].Command Write-Host "${caller}: $message" } # Print the given message, preceded by the name of the calling function, # prompt the user to hit a key, and exit. function fail($message) { $caller = (Get-PSCallStack)[1].Command Write-Host "ERROR: ${caller}: $message" Read-Host -Prompt "Press Enter to exit" exit -1 } function pause($message) { $caller = (Get-PSCallStack)[1].Command Write-Host "${caller}: $message`n`n" Read-Host -Prompt "Press Enter to continue, or CTRL+C to exit" } function GetModifiedDate($dirPath) { $dir = Get-Item $dirPath $date = Get-Date $dir.LastWriteTime return $date.ToShortDateString() } # Rename the specified directory to have a suffix based on the # last modified time. function makeDatedBackup($dirPath) { if (-not(Test-Path $dirPath)) { # Not an error, but nothing we can do. out "Directory does no exist: ${dirPath}" return } $modifiedDate = GetModifiedDate($dirPath) $newPath = "$dirPath-$modifiedDate" if (Test-Path $newPath) { $num = 2 $newNewPath = "$newPath-$num" while (Test-Path $newNewPath) { $num++ } out "$newPath already exists. Using $newNewPath" $newPath = $newNewPath } $dirName = [io.path]::GetFileName($newPath) pause "Moving $dirPath to $dirName" Move-Item -Path $dirPath -Destination $newPath } # Synchronize the contents of two directories. # -NoBackup: Skip moving the existing destination to a dated backup path. # -IgnoreTimestamps: Skip re-copying files if only timestamps are different. function syncDirs { param ( [string] $Source, [string] $Destination, [boolean] $MakeBackup, [boolean] $IgnoreTimestamps ) if (($Source.Length -eq 0) -or ($Destination.Length -eq 0)) { fail "Both Source and Destination need to be specified." } if (-not(Test-Path $Source)) { fail "Source does not exist: $Source" } if ($MakeBackup) { out "Backing up $Destination" makeDatedBackup $Destination } # Docs: https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/robocopy $roboWhat = @("/s", "/copy:DAT", "/dcopy:DAT") $roboOptions = @("/v", "/j") $roboExclude = @("/xo", "/xx") $roboAdditionalExcludes = @() if ($IgnoreTimestamps) { $roboAdditionalExcludes = @("/xn") } pause "Copying contents of: `n`t${Source}`nTo:`n`t${Destination}" robocopy $Source $Destination $roboWhat $roboOptions $roboExclude } function chooseSyncOption() { $num = 1 Write-Host "`nPick an option to run:" ForEach ($o in $CHOICES) { Write-Host ([string]::Format("{0}) {1}", $num, $o.DisplayName)) $num++ } Write-Host "`n`n" Write-Host "Before an event: 1, 2, 6, 9" Write-Host "During an event: 3" Write-host "After an event: 5, 7, 8, 10, 4" $input = Read-Host -Prompt "Enter option number" $choice = [int]$input if (($choice -lt 1) -or ($choice -gt $CHOICES.Length + 1)) { fail 'Invalid choice!' } return $CHOICES[$choice-1] } function Main() { [Choice] $option = chooseSyncOption $option syncDirs ` -Source $option.Source ` -Destination $option.Dest ` -MakeBackup $option.MakeBackup ` -IgnoreTimestamps $option.IgnoreTimestamps #syncDirs -Source ([SourceDirs]::MixxxConfig) -Destination ([SourceDirs]::Test) -NoBackup -IgnoreTimestamps pause ([string]::Format("Done running {0}!", $option.DisplayName)) } Main