|
|
@@ -0,0 +1,247 @@
|
|
|
+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
|