Pastebin launched a little side project called VERYVIRAL.com, check it out ;-) Want more features on Pastebin? Sign Up, it's FREE!
Guest

Sort TV debug

By: a guest on Oct 25th, 2010  |  syntax: Perl  |  size: 28.82 KB  |  views: 78  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print
Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  1. #!/usr/bin/perl
  2.  
  3. # 2010 Z. Cliffe Schreuders
  4. # free software: GPL v3 or later
  5. #
  6. # sorts tv shows into tvshow/series directories
  7. # if the dirs don't exist they are created
  8. # updates xbmc via the web interface
  9. # unsorted files are moved to a dir if specifed
  10. #
  11. # other contributers:
  12. # salithus - xbmc forum
  13. # schmoko - xbmc forum
  14. #
  15. # Please goto the xbmc forum to discuss SortTV:
  16. # http://forum.xbmc.org/showthread.php?t=75949
  17. #
  18. # Get the latest version from here:
  19. # http://sourceforge.net/projects/sorttv/files/
  20. #
  21. # Cliffe's website:
  22. # http://schreuders.org/
  23. #
  24. # Please consider a $5 donation if you find this program helpful.
  25.  
  26.  
  27. use File::Copy::Recursive "dirmove", "dircopy";
  28. use File::Copy;
  29. use File::Glob ':glob';
  30. use LWP::Simple;
  31. use File::Spec::Functions "rel2abs";
  32. use File::Basename;
  33. use TVDB::API;
  34. use File::Find;
  35. use FileHandle;
  36. use warnings;
  37. use strict;
  38.  
  39. my ($sortdir, $tvdir, $nonepisodedir, $xbmcwebserver, $matchtype);
  40. my ($showname, $series, $episode, $pureshowname) = "";
  41. my ($newshows, $new, $log);
  42. my $REDO_FILE = my $moveseasons = "TRUE";
  43. my $usedots = my $rename = my $logfile = my $verbose = my $seasondoubledigit = my $removesymlinks = my $needshowexist = my $windowsnames = 0;
  44. my $seasontitle = "Season ";
  45. my $sortby = "MOVE";
  46. my $renameformat = "[SHOW_NAME] - [EP1][EP_NAME1]";
  47. my $treatdir = "RECURSIVELY_SORT_CONTENTS";
  48. my $fetchimages = "NEW_SHOWS";
  49. my $imagesformat = "POSTER";
  50. my @showrenames;
  51. my $scriptpath = dirname(rel2abs($0));
  52. my $tvdblanguage = "en";
  53.  
  54. print "Got torough the declirations";
  55.  
  56. out("std", "SortTV\n", "~" x 6,"\n");
  57. get_config_from_file("$scriptpath/sorttv.conf");
  58. process_args(@ARGV);
  59. if(!defined($sortdir) || !defined($tvdir)) {
  60.         out("warn", "Incorrect usage or configuration (missing sort or sort-to directories)\n");
  61.         out("warn", "run 'perl sorttv.pl --help' for more information about how to use SortTV");
  62.         exit;
  63. }
  64.  
  65. print "Got torough the first if";
  66.  
  67. my $TVDBAPIKEY = "FDDBDB916D936956";
  68. my $tvdb = TVDB::API::new($TVDBAPIKEY);
  69. # if uses thetvdb, set it up
  70. if($renameformat =~ /\[EP_NAME\d]/i || $fetchimages ne "FALSE") {
  71.         $tvdb->setLang($tvdblanguage);
  72.         my $hashref = $tvdb->getAvailableMirrors();
  73.         $tvdb->setMirrors($hashref);
  74.         $tvdb->chooseMirrors();
  75.         unless (-e "$scriptpath/.cache" || mkdir "$scriptpath/.cache") {
  76.                 out("warn", "WARN: Could not create cache dir: $scriptpath/cache $!\n");
  77.                 exit;
  78.         }
  79.         $tvdb->setCacheDB("$scriptpath/.cache/.tvdb.db");
  80.         $tvdb->setUserAgent("SortTV");
  81.         $tvdb->setBannerPath("$scriptpath/.cache/");
  82. }
  83.  
  84. print "Got torough the second if";
  85.  
  86. $log = FileHandle->new("$logfile", "a") or out("warn", "WARN: Could not open log file $logfile: $!\n") if $logfile;
  87.  
  88. display_info();
  89.  
  90. sort_directory($sortdir);
  91.  
  92. if($xbmcwebserver && $newshows) {
  93.         sleep(4);
  94.         get "http://$xbmcwebserver/xbmcCmds/xbmcHttp?command=ExecBuiltIn(Notification(,NEW EPISODES NOW AVAILABLE TO WATCH\n$newshows, 7000))";
  95. }
  96.  
  97. $log->close if(defined $log);
  98. exit;
  99.  
  100. print "Got torough the third if";
  101.  
  102. sub sort_directory {
  103.         my ($sortd) = @_;
  104.         FILE: foreach my $file (bsd_glob($sortd.'*')) {
  105.                 $showname = "";
  106.                 # Regex for tv show season directory
  107.                 if(-l $file) {
  108.                         if($removesymlinks eq "TRUE") {
  109.                                 out("std", "DELETE: Removing symlink: $file\n");
  110.                                 unlink($file) or out("warn", "WARN: Could not delete symlink $file: $!\n");
  111.                         }
  112.                         # otherwise file is a symlink, ignore
  113.                 } elsif(-d $file && $treatdir eq "IGNORE") {
  114.                         # ignore directories
  115.                 } elsif(-d $file && $treatdir eq "RECURSIVELY_SORT_CONTENTS") {
  116.                         sort_directory("$file/");
  117.                         # removes any empty directories from the to-sort directory and sub-directories
  118.                         finddepth(sub{rmdir},"$sortd");
  119.                 } elsif(-d $file && $file =~ /.*\/(.*)(?:Season|Series|$seasontitle)\D?0*(\d+).*/i && $1) {
  120.                         $pureshowname = $1;
  121.                         if($seasondoubledigit eq "TRUE") {
  122.                                 $series = sprintf("%02d", $2);
  123.                         } else {
  124.                                 $series = $2;
  125.                         }
  126.                         $showname = fixtitle($pureshowname);
  127.                         if(move_series($pureshowname, $showname, $series, $file) eq $REDO_FILE) {
  128.                                 redo FILE;
  129.                         }
  130.                         # Regex for tv show episode: S01E01 or 1x1 or 1 x 1 etc
  131.                 } elsif(filename($file) =~ /(.*)(?:\.|\s)[Ss]0*(\d+)\s*[Ee]0*(\d+).*/
  132.                 || filename($file) =~ /(.*)(?:\.|\s)0*(\d+)\s*[xX]\s*0*(\d+).*/
  133.                 || ($matchtype eq "LIBERAL" && filename($file) =~ /(.*)(?:\.|\s)0*(\d+)\D*0*(\d+).*/)) {
  134.                         $pureshowname = $1;
  135.                         $showname = fixtitle($pureshowname);
  136.                         if($seasondoubledigit eq "TRUE") {
  137.                                 $series = sprintf("%02d", $2);
  138.                         } else {
  139.                                 $series = $2;
  140.                         }
  141.                         $episode = $3;
  142.                         if($showname ne "") {
  143.                                 if(move_episode($pureshowname, $showname, $series, $episode, $file) eq $REDO_FILE) {
  144.                                         redo FILE;
  145.                                 }
  146.                         }
  147.                 } elsif(defined $nonepisodedir) {
  148.                         my $newname = $file;
  149.                         $newname =~ s/$sortdir//;
  150.                         $newname = escape_myfilename($newname);
  151.                         out("std", "MOVING NON-EPISODE: $file to $nonepisodedir$newname\n");
  152.                         if(-d $file) {
  153.                                 dirmove($file, $nonepisodedir . $newname) or out("warn", "WARN: File $file cannot be copied to $nonepisodedir. : $!");
  154.                         } else {
  155.                                 move($file, $nonepisodedir . $newname) or out("warn", "WARN: File $file cannot be copied to $nonepisodedir. : $!");
  156.                         }
  157.                 }
  158.         }
  159. }
  160.  
  161. print "Got torough the main sort";
  162.  
  163. sub process_args {
  164.         foreach my $arg (@_) {
  165.                 if($arg =~ /^--non-episode-dir:(.*)/ || $arg =~ /^-ne:(.*)/) {
  166.                         if(-e $1) {
  167.                                 $nonepisodedir = $1;
  168.                                 # append a trailing / if it's not there
  169.                                 $nonepisodedir .= '/' if($nonepisodedir !~ /\/$/);
  170.                         } else {
  171.                                 out("warn", "WARN: Non-episode directory does not exist ($1)\n");
  172.                         }
  173.                 } elsif($arg =~ /^--xbmc-web-server:(.*)/ || $arg =~ /^-xs:(.*)/) {
  174.                         $xbmcwebserver = $1;
  175.                 } elsif($arg =~ /^--match-type:(.*)/ || $arg =~ /^-mt:(.*)/) {
  176.                         $matchtype = $1;
  177.                 } elsif($arg =~ /^--treat-directories:(.*)/ || $arg =~ /^-td:(.*)/) {
  178.                         $treatdir = $1;
  179.                 } elsif($arg =~ /^--show-name-substitute:(.*-->.*)/ || $arg =~ /^-sub:(.*-->.*)/) {
  180.                         push @showrenames, $1;
  181.                 } elsif($arg =~ /^--log-file:(.*)/ || $arg =~ /^-o:(.*)/) {
  182.                         $logfile = $1;
  183.                 } elsif($arg =~ /^--rename-episodes:(.*)/ || $arg =~ /^-rn:(.*)/) {
  184.                         $rename = $1;
  185.                 } elsif($arg =~ /^--lookup-language:(.*)/ || $arg =~ /^-lang:(.*)/) {
  186.                         $tvdblanguage = $1;
  187.                 } elsif($arg =~ /^--fetch-images:(.*)/ || $arg =~ /^-fi:(.*)/) {
  188.                         $fetchimages = $1;
  189.                 } elsif($arg =~ /^--images-format:(.*)/ || $arg =~ /^-if:(.*)/) {
  190.                         $imagesformat = $1;
  191.                 } elsif($arg =~ /^--require-show-directories-already-exist:(.*)/ || $arg =~ /^-rs:(.*)/) {
  192.                         $needshowexist = $1;
  193.                 } elsif($arg =~ /^--force-windows-compatible-filenames:(.*)/ || $arg =~ /^-fw:(.*)/) {
  194.                         $windowsnames = $1;
  195.                 } elsif($arg =~ /^--rename-format:(.*)/ || $arg =~ /^-rf:(.*)/) {
  196.                         $renameformat = $1;
  197.                 } elsif($arg =~ /^--remove-symlinks:(.*)/ || $arg =~ /^-rs:(.*)/) {
  198.                         $removesymlinks = $1;
  199.                 } elsif($arg =~ /^--use-dots-instead-of-spaces:(.*)/ || $arg =~ /^-dots:(.*)/) {
  200.                         $usedots = $1;
  201.                 } elsif($arg =~ /^--season-title:(.*)/ || $arg =~ /^-st:(.*)/) {
  202.                         $seasontitle = $1;
  203.                 } elsif($arg =~ /^--sort-by:(.*)/ || $arg =~ /^-by:(.*)/) {
  204.                         $sortby = $1;
  205.                 } elsif($arg =~ /^--season-double-digits:(.*)/ || $arg =~ /^-sd:(.*)/) {
  206.                         $seasondoubledigit = $1;
  207.                 } elsif($arg =~ /^--verbose:(.*)/ || $arg =~ /^-v:(.*)/) {
  208.                         $verbose = $1;
  209.                 } elsif($arg =~ /^--read-config-file:(.*)/ || $arg =~ /^-conf:(.*)/) {
  210.                         get_config_from_file($1);
  211.                 } elsif($arg =~ /^--directory-to-sort:(.*)/ || $arg =~ /^-sort:(.*)/) {
  212.                         if(-e $1) {
  213.                                 $sortdir = $1;
  214.                                 # append a trailing / if it's not there
  215.                                 $sortdir .= '/' if($sortdir !~ /\/$/);
  216.                         } else {
  217.                                 out("warn", "WARN: Directory to sort does not exist ($1)\n");
  218.                         }
  219.                 } elsif($arg =~ /^--directory-to-sort-into:(.*)/ || $arg =~ /^-sortto:(.*)/) {
  220.                         if(-e $1) {
  221.                                 $tvdir = $1;
  222.                                 # append a trailing / if it's not there
  223.                                 $tvdir .= '/' if($tvdir !~ /\/$/);
  224.                         } else {
  225.                                 out("warn", "WARN: Directory to sort into does not exist ($1)\n");
  226.                         }
  227.                 } elsif($arg eq "--help" || $arg eq "-h") {
  228.                         showhelp();
  229.                 } elsif(!defined($sortdir)) {
  230.                         if(-e $arg) {
  231.                                 $sortdir = $arg;
  232.                                 # append a trailing / if it's not there
  233.                                 $sortdir .= '/' if($sortdir !~ /\/$/);
  234.                         } else {
  235.                                 out("warn", "WARN: Directory to sort does not exist ($arg)\n");
  236.                         }
  237.                 } elsif(!defined($tvdir)) {
  238.                         if(-e $arg) {
  239.                                 $tvdir = $arg;
  240.                                 # append a trailing / if it's not there
  241.                                 $tvdir .= '/' if($tvdir !~ /\/$/);
  242.                         } else {
  243.                                 out("warn", "Directory to sort into does not exist ($arg)\n");
  244.                         }
  245.                 } else {
  246.                         out("warn", "WARN: Incorrect usage (invalid option): $arg\n");
  247.                         out("warn", "INFO: run 'perl sorttv.pl --help' for more information about how to use SortTV");
  248.                 }
  249.         }
  250. }
  251.  
  252. print "Got torough the aug";
  253.  
  254. sub get_config_from_file {
  255.         my ($filename) = @_;
  256.         my @arraytoconvert;
  257.        
  258.         if(open (IN, $filename)) {
  259.                 out("verbose", "INFO: Reading configuration settings from '$filename'\n");
  260.                 while(my $in = <IN>) {
  261.                         chomp($in);
  262.                         if($in =~ /^\s*#/ || $in =~ /^\s*$/) {
  263.                                 # ignores comments and whitespace
  264.                         } elsif($in =~ /(.+):(.+)/) {
  265.                                 process_args("--$1:$2");
  266.                         } else {
  267.                                 out("warn", "WARN: this line does not match expected format: '$in'\n");
  268.                         }
  269.                 }
  270.                 close (IN);
  271.         } else {
  272.                 out("warn", "WARN: Couldn't open config file '$filename': $!\n");
  273.                 out("warn", "INFO: An example config file is available and can make using SortTV easier\n");
  274.         }
  275. }
  276.  
  277. print "Got torough the config file";
  278.  
  279. sub showhelp {
  280.         my $heredoc = <<END;
  281. Usage: sorttv.pl [OPTIONS] [directory-to-sort directory-to-sort-into]
  282.  
  283. By default SortTV tries to read the configuration from sorttv.conf
  284.         (an example config file is available online)
  285. You can overwrite any config options with commandline arguments, which match the format of the config file (except that each argument starts with "--")
  286.  
  287. OPTIONS:
  288. --directory-to-sort:dir
  289.         A directory containing files to sort
  290.         For example, set this to where completed downloads are stored
  291.  
  292. --directory-to-sort-into:dir
  293.         Where to sort episodes into (dir that will contain dirs for each show)
  294.         This directory will contain the structure (Show)/(Seasons)/(episodes)
  295.  
  296. --non-episode-dir:dir
  297.         Where to put things that are not episodes
  298.         If this is supplied then files and directories that SortTV does not believe are episodes will be moved here
  299.         If not specified, non-episodes are not moved
  300.  
  301. --xbmc-web-server:host:port
  302.         host:port for xbmc webserver, to automatically update library when new episodes arrive
  303.         Remember to enable the webserver within xbmc, and "set the content" of your TV directory in xbmc.
  304.         If not specified, xbmc is not updated
  305.  
  306. --log-file:filepath
  307.         Log to this file
  308.         If not specified, output only goes to stdout (the screen)
  309.  
  310. --verbose:[TRUE|FALSE]
  311.         Output verbosity. Set to TRUE to show messages describing the decision making process.
  312.         If not specified, FALSE
  313.  
  314. --read-config-file:filepath
  315.         Secondary config file, overwrites settings loaded so far
  316.         If not specified, only the default config file is loaded (sorttv.conf)
  317.  
  318. --rename-episodes:[TRUE|FALSE]
  319.         Rename episodes to "show name S01E01.ext" format when moving
  320.         If not specified, FALSE
  321.  
  322. --rename-format:{formatstring}
  323.         the format to use if renaming to a new format (as specified above)
  324.         Hint: including the Episode Title as part of the name slows the process down a bit since titles are retrieved from thetvdb.com
  325.         The formatstring can be made up of:
  326.         [SHOW_NAME]: "My Show"
  327.         [EP1]: "S01E01"
  328.         [EP2]: "1x1"
  329.         [EP3]: "1x01"
  330.         [EP_NAME1] " - Episode Title"
  331.         [EP_NAME2] ".Episode Title"
  332.         If not specified the format is "[SHOW_NAME] - [EP1][EP_NAME1]"
  333.         For example:
  334.                 for "My Show S01E01 - Episode Title" (this is the default)
  335.                 --rename-format:[SHOW_NAME] - [EP1][EP_NAME1]
  336.                 for "My Show.S01E01.Episode Title"
  337.                 --rename-format:[SHOW_NAME].[EP1][EP_NAME2]
  338.                
  339. --use-dots-instead-of-spaces:[TRUE|FALSE]
  340.         Renames episodes to replace spaces with dots
  341.         If not specified, FALSE
  342.  
  343. --season-title:string
  344.         Season title
  345.         Note: if you want a space it needs to be included
  346.         (eg "Season " -> "Season 1",  "Series "->"Series 1", "Season."->"Season.1")
  347.         If not specified, "Season "
  348.  
  349. --season-double-digits:[TRUE|FALSE]
  350.         Season format padded to double digits (eg "Season 01" rather than "Season 1")
  351.         If not specified, FALSE
  352.  
  353. --match-type:[NORMAL|LIBERAL]
  354.         Match type.
  355.         LIBERAL assumes all files are episodes and tries to extract season and episode number any way possible.
  356.         If not specified, NORMAL
  357.  
  358. --sort-by:[MOVE|COPY|MOVE-AND-LEAVE-SYMLINK-BEHIND|LEAVE-AND-PLACE-SYMLINK]
  359.         Sort by moving or copying the file. If the file already exists because it was already copied it is silently skipped.
  360.         The MOVE-AND-LEAVE-SYMLINK-BEHIND option may be handy if you want to continue to seed after sorting, this leaves a symlink in place of the newly moved file.
  361.         PLACE-SYMLINK does not move the original file, but places a symlink in the sort-to directory (probably not what you want)
  362.         If not specified, MOVE
  363.  
  364. --treat-directories:[AS_FILES_TO_SORT|RECURSIVELY_SORT_CONTENTS|IGNORE]
  365.         How to treat directories.
  366.         AS_FILES_TO_SORT - sorts directories, moving entire directories that represents an episode, also detects and moves directories of entire seasons
  367.         RECURSIVELY_SORT_CONTENTS - doesn't move directories, just their contents, including subdirectories
  368.         IGNORE - ignores directories
  369.         If not specified, RECURSIVELY_SORT_CONTENTS
  370.        
  371. --require-show-directories-already-exist:[TRUE|FALSE]
  372.         Only sort into show directories that already exist
  373.         This may be helpful if you have multiple destination directories. Just set up all the other details in the conf file,
  374.         and specify the destination directory when invoking the script. Only episodes that match existing directories in the destination will be moved.
  375.         If this is false, then new directories are created for shows that dont have a directory.
  376.         If not specified, FALSE
  377.        
  378. --remove-symlinks:[TRUE|FALSE]
  379.         Deletes symlinks from the directory to sort while sorting.
  380.         This may be helpful if you want to remove all the symlinks you previously left behind using --sort-by:MOVE-AND-LEAVE-SYMLINK-BEHIND
  381.         You could schedule "perl sorttv.pl --remove-symlinks:TRUE" to remove these once a week/month
  382.         If this option is enabled and used at the same time as --sort-by:MOVE-AND-LEAVE-SYMLINK-BEHIND,
  383.          then only the previous links will be removed, and new ones may also be created
  384.         If not specified, FALSE
  385.  
  386. --show-name-substitute:NAME1-->NAME2
  387.         Substitutes files equal to NAME1 for NAME2
  388.         This argument can be repeated to add multiple rules for substitution
  389.  
  390. --force-windows-compatible-filenames:[TRUE|FALSE]
  391.         Forces MSWindows compatible file names, even when run on other platforms such as Linux
  392.         This may be helpful if you are writing to a Windows share from a Linux system
  393.         If not specified, FALSE
  394.  
  395. --lookup-language:[en|...]
  396.         Set language for thetvdb lookups, this effects episode titles etc
  397.         Valid values include: it, zh, es, hu, nl, pl, sl, da, de, el, he, sv, eng, fi, no, fr, ru, cs, en, ja, hr, tr, ko, pt
  398.         If not specified, en (English)
  399.  
  400. --fetch-images:[NEW_SHOWS|FALSE]
  401.         Download images for shows, seasons, and episodes from thetvdb
  402.         Downloaded images are copied into the sort-to (destination) directory.
  403.         NEW_SHOWS - When new shows, seasons, or episodes are created the associated images are downloaded
  404.         FALSE - No images are downloaded
  405.         if not specified, NEW_SHOWS
  406.  
  407. --images-format:POSTER
  408.         Sets the image format to use, poster or banner.
  409.         POSTER/BANNER
  410.         if not specified, POSTER
  411.  
  412.  
  413. EXAMPLES:
  414. Does a sort, as configured in sorttv.conf:
  415.         perl sorttv.pl
  416.  
  417. The directory-to-sort and directory-to-sort-to can be supplied directly:
  418. To sort a Downloads directory contents into a TV directory
  419.         perl sorttv.pl /home/me/Downloads /home/me/Videos/TV
  420. Alternatively:
  421.         perl sorttv.pl --directory-to-sort:/home/me/Downloads --directory-to-sort-into:/home/me/Videos/TV
  422.  
  423. To move non-episode files in a separate directory:
  424.         perl sorttv.pl --directory-to-sort:/home/me/Downloads --directory-to-sort-into:/home/me/Videos/TV --non-episode-dir:/home/me/Videos/Non-episodes
  425.  
  426. To integrate with xbmc (notification and automatic library update):
  427.         perl sorttv.pl --directory-to-sort:/home/me/Downloads --directory-to-sort-into:/home/me/Videos/TV --xbmc-webserver:localhost:8080
  428.  
  429. And so on...
  430.  
  431. FURTHER INFORMATION:
  432. Please goto the xbmc forum to discuss SortTV:
  433. http://forum.xbmc.org/showthread.php?t=75949
  434.  
  435. Get the latest version from here:
  436. http://sourceforge.net/projects/sorttv/files/
  437.  
  438. Cliffe's website:
  439. http://schreuders.org/
  440.  
  441. Please consider a \$5 paypal donation if you find this program helpful.
  442.  
  443. END
  444.         out("std", $heredoc);
  445.         exit;
  446. }
  447.  
  448. sub displayandupdateinfo {
  449.         my ($show, $xbmcwebserver) = @_;
  450.  
  451.         # update xbmc video library
  452.         get "http://$xbmcwebserver/xbmcCmds/xbmcHttp?command=ExecBuiltIn(updatelibrary(video))";
  453.  
  454.         # pop up a notification on xbmc
  455.         # xbmc.executebuiltin('XBMC.Notification(New content found, ' + filename + ', 2000)')
  456.         get "http://$xbmcwebserver/xbmcCmds/xbmcHttp?command=ExecBuiltIn(Notification(NEW EPISODE, $show, 7000))";
  457. }
  458.  
  459. # replaces ".", "_" and removes "the" and ","
  460. # removes numbers and spaces
  461. # removes the dir path
  462. sub fixtitle {
  463.         my ($title) = @_;
  464.         $title = substitute_name($title);
  465.         $title =~ s/,|\.the\.|\bthe\b//ig;
  466.         $title =~ s/\.and\.|\band\b//ig;
  467.         $title =~ s/&|\+//ig;
  468.         $title =~ s/(.*\/)(.*)/$2/;
  469.         $title = remdot($title);
  470.         $title =~ s/\d|\s|\(|\)//ig;
  471.         return $title;
  472. }
  473.  
  474. # substitutes show names as configured
  475. sub substitute_name {
  476.         my ($from) = @_;
  477.         foreach my $substitute (@showrenames) {
  478.                 if($substitute =~ /(.*)-->(.*)/) {
  479.                         if($from eq $1) {
  480.                                 return $2;
  481.                         }
  482.                 }
  483.         }
  484.         # if no matches, returns unchanged
  485.         return $from;
  486. }
  487.  
  488. # removes dots and underscores for creating dirs
  489. sub remdot {
  490.         my ($title) = @_;
  491.         $title =~ s/\./ /ig;
  492.         $title =~ s/_/ /ig;
  493.         $title =~ s/-//ig;
  494.         $title =~ s/'//ig;
  495.         # don't start or end on whitespace
  496.         $title =~ s/\s$//ig;
  497.         $title =~ s/^\s//ig;
  498.         return $title;
  499. }
  500.  
  501. # removes path
  502. sub filename {
  503.         my ($title) = @_;
  504.         $title =~ s/(.*\/)(.*)/$2/;
  505.         return $title;
  506. }
  507.  
  508. sub escape_myfilename {
  509.         my ($name) = @_;
  510.         if($^O =~ /MSWin/ || $windowsnames eq "TRUE") {
  511.                 $name =~ s/[\\\/:*?\"<>|]/-/g;
  512.         } else {
  513.                 $name =~ s/[\\\/\"<>|]/-/g;
  514.         }
  515.         return $name;
  516. }
  517.  
  518. sub display_info {
  519.         my ($second, $minute, $hour, $dayofmonth, $month, $yearoffset) = localtime();
  520.         my $year = 1900 + $yearoffset;
  521.         my $thetime = "$hour:$minute:$second, $dayofmonth-$month-$year";
  522.         out("std", "$thetime\n");
  523.         out("std", "Sorting $sortdir into $tvdir\n");
  524. }
  525.  
  526. sub move_episode {
  527.         my ($pureshowname, $showname, $series, $episode, $file) = @_;
  528.  
  529.         out("verbose", "INFO: trying to move $pureshowname season $series episode $episode\n");
  530.         SHOW: foreach my $show (bsd_glob($tvdir.'*')) {
  531.                 my $subshowname = fixtitle(escape_myfilename(substitute_name($showname)));
  532.                 if(fixtitle($show) =~ /^\Q$showname\E$/i || fixtitle(escape_myfilename(substitute_name(filename($show)))) =~ /^\Q$subshowname\E$/i) {
  533.                         out("verbose", "INFO: found a matching show:\n\t$show\n");
  534.                         my $s = $show.'/*';
  535.                         my @g=bsd_glob($show);
  536.                         foreach my $season (bsd_glob($show.'/*')) {
  537.                                 if(-d $season.'/' && $season =~ /(?:Season|Series|$seasontitle)?\s?0*(\d+)$/i && $1 == $series) {
  538.                                         out("verbose", "INFO: found a matching season:\n\t$season\n");
  539.                                         move_an_ep($file, $season, $show, $series, $episode);
  540.                                         if($xbmcwebserver) {
  541.                                                 $new = "$showname season $series episode $episode";
  542.                                                 displayandupdateinfo($new, $xbmcwebserver);
  543.                                                 $newshows .= "$new\n";
  544.                                         }
  545.                                         # next FILE;
  546.                                         return 0;
  547.                                 }
  548.                         }
  549.                         # didn't find a matching season, make DIR
  550.                         out("std", "INFO: making season directory: $show/$seasontitle$series\n");
  551.                         my $newpath = "$show/$seasontitle$series";
  552.                         if(mkdir($newpath, 0777)) {
  553.                                 fetchseasonimages(substitute_name(remdot($pureshowname)), $show, $series, $newpath) if $fetchimages ne "FALSE";
  554.                                 redo SHOW; # try again now that the dir exists
  555.                         } else {
  556.                                 out("warn", "WARN: Could not create season dir: $!\n");
  557.                                 # next FILE;
  558.                                 return 0;
  559.                         }
  560.                 }
  561.         }
  562.         if($needshowexist ne "TRUE") {
  563.                 # if we are here then we couldn't find a matching show, make DIR
  564.                 my $newshowdir = $tvdir . escape_myfilename(substitute_name(remdot($pureshowname)));
  565.                 out("std", "INFO: making show directory: $newshowdir\n");
  566.                 if(mkdir($newshowdir, 0777)) {
  567.                         fetchshowimages(substitute_name(remdot($pureshowname)), $newshowdir) if $fetchimages ne "FALSE";
  568.                         # try again now that the dir exists
  569.                         # redo FILE;
  570.                         return $REDO_FILE;
  571.                 } else {
  572.                         out("warn", "WARN: Could not create show dir: $newshowdir:$!\n");
  573.                         # next FILE;
  574.                         return 0;
  575.                 }
  576.         } else {
  577.                 out("verbose", "SKIP: Show directory does not exist: " . $tvdir . escape_myfilename(substitute_name(remdot($pureshowname)))."\n");
  578.                 # next FILE;
  579.                 return 0;
  580.         }
  581. }
  582.  
  583. sub fetchshowimages {
  584.         my ($fetchname, $newshowdir) = @_;
  585.         out("std", "DOWNLOAD: downloading images for $fetchname\n");
  586.         my $banner = $tvdb->getSeriesBanner($fetchname);
  587.         my $fanart = $tvdb->getSeriesFanart($fetchname);
  588.         my $poster = $tvdb->getSeriesPoster($fetchname);
  589.         copy ("$scriptpath/.cache/$fanart", "$newshowdir/fanart.jpg") if $fanart && -e "$scriptpath/.cache/$fanart";
  590.         copy ("$scriptpath/.cache/$banner", "$newshowdir/banner.jpg") if $banner && -e "$scriptpath/.cache/$banner";
  591.         copy ("$scriptpath/.cache/$poster", "$newshowdir/poster.jpg") if $poster && -e "$scriptpath/.cache/$poster";
  592.         my $ok = eval{symlink "$newshowdir/poster.jpg", "$newshowdir/folder.jpg" if $poster && -e "$scriptpath/.cache/$poster" && $imagesformat eq "POSTER";};
  593.         if(!defined $ok) {copy "$newshowdir/poster.jpg", "$newshowdir/folder.jpg" if $poster && -e "$scriptpath/.cache/$poster" && $imagesformat eq "POSTER";};
  594.         $ok = eval{symlink "$newshowdir/banner.jpg", "$newshowdir/folder.jpg" if $banner && -e "$scriptpath/.cache/$banner" && $imagesformat eq "BANNER";};
  595.         if(!defined $ok) {copy "$newshowdir/banner.jpg", "$newshowdir/folder.jpg" if $banner && -e "$scriptpath/.cache/$banner" && $imagesformat eq "BANNER";};
  596. }
  597.  
  598. sub fetchseasonimages {
  599.         my ($fetchname, $newshowdir, $season, $seasondir) = @_;
  600.         out("std", "DOWNLOAD: downloading season image for $fetchname\n");
  601.         my $banner = $tvdb->getSeasonBanner($fetchname, $season);
  602.         my $bannerwide = $tvdb->getSeasonBannerWide($fetchname, $season);
  603.         my $snum = sprintf("%02d", $season);
  604.         copy ("$scriptpath/.cache/$banner", "$newshowdir/season${snum}.jpg") if $banner && -e "$scriptpath/.cache/$banner" && $imagesformat eq "POSTER";
  605.         copy ("$scriptpath/.cache/$bannerwide", "$newshowdir/season${snum}.jpg") if $bannerwide && -e "$scriptpath/.cache/$bannerwide" && $imagesformat eq "BANNER";
  606.         my $ok = eval{symlink "$newshowdir/season$snum.jpg", "$seasondir/folder.jpg" if -e "$newshowdir/season$snum.jpg";};
  607.         if(!defined $ok) {copy "$newshowdir/season$snum.jpg", "$seasondir/folder.jpg" if -e "$newshowdir/season$snum.jpg";};
  608. }
  609.  
  610. sub fetchepisodeimage {
  611.         my ($fetchname, $newshowdir, $season, $seasondir, $episode, $newfilename) = @_;
  612.         # if the episode was moved (or already existed)
  613.         if(-e "$seasondir/$newfilename") {
  614.                 my $epimage = $tvdb->getEpisodeBanner($fetchname, $season, $episode);
  615.                 my $newimagepath = "$seasondir/$newfilename";
  616.                 $newimagepath =~ s/(.*)(\..*)/$1.tbn/;
  617.                 copy ("$scriptpath/.cache/$epimage", $newimagepath) if $epimage && -e "$scriptpath/.cache/$epimage";
  618.         }
  619. }
  620.  
  621. sub move_an_ep {
  622.         my($file, $season, $show, $series, $episode) = @_;
  623.         my $newfilename = filename($file);
  624.         my $newpath;
  625.        
  626.         if($rename eq "TRUE") {
  627.                 my $ext = my $eptitle = "";
  628.                 unless(-d $file) {
  629.                         $ext = $file;
  630.                         $ext =~ s/(.*\.)(.*)/\.$2/;
  631.                 }
  632.                 if($renameformat =~ /\[EP_NAME(\d)]/i) {
  633.                         out("verbose", "INFO: Fetching episode title for ", substitute_name(remdot($pureshowname)), " Season $series Episode $episode.\n");
  634.                         my $name = $tvdb->getEpisodeName(substitute_name(remdot($pureshowname)), $series, $episode);
  635.                         if($name) {
  636.                                 $eptitle = " - $name" if $1 == 1;
  637.                                 $eptitle = ".$name" if $1 == 2;
  638.                         } else {
  639.                                 out("warn", "WARN: Could not get episode title for ", substitute_name(remdot($pureshowname)), " Season $series Episode $episode.\n");
  640.                         }
  641.                 }
  642.                 my $sname = substitute_name(remdot($pureshowname));
  643.                 my $ep1 = sprintf("S%02dE%02d", $series, $episode);
  644.                 my $ep2 = sprintf("%dx%d", $series, $episode);
  645.                 my $ep3 = sprintf("%dx%02d", $series, $episode);
  646.                 # create the new file name
  647.                 $newfilename = $renameformat;
  648.                 $newfilename =~ s/\[SHOW_NAME]/$sname/ig;
  649.                 $newfilename =~ s/\[EP1]/$ep1/ig;
  650.                 $newfilename =~ s/\[EP2]/$ep2/ig;
  651.                 $newfilename =~ s/\[EP3]/$ep3/ig;
  652.                 $newfilename =~ s/\[EP_NAME\d]/$eptitle/ig;
  653.                 $newfilename .= $ext;
  654.                 # make sure it is filesystem friendly:
  655.                 $newfilename = escape_myfilename($newfilename, "-");
  656.         }
  657.         if($usedots eq "TRUE") {
  658.                 $newfilename =~ s/\s/./ig;
  659.         }
  660.  
  661.         $newpath = $season . '/' . $newfilename;
  662.         if(-e $newpath) {
  663.                 if(filename($file) =~ /repack|proper/i) {
  664.                         out("warn", "OVERWRITE: Repack/proper version.\n");
  665.                 } else {
  666.                         out("warn", "SKIP: File $newpath already exists, skipping.\n") unless($sortby eq "COPY" || $sortby eq "PLACE-SYMLINK");
  667.                         return;
  668.                 }
  669.         }
  670.         out("std", "$sortby: sorting $file to ", $newpath, "\n");
  671.         if($sortby eq "MOVE" || $sortby eq "MOVE-AND-LEAVE-SYMLINK-BEHIND") {
  672.                 if(-d $file) {
  673.                         dirmove($file, $newpath) or out("warn", "File $show cannot be moved to $season. : $!");
  674.                 } else {
  675.                         move($file, $newpath) or out("warn", "File $show cannot be moved to $season. : $!");
  676.                 }
  677.         } elsif($sortby eq "COPY") {
  678.                 if(-d $file) {
  679.                         dircopy($file, $newpath) or out("warn", "File $show cannot be copied to $season. : $!");
  680.                 } else {
  681.                         copy($file, $newpath) or out("warn", "File $show cannot be copied to $season. : $!");
  682.                 }
  683.         } elsif($sortby eq "PLACE-SYMLINK") {
  684.                 symlink($file, $newpath) or out("warn", "File $file cannot be symlinked to $newpath. : $!");
  685.         }
  686.         # have moved now link
  687.         if($sortby eq "MOVE-AND-LEAVE-SYMLINK-BEHIND") {
  688.                 symlink($newpath, $file) or out("warn", "File $newpath cannot be symlinked to $file. : $!");
  689.         }
  690.        
  691.         fetchepisodeimage(substitute_name(remdot($pureshowname)), $show, $series, $season, $episode, $newfilename) if $fetchimages ne "FALSE";
  692.        
  693. }
  694.  
  695. sub move_a_season {
  696.         my($file, $show, $series) = @_;
  697.         my $newpath = $show."/".escape_myfilename("$seasontitle$series", "-");
  698.         if(-e $newpath) {
  699.                 out("warn", "SKIP: File $newpath already exists, skipping.\n") unless($sortby eq "COPY" || $sortby eq "PLACE-SYMLINK");
  700.                 return;
  701.         }
  702.         print "$sortby SEASON: $file to $newpath\n";   
  703.         out("verbose", "$sortby: sorting directory to: $newpath\n");
  704.         if($sortby eq "MOVE" || $sortby eq "MOVE-AND-LEAVE-SYMLINK-BEHIND") {
  705.                 dirmove($file, "$newpath") or out("warn", "$show cannot be moved to $show/$seasontitle$series: $!");
  706.         } elsif($sortby eq "COPY") {
  707.                 dircopy($file, "$newpath") or out("warn", "$show cannot be copied to $show/$seasontitle$series: $!");
  708.         } elsif($sortby eq "PLACE-SYMLINK") {
  709.                 symlink($file, $newpath) or out("warn", "File $file cannot be symlinked to $newpath. : $!");
  710.         }
  711.         if($sortby eq "MOVE-AND-LEAVE-SYMLINK-BEHIND") {
  712.                 symlink($newpath, $file) or out("warn", "File $newpath cannot be symlinked to $file. : $!");
  713.         }
  714. }
  715.  
  716. # move a new Season x directory
  717. sub move_series {
  718.         my ($pureshowname, $showname, $series, $file) = @_;
  719.  
  720.         out("verbose", "INFO: trying to move $pureshowname season $series directory\n");
  721.         SHOW: foreach my $show (bsd_glob($tvdir.'*')) {
  722.                 if(fixtitle($show) =~ /^\Q$showname\E$/i) {
  723.                         out("verbose", "INFO: found a matching show:\n\t$show\n");
  724.                         my $s = $show.'/*';
  725.                         my @g=bsd_glob($show);
  726.                         foreach my $season (bsd_glob($show.'/*')) {
  727.                                 if(-d $season.'/' && $season =~ /(?:Season|Series|$seasontitle)?\s?0*(\d)$/i && $1 == $series) {
  728.                                         out("warn", "SKIP: Cannot move season directory: found a matching season already existing:\n\t$season\n");
  729.                                         return 0;
  730.                                 }
  731.                         }
  732.                         # didn't find a matching season, move DIR
  733.                         move_a_season($file, $show, $series);
  734.                         if($xbmcwebserver) {
  735.                                 $new = "$showname Season $series directory";
  736.                                 displayandupdateinfo($new, $xbmcwebserver);
  737.                                 $newshows .= "$new\n";
  738.                         }
  739.                         return 0;
  740.                 }
  741.         }
  742.         if($needshowexist ne "TRUE") {
  743.                 # if we are here then we couldn't find a matching show, make DIR
  744.                 my $newshowdir = $tvdir . escape_myfilename(substitute_name(remdot($pureshowname)));
  745.                 out("std", "INFO: making show directory: $newshowdir\n");
  746.                 if(mkdir($newshowdir, 0777)) {
  747.                         fetchshowimages(substitute_name(remdot($pureshowname)), $newshowdir) if $fetchimages ne "FALSE";
  748.                         # try again now that the dir exists
  749.                         # redo FILE;
  750.                         return $REDO_FILE;
  751.                 } else {
  752.                         out("warn", "WARN: Could not create show dir: $newshowdir:$!\n");
  753.                         # next FILE;
  754.                         return 0;
  755.                 }
  756.         } else {
  757.                 out("verbose", "SKIP: Show directory does not exist: " . $tvdir . escape_myfilename(substitute_name(remdot($pureshowname)))."\n");
  758.                 # next FILE;
  759.                 return 0;
  760.         }
  761. }
  762.  
  763. sub out {
  764.         my ($type, @msg) = @_;
  765.        
  766.         if($type eq "verbose") {
  767.                 return if $verbose ne "TRUE";
  768.                 print @msg;
  769.                 print $log @msg if(defined $log);
  770.         }elsif($type eq "std") {
  771.                 print @msg;
  772.                 print $log @msg if(defined $log);
  773.         } elsif($type eq "warn") {
  774.                 warn @msg;
  775.                 print $log @msg if(defined $log);
  776.         }
  777. }