When a torrent finishes copy the downloaded files to a new folder for processing.
Script notes
Utorrent has a new feature allowing a command line to be executed when a torrent changes state or finishes. I wanted to test this out and see if I could perform some utorrent automation via powershell.
The desired process is. One a torrent finishes downloading it will move in to seeding. The downloaded files will be copied from the seeding folder to a "new downloads" folder where they can be processed (uncompressing, moving, renaming and so on) without having to stop the torrent from seeding.
When the torrent finishes seeding the files in the seeding folder can be deleted.
To achieve this I have utorrent copy the the downloaded files to a new folder. When I finish seeding the torrent I can remove the files in the seeding folder. I also have another script that runs each day to clean up the seeding folder in case any torrents are removed from utorrent without removing the datafiles.
One tricky part in achieving this was that folder and file torrents are treated differently. There is no single path the the item I want to copy. From testing I came to If the torrent name ($N) is equal to file name ($F) its a file torrent and the file path is D$\$F (file $F in folder path $D). Otherwise we can just use the folder path ($D). I added a check to see if the folder name in $D was the same as the torrent name ($N) to catch any issues for folder torrents, this might not be needed.
The time take to perform the copy and the size of the folders before and after are logged to a file. This was so I could confirm that the copy was successful (this a POC script so testing and logs are needed).
param( [string]$Reason = "Unknown", [string]$F = "", [string]$D = "", [string]$N = "", [string]$S = "", [string]$P = "", [string]$L = "", [string]$T = "", [string]$M = "", [string]$I = "" ) #******************************************************************* # Global Variables #******************************************************************* $Script:Version = '0.4.0.5' <# Comments copys completed torrents Notes Scheduled Performs (via another script) Check for removed seeds Check for dead partial Inputs to this scripts $Reason - Default = 'unknown' - Does nothing Completed - The torrent was completed. StateChanged - The torrent state changed. Utorrent Falcon Commands %F - Name of downloaded file (for single file torrents) %D - Directory where files are saved %N - Title of torrent %S - State of torrent %P - Previous state of torrent %L- Label %T-Tracker %M - Status message string (same as status column) %I- hexencoded info-hash State is one of: error = 1, checked = 2 paused = 3 super seeding = 4 seeding = 5 downloading =6 super seed [F] = 7 seeding [F] = 8 downloading [F] = 9 queued seed = 10, finished = 11 queued = 12 stopped = 13 How to call from utorrent called from Completed powershell.exe -noprofile "& 'c:\path\utorrent.ps1' -Reason 'Completed' -F '%F' -D '%D' -N '%N' -S '%S' -P '%P' -L '%L' -T '%T' -M '%M' -I '%I'" called from StateChange powershell.exe -noprofile "& 'c:\path\utorrent.ps1' -Reason 'StateChanged' -F '%F' -D '%D' -N '%N' -S '%S' -P '%P' -L '%L' -T '%T' -M '%M' -I '%I'" #> ######################################################### #Load config from file ######################################################### [xml]$configFile = get-ScriptConfig if ($configFile -eq $null) { Write-Error "Failed to load config`nExiting" Exit} #Location where logs will be written $GlobalLogPath = $configFile.Configuration.GlobalLogPath #Location of Utorrent completed folders (Active torrents) $UtorrentCompletedFolder = $configFile.Configuration.UtorrentCompletedFolder #Where to place new unsorted downloads $DownloadsCompletedFolder = $configFile.Configuration.DownloadsCompletedFolder ########################################################### #functions ########################################################### function GetState( [string] $intState ) { #converts the integer state value to its English name switch ($intState) { "1" {return 'error'} "2" {return 'checked'} "3" {return 'paused'} "4" {return 'super seeding'} "5" {return 'seeding'} "6" {return 'downloading'} "7" {return 'super seed [F]'} "8" {return 'seeding [F]'} "9" {return 'downloading [F]'} "10" {return 'queued seed'} "11" {return 'finished'} "12" {return 'queued'} "13" {return 'stopped'} default {return 'unknown'} } } #creates a dir it is needed function CreateDirectoryIfNeeded ( [string] $directory ) { if ((test-path -LiteralPath $directory) -ne $True) { New-Item $directory -type directory | out-null if ((test-path -LiteralPath $directory) -ne $True) { return #[boolean]$false } else { return #[boolean]$true } } else { return #[boolean]$true } } #logs a message to file, #the filename changes every month to keep the file in a usablestate function Log_Message([string]$Message) { [datetime]$date = get-date [String]$filename = "$GlobalLogPath\uTorrent-{1}.log" -f $functionName, $date.ToString( "yyyyMM") CreateDirectoryIfNeeded $GlobalLogPath #Writes message to log file add-content $filename "$date | $Message" #displays message on console Write-Host $Message } function Get-FolderSize([string]$FolderPath) { <# .Synopsis gets the folder size recursive .Example [long]$result = Get-FolderSize "C:\temp" #> [long]$FolderLength = (Get-ChildItem -LiteralPath $FolderPath -Recurse | Measure-Object -Property Length -Sum).Sum return $FolderLength } function Get-BytesasString([long]$Bytes) { <# .Synopsis Displays the folder size in a pretty way .INPUTS None. You cannot pipe objects to Get-BytesasString. #> if ($Bytes -gt 1073741823) { [Decimal]$size = $Bytes / 1073741824 return "{0:##.##} GB" -f $size } elseif ($Bytes -gt 1048575) { [Decimal]$size = $Bytes / 1048576 return "{0:##.##} MB" -f $size } elseif ($Bytes -gt 1023) { [Decimal]$size = $Bytes / 1024 return "{0:##.##} KB" -f $size } elseif ($Bytes -gt 0) { [Decimal]$size = $Bytes return "{0:##.##} bytes" -f $size } else { return "0 bytes"; } } ########################################################### #Main script ########################################################### #Log Utorrent messages Log_Message "$Reason | F=$F|D=$D|N=$N|S=$(GetState $S)|P=$(GetState $P)|L=$L|T=$T|M=$M|I=$I|END" #tasks #Process completed torrent if($Reason -eq 'Completed') { $Source = $null #if the name is equal to the file its prob single file torrent if($N -eq $F) { #It's a single file torrent $Source = join-path -path $D -childpath $F } else { #It's a folder torrent $Source = $D $sourceObj = get-Item -LiteralPath $Source if ($sourceObj.name -ne $N) { Log_Message "ERROR| Folder name does not match %N: $($sourceObj.name)" $Source = $null } } if ((test-path -LiteralPath $Source) -eq $False) { Log_Message "ERROR | File does not exist: $Source. Exiting" } else { $message = "" $measure = Measure-Command { [string]$sizeSourceBefore = Get-BytesasString (Get-FolderSize $Source) Log_Message "Torrent Completed, copying object '$Source' to '$DownloadsCompletedFolder'" Copy-Item -LiteralPath $Source -Destination $DownloadsCompletedFolder -Recurse -Force $sourceObj = get-Item -LiteralPath $Source $targetFolder = join-path -path $DownloadsCompletedFolder -childpath $sourceObj.name [string]$sizeTargetAfter = Get-BytesasString (Get-FolderSize $targetFolder) [string]$sizeSourceAfter = Get-BytesasString (Get-FolderSize $Source) #log the before and after sizes of the source and destination folders, for debug $message = "sizeSourceBefore $sizeSourceBefore, sizeSourceAfter $sizeSourceAfter, sizeTarget $sizeTargetAfter" } #log how long the copy took Log_Message "$message - In $($measure.Days) Days $($measure.Hours):$($measure.Minutes):$($measure.Seconds).$($measure.Milliseconds)" } }
Happy to receive feedback