critrole.pl 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. #!/usr/bin/perl
  2. use strict;
  3. use warnings;
  4. use LWP::UserAgent;
  5. use File::Basename;
  6. use IPC::Cmd qw( can_run );
  7. # Parameters
  8. my $TOP_URL = 'http://geekandsundry.com/shows/critical-role/';
  9. my $PG_MATCH = qr/\<link rel=\'next\' href=\'([^\']+\/page\/\d+\/)\'/;
  10. my $EP_MATCH = qr/^https?\:\/\/[^\/]+\/(?:critical\-role\-episode\-\d+|.*\bcritical-role\b.*\bcampaign-2-episode-\d+)/i;
  11. my $EP_EXCLUDE = qr/-podcast-/i;
  12. my $INI_PATH = `~/bin/video/mediaPath` . '/YouTube/Critical Role (None)/extra_videos.ini';
  13. my $UA_STR = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.75.14 (KHTML, like Gecko) Version/7.0.3 Safari/7046A194A';
  14. my $TIMEOUT = 10;
  15. # Debug
  16. our $DEBUG = 0;
  17. if (defined($ENV{'DEBUG'}) && $ENV{'DEBUG'} =~ /(\d+)/) {
  18. $DEBUG = $1;
  19. }
  20. # ENV Parameters
  21. if (exists($ENV{'TOP_URL'})) {
  22. $TOP_URL = $ENV{'TOP_URL'};
  23. if ($DEBUG) {
  24. print STDERR 'Using ENV TOP_URL: ' . $TOP_URL . "\n";
  25. }
  26. }
  27. if (exists($ENV{'EP_MATCH'})) {
  28. $EP_MATCH = qr/${ENV{'EP_MATCH'}}/i;
  29. if ($DEBUG) {
  30. print STDERR 'Using ENV EP_MATCH: qr/' . $ENV{'EP_MATCH'} . "/i\n";
  31. }
  32. }
  33. if (exists($ENV{'EP_EXCLUDE'})) {
  34. $EP_EXCLUDE = qr/${ENV{'EP_EXCLUDE'}}/i;
  35. if ($DEBUG) {
  36. print STDERR 'Using ENV EP_EXCLUDE: qr/' . $ENV{'EP_EXCLUDE'} . "/i\n";
  37. }
  38. }
  39. if (exists($ENV{'INI_PATH'})) {
  40. $INI_PATH = $ENV{'INI_PATH'};
  41. if ($DEBUG) {
  42. print STDERR 'Using ENV INI_PATH: ' . $INI_PATH . "\n";
  43. }
  44. }
  45. # Setup LWP
  46. my $ua = LWP::UserAgent->new();
  47. if (!$ua) {
  48. die('Unable to init UA: ' . $! . "\n");
  49. }
  50. $ua->agent($UA_STR);
  51. $ua->timeout($TIMEOUT);
  52. $ua->env_proxy();
  53. # Read the existing INI file
  54. my %old_ytids = ();
  55. {
  56. my $fh = undef();
  57. open($fh, '<', $INI_PATH)
  58. or die('Unable to read ini file: ' . $INI_PATH . ': ' . $! . "\n");
  59. my $url = undef();
  60. while (<$fh>) {
  61. if (/^\s*#\s*(https?\:\/\/[^\s]+)/i) {
  62. $url = $1;
  63. } elsif (/([\w\-]+)/) {
  64. if (!$url) {
  65. $url = 'Unknown';
  66. }
  67. $old_ytids{$1} = $url;
  68. $url = undef();
  69. if ($DEBUG > 1) {
  70. print STDERR 'Found existing YTID: ' . $1 . ' # ' . $old_ytids{$1} . "\n";
  71. }
  72. }
  73. }
  74. close($fh);
  75. }
  76. # Use the top-level page to find episode-specific pages
  77. my @episodes = ();
  78. {
  79. my $url = $TOP_URL;
  80. NEXT_PAGE: {
  81. if ($DEBUG) {
  82. print STDERR 'Loading top page: ' . $url . "\n";
  83. }
  84. my $response = $ua->get($url);
  85. if (!$response->is_success()) {
  86. die(basename($0) . ": Unable to fetch top-level page\n");
  87. }
  88. foreach my $link (split(/\<a\s+/, $response->decoded_content())) {
  89. if ($link =~ /\s+href=\"([^\"]+)\"/) {
  90. my $href = $1;
  91. if ($href =~ $EP_EXCLUDE) {
  92. next;
  93. }
  94. if ($href =~ $EP_MATCH) {
  95. push(@episodes, $href);
  96. }
  97. }
  98. }
  99. if ($response->decoded_content() =~ $PG_MATCH) {
  100. $url = $1;
  101. redo NEXT_PAGE;
  102. }
  103. }
  104. }
  105. if ($DEBUG) {
  106. print STDERR 'Episode URLs (' . scalar(@episodes) . "):\n\t" . join("\n\t", @episodes) . "\n";
  107. }
  108. # Find each episode's YTID
  109. my %ytids = ();
  110. {
  111. foreach my $url (@episodes) {
  112. my $response = $ua->get($url);
  113. if (!$response->is_success()) {
  114. die(basename($0) . ': Unable to fetch episode page: ' . $url . "\n");
  115. }
  116. if ($response->decoded_content() =~ /data-ytid=\"([^\"]+)\"/) {
  117. $ytids{$1} = $url;
  118. } elsif ($DEBUG > 0) {
  119. print STDERR 'No YTID found on: ' .$url . "\n";
  120. if ($DEBUG > 1) {
  121. print STDERR "\tPage content:\n" . $response->decoded_content() . "\n\n";
  122. }
  123. }
  124. }
  125. }
  126. if ($DEBUG) {
  127. print STDERR 'YouTube IDs (' . scalar(keys(%ytids)) . "):\n\t" . join("\n\t", keys(%ytids)) . "\n";
  128. }
  129. # Drop known episodes
  130. {
  131. foreach my $id (keys(%ytids)) {
  132. if (exists($old_ytids{$id})) {
  133. if ($DEBUG > 1) {
  134. print 'Ignoring known episode: ' . $id . ' # ' . $old_ytids{$id} . "\n";
  135. }
  136. delete($ytids{$id});
  137. }
  138. }
  139. }
  140. # Update the INI file
  141. {
  142. my $fh = undef();
  143. open($fh, '>>', $INI_PATH)
  144. or die('Unable to write ini file: ' . $INI_PATH . ': ' . $! . "\n");
  145. foreach my $id (keys(%ytids)) {
  146. if ($DEBUG) {
  147. print 'Adding episode: ' . $id . ' # ' . $ytids{$id} . "\n";
  148. }
  149. print $fh "\n# " . $ytids{$id} . "\n" . $id . "\n";
  150. }
  151. close($fh);
  152. }
  153. # Autodate
  154. my @autodate = ($ENV{'HOME'} . '/bin/video/yt/autodate.sh');
  155. if (can_run($autodate[0])) {
  156. if ($DEBUG) {
  157. print STDERR "Executing autodate script...\n";
  158. }
  159. system(@autodate);
  160. }
  161. # Cleanup
  162. exit(0);