소스 검색

Framework for NZB integration

Zachary Kotlarek 9 년 전
부모
커밋
d17c57ed53
1개의 변경된 파일과 202개의 추가작업 그리고 133개의 파일을 삭제
  1. 202 133
      transmission.pl

+ 202 - 133
transmission.pl

@@ -28,18 +28,25 @@ sub seriesCleanupCore($);
 sub seriesCleanup($);
 sub seriesCleanup($);
 sub readDir($$);
 sub readDir($$);
 sub findMediaFiles($$);
 sub findMediaFiles($$);
+sub available($);
 
 
 # Parameters
 # Parameters
-my $maxAge         = 2.5 * 86400;
-my $tvDir          = `~/bin/video/mediaPath` . '/TV';
-my $monitoredExec  = $ENV{'HOME'} . '/bin/video/torrentMonitored.pl';
-my $host           = 'http://vera.uberzach.com:9091';
-my $url            = $host . '/transmission/rpc';
-my $content        = '{"method":"torrent-get","arguments":{"fields":["hashString","id","addedDate","comment","creator","dateCreated","isPrivate","name","totalSize","pieceCount","pieceSize","downloadedEver","error","errorString","eta","haveUnchecked","haveValid","leftUntilDone","metadataPercentComplete","peersConnected","peersGettingFromUs","peersSendingToUs","rateDownload","rateUpload","recheckProgress","sizeWhenDone","status","trackerStats","uploadedEver","uploadRatio","seedRatioLimit","seedRatioMode","downloadDir","files","fileStats"]}}';
+my $maxAge        = 2.5 * 86400;
+my $tvDir         = `~/bin/video/mediaPath` . '/TV';
+my $monitoredExec = $ENV{'HOME'} . '/bin/video/torrentMonitored.pl';
+my $content =
+    '{"method":"torrent-get","arguments":{"fields":["hashString","id","addedDate","comment","creator",'
+  . '"dateCreated","isPrivate","name","totalSize","pieceCount","pieceSize","downloadedEver","error","errorString",'
+  . '"eta","haveUnchecked","haveValid","leftUntilDone","metadataPercentComplete","peersConnected","peersGettingFromUs",'
+  . '"peersSendingToUs","rateDownload","rateUpload","recheckProgress","sizeWhenDone","status","trackerStats",'
+  . '"uploadedEver","uploadRatio","seedRatioLimit","seedRatioMode","downloadDir","files","fileStats"]}}';
 my $delContent     = '{"method":"torrent-remove","arguments":{"ids":["#_ID_#"], "delete-local-data":"true"}';
 my $delContent     = '{"method":"torrent-remove","arguments":{"ids":["#_ID_#"], "delete-local-data":"true"}';
-my $delSleep       = 10;
+my $history        = '{"method":"history","params":[]}';
+my $delHistory     = '{"method":"editqueue","params":["historyFinalDelete","",[#_ID_#]]}';
+my $delSleep       = 2;
 my $RAR_MIN_FILES  = 4;
 my $RAR_MIN_FILES  = 4;
 my $FIND_MAX_DEPTH = 5;
 my $FIND_MAX_DEPTH = 5;
+my $CONF_FILE      = $ENV{'HOME'} . '/.download.config';
 
 
 # Debug
 # Debug
 my $DEBUG = 0;
 my $DEBUG = 0;
@@ -50,166 +57,199 @@ if ($ENV{'DEBUG'}) {
 # Command-line parameters
 # Command-line parameters
 my ($force, $maxDays, $hash) = @ARGV;
 my ($force, $maxDays, $hash) = @ARGV;
 
 
+# Config file
+my %CONFIG = ();
+if ($CONF_FILE && -r $CONF_FILE) {
+	my $fh;
+	open($fh, '<', $CONF_FILE)
+	  or die('Unable to open config file: ' . $CONF_FILE . ': ' . $! . "\n");
+	while (<$fh>) {
+
+		# Skip blank lines and comments
+		if (/^\s*#/ || /^\s*$/) {
+			next;
+		}
+
+		# Assume everything else is bash variables
+		if (my ($key, undef(), $val) = $_ =~ /^\s*(\S[^\=]+)\=([\'\"])(.*)\g{-2}\s*$/) {
+			$CONFIG{$key} = $val;
+			if ($DEBUG) {
+				print STDERR 'Adding config: ' . $key . ' => ' . $val . "\n";
+			}
+		} else {
+			warn('Ignoring config line: ' . $_ . "\n");
+		}
+	}
+	close($fh);
+}
+
 # If a hash is provided, always process matching torrents
 # If a hash is provided, always process matching torrents
 if (defined($hash)) {
 if (defined($hash)) {
 	$force = 1;
 	$force = 1;
 }
 }
 
 
 # Init
 # Init
-my $fetch = Fetch->new();
+my $fetch = undef();
 
 
-# Check availability of web interface
-$fetch->url($host);
-if ($DEBUG) {
-	print STDERR "Checking for torrent web interface\n";
-}
-$fetch->fetch('nocheck' => 1);
-if ($fetch->status_code() != 200) {
-	if ($DEBUG) {
-		die("Transmission web interface not available\n");
-	}
-	exit(0);
-}
-
-# Fetch the list of paused (i.e. completed) torrents
-$fetch->url($url);
-$fetch->post_content($content);
-&fetch($fetch);
+# Tranmission
+if (available($CONFIG{'TRANS_URL'})) {
 
 
-my $torrents = decode_json($fetch->content());
-$torrents = $torrents->{'arguments'}->{'torrents'};
+	# Fetch the list of paused (i.e. completed) torrents
+	$fetch->url($CONFIG{'TRANS_URL'});
+	$fetch->post_content($content);
+	&fetch($fetch);
 
 
-# Process
-if ($DEBUG) {
-	print STDERR "Processing torrents...\n";
-}
-foreach my $tor (@{$torrents}) {
+	my $torrents = decode_json(scalar($fetch->content()));
+	$torrents = $torrents->{'arguments'}->{'torrents'};
 
 
-	# Skip torrents that aren't yet done
-	if ($tor->{'leftUntilDone'} > 0 || $tor->{'metadataPercentComplete'} < 1) {
-		next;
+	# Process
+	if ($DEBUG) {
+		print STDERR "Processing torrents...\n";
 	}
 	}
+	foreach my $tor (@{$torrents}) {
 
 
-	# Only process recent files
-	# Torrents created with magnet links have no dateCreated, so fake it with addedDate
-	if ($tor->{'dateCreated'} < 946684800) {
-		$tor->{'dateCreated'} = $tor->{'addedDate'};
-	}
-	if (!$force && $tor->{'dateCreated'} < time() - $maxAge) {
-		my $age = (time() - $tor->{'dateCreated'}) / 86400;
-		if ($DEBUG) {
-			print STDERR 'Skipping old torrent: ' . $tor->{'name'} . ' (' . sprintf('%.0f', $age) . " days)\n";
+		# Skip torrents that aren't yet done
+		if ($tor->{'leftUntilDone'} > 0 || $tor->{'metadataPercentComplete'} < 1) {
+			next;
 		}
 		}
-		next;
-	}
 
 
-	# If a hash is provided, process only that file
-	if (defined($hash) && $tor->{'hashString'} ne $hash) {
-		if ($DEBUG) {
-			printf STDERR 'Skipping torrent with non-matching hash: ' . $tor->{'name'} . ' (' . $tor->{'hashString'} . ")\n";
+		# Only process recent files
+		# Torrents created with magnet links have no dateCreated, so fake it with addedDate
+		if ($tor->{'dateCreated'} < 946684800) {
+			$tor->{'dateCreated'} = $tor->{'addedDate'};
 		}
 		}
-		next;
-	}
-
-	# We've decided to do something about this torrent
-	if ($DEBUG) {
-		print STDERR 'Processing completed torrent: ' . $tor->{'name'} . "\n";
-	}
-
-	# Handle individual files and directories
-	my @files    = ();
-	my @newFiles = ();
-	my $path     = $tor->{'downloadDir'} . '/' . $tor->{'name'};
-	if (!-d $path) {
-		push(@files, $path);
-	} else {
-
-		# State variables
-		my $IS_RAR  = '';
-		my $IS_FAKE = 0;
-
-		# Look for RAR files
-		foreach my $file (@{ $tor->{'files'} }) {
-			if ($file->{'name'} =~ /\.rar$/i) {
-				$IS_RAR = $tor->{'downloadDir'} . '/' . $file->{'name'};
-				last;
+		if (!$force && $tor->{'dateCreated'} < time() - $maxAge) {
+			my $age = (time() - $tor->{'dateCreated'}) / 86400;
+			if ($DEBUG) {
+				print STDERR 'Skipping old torrent: ' . $tor->{'name'} . ' (' . sprintf('%.0f', $age) . " days)\n";
 			}
 			}
+			next;
 		}
 		}
 
 
-		# Check for password files
-		foreach my $file (@{ $tor->{'files'} }) {
-			if ($file->{'name'} =~ /passw(?:or)?d/i) {
-				print STDERR 'Password file detected in: ' . $tor->{'name'} . "\n";
-				$IS_FAKE = 1;
-				last;
+		# If a hash is provided, process only that file
+		if (defined($hash) && $tor->{'hashString'} ne $hash) {
+			if ($DEBUG) {
+				printf STDERR 'Skipping torrent with non-matching hash: ' . $tor->{'name'} . ' (' . $tor->{'hashString'} . ")\n";
 			}
 			}
+			next;
 		}
 		}
 
 
-		# Check for single-file RARs
-		if ($IS_RAR && scalar(@{ $tor->{'files'} }) < $RAR_MIN_FILES) {
-			print STDERR 'Single-file RAR detected in: ' . $tor->{'name'} . "\n";
-			$IS_FAKE = 1;
+		# We've decided to do something about this torrent
+		if ($DEBUG) {
+			print STDERR 'Processing completed torrent: ' . $tor->{'name'} . "\n";
 		}
 		}
 
 
-		# Delete fake torrents and bail
-		if ($IS_FAKE) {
-			if ($DEBUG) {
-				print STDERR 'Deleting fake torrent: ' . $tor->{'name'} . "\n";
+		# Handle individual files and directories
+		my @files    = ();
+		my @newFiles = ();
+		my $path     = $tor->{'downloadDir'} . '/' . $tor->{'name'};
+		if (!-d $path) {
+			push(@files, $path);
+		} else {
+
+			# State variables
+			my $IS_RAR  = '';
+			my $IS_FAKE = 0;
+
+			# Look for RAR files
+			foreach my $file (@{ $tor->{'files'} }) {
+				if ($file->{'name'} =~ /\.rar$/i) {
+					$IS_RAR = $tor->{'downloadDir'} . '/' . $file->{'name'};
+					last;
+				}
 			}
 			}
-			delTor($tor);
-			next;
-		}
 
 
-		# Unrar if necessary
-		if (defined($IS_RAR) && length($IS_RAR) > 0) {
-			@newFiles = &unrar($IS_RAR, $path);
-		}
+			# Check for password files
+			foreach my $file (@{ $tor->{'files'} }) {
+				if ($file->{'name'} =~ /passw(?:or)?d/i) {
+					print STDERR 'Password file detected in: ' . $tor->{'name'} . "\n";
+					$IS_FAKE = 1;
+					last;
+				}
+			}
 
 
-		# Find all media files, recursively
-		@files = findMediaFiles($path, 0);
-	}
+			# Check for single-file RARs
+			if ($IS_RAR && scalar(@{ $tor->{'files'} }) < $RAR_MIN_FILES) {
+				print STDERR 'Single-file RAR detected in: ' . $tor->{'name'} . "\n";
+				$IS_FAKE = 1;
+			}
 
 
-	# Bail if we found no files
-	if (scalar(@files) < 1) {
-		print STDERR 'No media files found in torrent: ' . $tor->{'name'} . "\n";
-		next;
-	}
+			# Delete fake torrents and bail
+			if ($IS_FAKE) {
+				if ($DEBUG) {
+					print STDERR 'Deleting fake torrent: ' . $tor->{'name'} . "\n";
+				}
+				delTor($tor);
+				next;
+			}
 
 
-	# Do all the normal file naming/copying/etc. if we found at least one file
-	my $failure = 0;
-	foreach my $file (@files) {
-		my $result = -1;
-		if (defined($file) && length($file) > 0 && -r $file) {
-			$result = &processFile($file, $path);
+			# Unrar if necessary
+			if (defined($IS_RAR) && length($IS_RAR) > 0) {
+				@newFiles = &unrar($IS_RAR, $path);
+			}
+
+			# Find all media files, recursively
+			@files = findMediaFiles($path, 0);
 		}
 		}
-		if (defined($result) && $result == 1) {
-			if ($DEBUG) {
-				print STDERR 'Torrent stored successfully: ' . $tor->{'name'} . "\n";
+
+		# Bail if we found no files
+		if (scalar(@files) < 1) {
+			print STDERR 'No media files found in torrent: ' . $tor->{'name'} . "\n";
+			next;
+		}
+
+		# Do all the normal file naming/copying/etc. if we found at least one file
+		my $failure = 0;
+		foreach my $file (@files) {
+			my $result = -1;
+			if (defined($file) && length($file) > 0 && -r $file) {
+				$result = &processFile($file, $path);
+			}
+			if (defined($result) && $result == 1) {
+				if ($DEBUG) {
+					print STDERR 'Torrent stored successfully: ' . $tor->{'name'} . "\n";
+				}
+			} elsif (defined($result) && $result == -1) {
+				if ($DEBUG) {
+					print STDERR 'Deleting bad torrent: ' . $tor->{'name'} . "\n";
+				}
+				last;
+			} else {
+				print STDERR 'Error storing file "' . basename($file) . '" from torrent: ' . $tor->{'name'} . "\n";
+				$failure = 1;
+				last;
 			}
 			}
-		} elsif (defined($result) && $result == -1) {
+		}
+
+		# Delete any files we added
+		foreach my $file (@newFiles) {
 			if ($DEBUG) {
 			if ($DEBUG) {
-				print STDERR 'Deleting bad torrent: ' . $tor->{'name'} . "\n";
+				print STDERR 'Deleting new file: ' . basename($file) . "\n";
 			}
 			}
-			last;
-		} else {
-			print STDERR 'Error storing file "' . basename($file) . '" from torrent: ' . $tor->{'name'} . "\n";
-			$failure = 1;
-			last;
+			unlink($file);
 		}
 		}
-	}
 
 
-	# Delete any files we added
-	foreach my $file (@newFiles) {
-		if ($DEBUG) {
-			print STDERR 'Deleting new file: ' . basename($file) . "\n";
+		# Only delete the torrent on success
+		if (!$failure) {
+			delTor($tor);
+			next;
 		}
 		}
-		unlink($file);
 	}
 	}
 
 
-	# Only delete the torrent on success
-	if (!$failure) {
-		delTor($tor);
-		next;
+	# NZB
+	if (available($CONFIG{'NZB_URL'})) {
+		
+		# Fetch the list of historical (i.e. completed) NZBs
+		$fetch->url($CONFIG{'NZB_URL'});
+		$fetch->post_content($history);
+		$fetch->fetch();
+
+		my $nzbs = decode_json(scalar($fetch->content()));
+		
+		# Process
+		if ($DEBUG) {
+			print STDERR "Processing NZBs...\n";
+		}
 	}
 	}
 }
 }
 
 
@@ -380,12 +420,18 @@ sub getDest($$$$) {
 				# Bail if the matches do not overlap
 				# Bail if the matches do not overlap
 				if ($show =~ m/^${series}\b/) {
 				if ($show =~ m/^${series}\b/) {
 					if ($DEBUG) {
 					if ($DEBUG) {
-						print STDERR 'Using longer match (' . $showsCan{$show} . ') in place of shorter match (' . $showsCan{$series} . ")\n";
+						print STDERR 'Using longer match ('
+						  . $showsCan{$show}
+						  . ') in place of shorter match ('
+						  . $showsCan{$series} . ")\n";
 					}
 					}
 					$series = $show;
 					$series = $show;
 				} elsif ($series =~ m/^${show}\b/) {
 				} elsif ($series =~ m/^${show}\b/) {
 					if ($DEBUG) {
 					if ($DEBUG) {
-						print STDERR 'Using longer match (' . $showsCan{$series} . ') in place of shorter match (' . $showsCan{$show} . ")\n";
+						print STDERR 'Using longer match ('
+						  . $showsCan{$series}
+						  . ') in place of shorter match ('
+						  . $showsCan{$show} . ")\n";
 					}
 					}
 					$series = $series;
 					$series = $series;
 				} else {
 				} else {
@@ -577,7 +623,7 @@ sub processFile($$) {
 
 
 	# Allow multiple guesses at the series/season/episode
 	# Allow multiple guesses at the series/season/episode
 	my $dest = '';
 	my $dest = '';
-	LOOP: {
+  LOOP: {
 
 
 		# Determine the series, season, and episode number
 		# Determine the series, season, and episode number
 		my ($series, $season, $episode) = &getSSE($filename);
 		my ($series, $season, $episode) = &getSSE($filename);
@@ -772,3 +818,26 @@ sub findMediaFiles($$) {
 	}
 	}
 	return @files;
 	return @files;
 }
 }
+
+# Check availability of web interface
+sub available($) {
+	my ($url) = @_;
+	if ($DEBUG) {
+		print STDERR "Checking for interface\n";
+	}
+	
+	# Reset from the last host
+	$fetch = Fetch->new();
+
+	my ($host) = $url =~ /^(\w+\:\/\/[^\/]+\/?)/;
+	$fetch->url($host);
+	if ($fetch->fetch('nocheck' => 1) != 200) {
+		if ($DEBUG) {
+			print STDERR 'Code: ' . $fetch->status_code() . "\n";
+			print STDERR 'Content: ' . $fetch->content() . "\n";
+			warn('Interface not available: ' . $host . "\n");
+		}
+		return 0;
+	}
+	return 1;
+}