Просмотр исходного кода

Add support for an extra_videos.ini file -- fetch extra videos and metadata simply by providing the episode number and video ID

Add support for disabling each stage -- NO_FETCH NO_SEARCH NO_CHANNEL NO_FILES NO_EXTRAS -- while otherwise executing normally
Zachary Kotlarek 12 лет назад
Родитель
Сommit
26ebc6f02c
1 измененных файлов с 204 добавлено и 72 удалено
  1. 204 72
      yt/subscribe.pl

+ 204 - 72
yt/subscribe.pl

@@ -16,33 +16,58 @@ use WWW::YouTube::Download;
 use PrettyPrint;
 
 # Paramters
+my $EXTRAS_FILE = 'extra_videos.ini';
 my $CURL_BIN    = 'curl';
 my @CURL_ARGS   = ('-4', '--insecure', '-C', '-');
 my $BATCH_SIZE  = 50;
 my $MAX_INDEX   = 500;
-my $URL_PREFIX  = 'http://gdata.youtube.com/feeds/api/users/';
-my $URL_SUFFIX  = '/uploads';
-my %CHAN_PARAMS = (
-	'strict' => 1,
-	'v'      => 2,
-	'alt'    => 'json',
-);
-my %LIST_PARAMS = (
-	'strict' => 1,
-	'v'      => 2,
-	'alt'    => 'jsonc',
+my $API_URL     = 'https://gdata.youtube.com/feeds/api/';
+my %API         = (
+	'search' => {
+		'prefix' => $API_URL . 'users/',
+		'suffix' => '/uploads',
+		'params' => {
+			'start-index' => 1,
+			'max-results' => 1,
+			'strict'      => 1,
+			'v'           => 2,
+			'alt'         => 'jsonc',
+		},
+	},
+	'video' => {
+		'prefix' => $API_URL . 'videos/',
+		'suffix' => '',
+		'params' => {
+			'strict' => 1,
+			'v'      => 2,
+			'alt'    => 'jsonc'
+		},
+	},
+	'channel' => {
+		'prefix' => $API_URL . 'users/',
+		'suffix' => '',
+		'params' => {
+			'strict' => 1,
+			'v'      => 2,
+			'alt'    => 'json'
+		},
+	},
 );
 
 # Prototypes
 sub findVideos($);
 sub findFiles($);
+sub findVideo($);
 sub ytURL($);
 sub buildNFO($);
 sub buildSeriesNFO($);
 sub getChannel($);
-sub fetchParse($);
-sub buildURL($$);
+sub fetchParse($$);
 sub saveString($$);
+sub readExtras($);
+sub parseVideoData($);
+sub addExtras($$);
+sub saveChannel($$);
 
 # Sanity check
 if (scalar(@ARGV) < 1) {
@@ -60,7 +85,7 @@ if (length($user) < 1 || !($user =~ /^\w+$/)) {
 	die('Invalid user: ' . $user . "\n");
 }
 
-# Environmental parameters
+# Environmental parameters (debug)
 my $DEBUG = 0;
 if ($ENV{'DEBUG'}) {
 	if ($ENV{'DEBUG'} =~ /(\d+)/) {
@@ -73,6 +98,24 @@ my $NO_FETCH = 0;
 if ($ENV{'NO_FETCH'}) {
 	$NO_FETCH = 1;
 }
+my $NO_SEARCH = 0;
+if ($ENV{'NO_SEARCH'}) {
+	$NO_SEARCH = 1;
+}
+my $NO_FILES = 0;
+if ($ENV{'NO_FILES'}) {
+	$NO_FILES = 1;
+}
+my $NO_CHANNEL = 0;
+if ($ENV{'NO_CHANNEL'}) {
+	$NO_CHANNEL = 1;
+}
+my $NO_EXTRAS = 0;
+if ($ENV{'NO_EXTRAS'}) {
+	$NO_EXTRAS = 1;
+}
+
+# Environmental parameters (functional)
 my $RENAME = 0;
 if ($ENV{'RENAME'}) {
 	$RENAME = 1;
@@ -85,50 +128,44 @@ if ($ENV{'BATCH_SIZE'} && $ENV{'BATCH_SIZE'} =~ /(\d+)/) {
 }
 
 # Construct globals
-$LIST_PARAMS{'max-results'} = $BATCH_SIZE;
+$API{'search'}{'params'}{'max-results'} = $BATCH_SIZE;
 if (!$DEBUG) {
 	push(@CURL_ARGS, '--silent');
 }
 
 # Grab the channel data
-my $channel = getChannel($user);
-
-# Create the channel NFO and poster, if needed
-my $nfo = $dir . '/tvshow.nfo';
-if (!-e $nfo) {
-	if ($DEBUG) {
-		print STDERR 'Saving series data for: ' . $channel->{'title'} . "\n";
-	}
-
-	# Save the poster
-	if (exists($channel->{'thumbnail'}) && length($channel->{'thumbnail'}) > 5) {
-		my $poster = $dir . '/poster.jpg';
-		my $jpg    = get($channel->{'thumbnail'});
-		saveString($poster, $jpg);
-	}
-
-	# Save the series NFO
-	my $xml = buildSeriesNFO($channel);
-	if ($DEBUG > 1) {
-		print STDERR 'Saving NFO: ' . $xml . "\n";
-	}
-	saveString($nfo, $xml);
+my $channel = {};
+if (!$NO_CHANNEL) {
+	$channel = getChannel($user);
+	saveChannel($dir, $channel);
 }
 
 # Find all the user's videos on YT
-my $videos = findVideos($user);
+my $videos = {};
+if (!$NO_SEARCH) {
+	$videos = findVideos($user);
+}
+
+# Find any requested "extra" videos
+my $extras = {};
+if (!$NO_EXTRAS) {
+	$extras = addExtras($dir, $videos);
+}
 
 # Find all existing YT files on disk
-my $files = findFiles($dir);
+my $files = {};
+if (!$NO_FILES) {
+	$files = findFiles($dir);
+}
 
 # Fill in missing videos and NFOs
 foreach my $id (keys(%{$videos})) {
 	my $basePath = $dir . '/S01E' . sprintf('%02d', $videos->{$id}->{'number'}) . ' - ' . $id . '.';
-	$nfo = $basePath . 'nfo';
+	my $nfo = $basePath . 'nfo';
 
-	# Warn (and optionally rename) if the episode numbers drift
+	# Warn (and optionally rename) if the video numbers drift
 	if (exists($files->{$id}) && $files->{$id}->{'number'} != $videos->{$id}->{'number'}) {
-		warn('Video ' . $id . ' had episode number ' . $files->{$id}->{'number'} . ' but now has episode number ' . $videos->{$id}->{'number'} . "\n");
+		warn('Video ' . $id . ' had video number ' . $files->{$id}->{'number'} . ' but now has video number ' . $videos->{$id}->{'number'} . "\n");
 		if ($RENAME) {
 			print STDERR 'Renaming ' . $files->{$id}->{'path'} . ' => ' . $basePath . $files->{$id}->{'suffix'} . "\n";
 			rename($files->{$id}->{'path'}, $basePath . $files->{$id}->{'suffix'});
@@ -158,7 +195,7 @@ foreach my $id (keys(%{$videos})) {
 			push(@cmd, @CURL_ARGS);
 			push(@cmd, '-o', $basePath . $suffix, $url);
 			if ($NO_FETCH) {
-				print STDERR 'Not fetching: ' . join(' ', @cmd) . "\n";
+				print STDERR 'Not running: ' . join(' ', @cmd) . "\n";
 			} else {
 				if ($DEBUG > 1) {
 					print STDERR join(' ', @cmd) . "\n";
@@ -307,6 +344,11 @@ sub findFiles($) {
 	my ($dir) = @_;
 	my %files = ();
 
+	# Allow complete bypass
+	if ($NO_FILES) {
+		return \%files;
+	}
+
 	# Read the output directory
 	my $fh = undef();
 	opendir($fh, $dir)
@@ -350,21 +392,17 @@ sub findFiles($) {
 	return \%files;
 }
 
-sub buildURL($$) {
-	my ($base, $params) = @_;
-	my $url = $base . '?';
-	foreach my $name (keys(%{$params})) {
-		$url .= '&' . uri_escape($name) . '=' . uri_escape($params->{$name});
-	}
-	return $url;
-}
+sub fetchParse($$) {
+	my ($name, $id) = @_;
 
-sub fetchParse($) {
-	my ($url) = @_;
+	my $url = $API{$name}{'prefix'} . $id . $API{$name}{'suffix'} . '?';
+	foreach my $key (keys(%{ $API{$name}{'params'} })) {
+		$url .= '&' . uri_escape($key) . '=' . uri_escape($API{$name}{'params'}{$key});
+	}
 
 	# Fetch
 	if ($DEBUG) {
-		print STDERR 'Fetching list URL: ' . $url . "\n";
+		print STDERR 'Fetching API URL: ' . $url . "\n";
 	}
 	my $content = get($url);
 	if (!defined($content) || length($content) < 10) {
@@ -383,12 +421,36 @@ sub fetchParse($) {
 	return $data;
 }
 
+sub saveChannel($$) {
+	my ($dir, $channel) = @_;
+
+	my $nfo = $dir . '/tvshow.nfo';
+	if (!-e $nfo) {
+		if ($DEBUG) {
+			print STDERR 'Saving series data for: ' . $channel->{'title'} . "\n";
+		}
+
+		# Save the poster
+		if (exists($channel->{'thumbnail'}) && length($channel->{'thumbnail'}) > 5) {
+			my $poster = $dir . '/poster.jpg';
+			my $jpg    = get($channel->{'thumbnail'});
+			saveString($poster, $jpg);
+		}
+
+		# Save the series NFO
+		my $xml = buildSeriesNFO($channel);
+		if ($DEBUG > 1) {
+			print STDERR 'Saving NFO: ' . $xml . "\n";
+		}
+		saveString($nfo, $xml);
+	}
+}
+
 sub getChannel($) {
 	my ($user) = @_;
 
 	# Build, fetch, parse
-	my $url = buildURL($URL_PREFIX . $user, \%CHAN_PARAMS);
-	my $data = fetchParse($url);
+	my $data = fetchParse('channel', $user);
 
 	if (!exists($data->{'entry'}) || ref($data->{'entry'}) ne 'HASH') {
 		die("Invalid channel data\n");
@@ -406,13 +468,91 @@ sub getChannel($) {
 	return \%channel;
 }
 
+sub readExtras($) {
+	my ($dir) = @_;
+	my %extras = ();
+
+	# Allow complete bypass
+	if ($NO_EXTRAS) {
+		return \%extras;
+	}
+
+	# Read and parse the extra videos file, if it exists
+	my $file = $dir . '/' . $EXTRAS_FILE;
+	if (-e $file) {
+		my $fh;
+		open($fh, $file)
+		  or die('Unable to open extra videos file: ' . $! . "\n");
+		while (<$fh>) {
+
+			# Skip blank lines and comments
+			if ($_ =~ /^\s*#/ || $_ =~ /^\s*$/) {
+				next;
+			}
+
+			# Match our specific format or whine
+			if ($_ =~ /^\s*(\d+)\s*[=:>]+\s*([\w\-]+)\s*$/) {
+				if ($DEBUG > 1) {
+					print STDERR 'Adding extra video: ' . $1 . ' => ' . $2 . "\n";
+				}
+				$extras{$2} = $1;
+			} else {
+				print STDERR 'Skipped extra video line: ' . $_;
+			}
+		}
+		close($fh);
+	}
+
+	return \%extras;
+}
+
+sub parseVideoData($) {
+	my ($data) = @_;
+	my %video = (
+		'title'       => $data->{'title'},
+		'date'        => str2time($data->{'uploaded'}),
+		'description' => $data->{'description'},
+		'duration'    => $data->{'duration'},
+		'rating'      => $data->{'rating'},
+		'creator'     => $data->{'uploader'},
+	);
+	return \%video;
+}
+
+sub findVideo($) {
+	my ($id) = @_;
+
+	# Build, fetch, parse
+	my $data = fetchParse('video', $id);
+
+	# Validate
+	if (!exists($data->{'data'})) {
+		die("Invalid video list\n");
+	}
+	$data = $data->{'data'};
+
+	# Parse the video data
+	return parseVideoData($data);
+}
+
+sub addExtras($$) {
+	my ($dir, $videos) = @_;
+	my $extras = readExtras($dir);
+	foreach my $id (keys(%{$extras})) {
+		my $video = findVideo($id);
+		$video->{'number'} = $extras->{$id};
+		$videos->{$id} = $video;
+	}
+}
+
 sub findVideos($) {
 	my ($user) = @_;
 	my %videos = ();
 
-	# Prepare the static URL elements
-	my $baseURL = $URL_PREFIX . $user . $URL_SUFFIX;
-	my %params  = %LIST_PARAMS;
+	# Allow complete bypass
+	if ($NO_SEARCH) {
+		return \%videos;
+	}
 
 	# Loop through until we have all the entries
 	my $index     = 1;
@@ -421,9 +561,8 @@ sub findVideos($) {
 	{
 
 		# Build, fetch, parse
-		$params{'start-index'} = $index;
-		my $url = buildURL($baseURL, \%params);
-		my $data = fetchParse($url);
+		$API{'search'}{'params'}{'start-index'} = $index;
+		my $data = fetchParse('search', $user);
 
 		# Grab the total count, so we know when to stop
 		if (!exists($data->{'data'})) {
@@ -441,16 +580,9 @@ sub findVideos($) {
 		my $items  = $data->{'items'};
 		my $offset = 0;
 		foreach my $item (@{$items}) {
-			my %tmp = (
-				'number'      => ($itemCount - $index - $offset + 1),
-				'title'       => $item->{'title'},
-				'date'        => str2time($item->{'uploaded'}),
-				'description' => $item->{'description'},
-				'duration'    => $item->{'duration'},
-				'rating'      => $item->{'rating'},
-				'creator'     => $item->{'uploader'},
-			);
-			$videos{ $item->{'id'} } = \%tmp;
+			my $video = parseVideoData($item);
+			$video->{'number'} = $itemCount - $index - $offset + 1;
+			$videos{ $item->{'id'} } = $video;
 			$offset++;
 		}