renameFromDisk.pl 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. #!/usr/bin/perl
  2. use strict;
  3. use warnings;
  4. # Includes
  5. use URI::Escape;
  6. use XML::Simple;
  7. use LWP::Simple;
  8. use File::Basename;
  9. use HTTP::Request::Common;
  10. # Debug
  11. my $DEBUG = 0;
  12. if ($ENV{'DEBUG'}) {
  13. $DEBUG = 1;
  14. }
  15. # Parameters
  16. my ($host, $user, $pass, $section, $series) = @ARGV;
  17. if (!$host || !$user || !$pass || (!$section && !$series)) {
  18. die('Usage: ' . basename($0) . ' host[:port] user passwd section_number [series_metadata_number]');
  19. }
  20. # Globals
  21. if (!($host =~ /\:\d+$/)) {
  22. $host .= ':32400';
  23. }
  24. my $baseURL = 'http://' . $host;
  25. my $auth = 'X-Plex-User=' . $user . '&X-Plex-Pass=' . $pass;
  26. my $xml = new XML::Simple;
  27. my @shows = ();
  28. if ($series) {
  29. # Fetch just the provided show
  30. print STDERR 'Fetching show ' . $series . "\n";
  31. @shows = ('/library/metadata/' . $series . '/children');
  32. } else {
  33. # Fetch the list of all shows in the section
  34. print STDERR "Fetching shows...\n";
  35. {
  36. my $content = get($baseURL . '/library/sections/' . $section . '/all/?' . $auth);
  37. if ($DEBUG) {
  38. print STDERR $baseURL . '/library/sections/' . $section . '/all/?' . $auth . "\n";
  39. }
  40. if (!$content) {
  41. die(basename($0) . ': Unable to fetch data for section: ' . $section . "\n");
  42. }
  43. # Find all TV shows in the section
  44. {
  45. my $tree = $xml->XMLin($content);
  46. @shows = keys(%{ $tree->{'Directory'} });
  47. }
  48. }
  49. print STDERR 'Found ' . scalar(@shows) . " shows\n";
  50. }
  51. # Fetch the list of all seasons in each show
  52. my @seasons = ();
  53. print STDERR "Fetching seasons...\n";
  54. foreach my $show (@shows) {
  55. my $content = get($baseURL . $show . '?' . $auth);
  56. if ($DEBUG) {
  57. print STDERR $baseURL . $show . '?' . $auth . "\n";
  58. }
  59. if (!$content) {
  60. warn(basename($0) . ': Could not fetch seasons for show: ' . $show . "\n");
  61. next;
  62. }
  63. # Find all seasons in the show
  64. {
  65. my $tree = $xml->XMLin($content);
  66. # XML::Simple retuns different structure depending on the number of same-named child elements
  67. if ($tree->{'Directory'}->{'key'}) {
  68. push(@seasons, $tree->{'Directory'}->{'key'});
  69. } else {
  70. push(@seasons, keys(%{ $tree->{'Directory'} }));
  71. }
  72. }
  73. }
  74. print STDERR 'Found ' . scalar(@seasons) . " seasons\n";
  75. # Forget the list of shows
  76. undef(@shows);
  77. # Fetch the list of all episodes in each season
  78. my @episodes = ();
  79. print STDERR "Fetching episodes...\n";
  80. foreach my $season (@seasons) {
  81. my $content = get($baseURL . $season . '?' . $auth);
  82. if ($DEBUG) {
  83. print STDERR $baseURL . $season . '?' . $auth . "\n";
  84. }
  85. if (!$content) {
  86. warn(basename($0) . ': Could not fetch episodes for season: ' . $season . "\n");
  87. next;
  88. }
  89. # Find all episodes in the season
  90. {
  91. my $tree = $xml->XMLin($content);
  92. # XML::Simple retuns different structure depending on the number of same-named child elements
  93. if ($tree->{'Video'}->{'key'}) {
  94. push(@episodes, $tree->{'Video'}->{'key'});
  95. } else {
  96. push(@episodes, keys(%{ $tree->{'Video'} }));
  97. }
  98. }
  99. }
  100. print STDERR 'Found ' . scalar(@episodes) . " episodes\n";
  101. # Forget the list of seasons
  102. undef(@seasons);
  103. # Fetch the metadata from each episode
  104. print STDERR "Checking episodes...\n";
  105. foreach my $episode (@episodes) {
  106. my $content = get($baseURL . $episode . '?' . $auth);
  107. if ($DEBUG) {
  108. print STDERR $baseURL . $episode . '?' . $auth . "\n";
  109. }
  110. if (!$content) {
  111. warn(basename($0) . ': Could not fetch metadata for episode: ' . $episode . "\n");
  112. next;
  113. }
  114. # Parse the metadata
  115. {
  116. my $tree = $xml->XMLin($content);
  117. my $title = $tree->{'Video'}->{'title'};
  118. my $summary = $tree->{'Video'}->{'summary'};
  119. my $file = $tree->{'Video'}->{'Media'}->{'Part'}->{'file'};
  120. if (!$title) {
  121. warn('Invalid title for episode: ' . $episode . "\n");
  122. next;
  123. }
  124. if (!$file) {
  125. if (!$tree->{'Video'}->{'Media'}->{'key'}) {
  126. warn('No support for multi-file episodes: ' . $episode . "\n");
  127. } else {
  128. warn('Invalid file for episode: ' . $episode . "\n");
  129. }
  130. next;
  131. }
  132. # Rename if the file has a valid name but the episode does not
  133. $file = uri_unescape($file);
  134. $file = basename($file);
  135. $file =~ s/^\d+\s+\-\s*//;
  136. $file =~ s/S\d+E\d+\s+\-\s*//i;
  137. $file =~ s/\.\w{2,4}//;
  138. if (!$file || length($file) < 2 || $file =~ /NoName/i || $file =~ /Episode\s+\d+/i) {
  139. next;
  140. }
  141. if ($file =~ /\s+S\d+D\d+\-\d+$/) {
  142. print STDERR 'Skipping encoder-named file: ' . $file . "\n";
  143. }
  144. if ($title =~ /^Episode\s+\d+$/) {
  145. my $ua = LWP::UserAgent->new();
  146. my $encoded = uri_escape($file);
  147. print STDERR 'Renaming ' . $episode . ' from "' . $title . '" to "' . $file . '"' . "\n";
  148. my $url = $baseURL . $episode . '?title=' . $encoded . '&titleSort=' . $encoded . '&title.locked=1&titleSort.locked=1&' . $auth;
  149. my $request = HTTP::Request->new('PUT' => $url);
  150. $ua->request($request);
  151. if (!$summary) {
  152. print STDERR "\tAlso setting summary\n";
  153. $url = $baseURL . $episode . '?title=' . $encoded . '&summary=' . $encoded . '&summary.locked=1&' . $auth;
  154. $request = HTTP::Request->new('PUT' => $url);
  155. $ua->request($request);
  156. }
  157. }
  158. }
  159. }