The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

File::TVShow::Organize - Perl module to move TVShow Files into their matching Show Folder on a media server.

VERSION

VERSION 0.35

SYNOPSIS

  use File::TVShow::Organize;

  our $exceptionList = "S.W.A.T.2017:S.W.A.T 2017|Other:other";

  my $obj = File::TVShow::Organize->new();

  $obj->new_show_folder("/tmp/");
  $obj->show_folder("/absolute/path/to/TV Shows");

  if((!defined $obj->new_show_folder()) || (!defined $obj->show_folder())) {
    print "Verify your paths. Something in wrong\n";
    exit;
  }

  # Create a hash for matching file name to Folders
  $obj->create_show_hash();

  # Delete files are processing.
  $obj->delete(1);

  # Don't create sub Season folders under the root show name folder.
  # Instead just dump them all into the root folder
  $obj->season_folder(0);

  # Batch process a folder containing TVShow files
  $obj->process_new_shows();

  # Report any file names which could not be handled automatically.
  $obj->were_there_errors();

  #end of program

DESCRIPTION

This module moves TV show files from the folder where they currently exist into the correct folder based on show name and season.

    Folder structure: /base/folder/Castle -> Season1 -> Castle.S01E01.avi
                                             Season2 -> Castle.S02E01.avi
                                             Specials -> Castle.S00E01.avi

This season folder behaviour can be disabled by calling season_folder(0). In this case all files are simply placed under Castle without sorting into season folders.

Source files are renamed or deleted upon successful relocation. This depends on the state of delete(). The default is to rename the files and not to delete. See delete() for more details.

Possible uses might include moving the files from an original rip directory and moving them into the correct folder structure for media servers such as Plex or Kodi. Another use might be to sort shows that are already in a single folder and to move them to a season by season or Special folder struture for better folder management.

This module does not examine file encodings and only parses the initial file naming. "name.SXXEXX.*" anything after SXXEXX is ignored with the exception that files ending in ".done" are also ignored by the module. These files will have already been successfully processed in previous executions of code using this module.

Works on Mac OS and *nix systems.

Methods

new

  Arguments: None or { Exeptions => 'MatchCase:DesiredValue'}

  $obj = File::TVShow::Organize->new();

  $obj = File::TVShow::Organize->new({ Exceptions =>
    'MatchCase:DesiredValue' })

  This subroutine creates a new object of type File::TVShow::Organize

  If Exceptions is passed to the method we load this data into a hash
  for later use to handle naming complications.

  E.G file: S.W.A.T.2017.S01E01.avi is not handled correctly by File::TVShow::Info
  so we need to know to handle this differently. Exceptions is an optional
  parameter and can be left out when calling new().
  Currently Exceptions is a scalar string.
  Its format is "MatchCase:DesiredValue|MatchCase:DesiredValue"

countries

  Arguments: String: Note the format below is used as part of a regex
              check in the module.
             As such () should always be included at the start and end of the
              string.

  $obj->countries("(US|UK|AU)");
  $obj->countries();

  This subroutine sets the countries internal value and returns it.

  Default value: (UK|US)


  This allows the system to match against programs names such as
  Agent X US / Agent X (US) / Agent X and reference the same single folder

show_folder

  Arguments: None or String

  Set the path return undef is the path is invalid
  $obj->show_folder("/path/to/folder");

  Return the path to the folder
  $obj->show_folder();

  Always confirm this does not return undef before using.
  undef will be returned if the path is invalid.

  Also a valid "path/to/folder" will always return with the "/" having been
  appended. "path/to/folder/"

  This is where the TV Show Folder resides on the file system.

  If the path is invalid this would leave the internal value as being undef.

new_show_folder

  Arguments: None or String

  Set the path return undef is the path is invalid
  $obj->new_show_folder("/path/to/folder");

  Return the path to the folder
  $obj->new_show_folder();

  Always confirm this does not return undef before using.
  undef will be returned if the path is invalid.

  Also a valid "path/to/folder" will always return with the "/" having been
  appened. "path/to/folder/"

  This is where new files to be add to the TV Show store reside on the
  file system.

create_show_hash

  Arguments: None

  $obj->create_show_hash;

  This function creates a hash of show names with the correct path to store
  data based on the directories that are found in showFolder.

  Examples:
        Life on Mars (US) creates 3 keys which point to the same folder
                                        key: life on mars (us) => folder: Life on Mars (US)
                                        key: life on mars us   => folder: Life on Mars (US)
                                        key: life on mars      => folder: Life on Mars (US)

        However if there already exists a folder: "Life on Mars" and a folder "Life on Mars (US)"
        the following hash key:folder pairs will be created. Note that the folderis differ
                                        key: life on mars      => folder: Life on Mars
                                        key: life on mars (us) => folder: Life on Mars (US)
                                        key: life on mars us   => folder: Life on Mars (US)

  As such file naming relating to country of origin is important if you are
  moving versions of the same show based on country.

clear_show_hash

  Arguments: None

  This function clears the ShowHash data so that create_show_hash can be run
  again before or after a folder change which might occur if show_folder() were
  to be set to a new folder.

show_path

  Arguments: String

  $obj->show_path("Life on Mars US") returns the name of the folder "Life on Mars (US)"
  or undef if "Life on Mars US" does not exist as a key.

  No key will be found if there was no folder found when $obj->create_show_hash was called.

  Example:

  my $file = File::TVShow::Info->new("Life.on.Mars.(US).S01E01.avi");

  # $file->{name} now contains "Life on Mars (US)"
  # $file->{season} now contains "01"

  my $dest = "/path/to/basefolder/" . $obj->show_path($file->{name});
  result => $dest now cotains "/path/to/basefolder/Life on Mars (US)/"

  $dest = $obj->create_season_folder($dest,$file->{season});
  result => $dest now contains "/path/to/basefolder/Life on Mars (US)/Season1/"

process_new_shows

  Arguments: None

  $obj->process_new_shows();


  This function requires that $obj->show_folder("/absolute/path") and
  $obj->new_show_folder("/absoute/path") have already been called as their paths
  will be used in this function call.

  This is the main process for batch processing of a folder of show files.
  Hidden files, files ending in ".done" as well as directories are excluded
  from being processed.

  This function will process a single folder and no deeper if recursion is not
  enabled.
  If recursion is enabled it will process any sub folders that it finds from
  the initial folder.

move_show

  Arguments: String, String, String
  The first arguement is the folder where the file is to be moved into
  The Second argument is the source folder where the new show file currently
  exists.
  The third argument is the file which is to be moved.

  $obj->move_show("/absolute/path/to/destintaion/folder/",
  "absolute/path/to/source/folder/", "file");

  This function does the heavy lifting of actually moving the show file into
  the determined folder.
  This function is called by process_new_shows which does the work to
  determine the paths to folder and file.

  This function could be called on its own after you have verified "folder"
  and "file"

  It uses a system() call to rsync which always checks that the copy was
  successful.

  This function then checks the state of $obj->delete to determine if the
  processed file should be renamed "file.done" or should be removed using
  unlink(). Note delete(1) should be called before process_new_shows() if you
  wish to delete the processed file. By default the file is only renamed.

delete

  Arguments: None,0,1

  $obj->delete return the current true or false state (1 or 0)
  $obj->delete(0) set delete to false
  $obj->delete(1) set delete to true

  Input should be 0 or 1. 0 being do not delete. 1 being delete.

  Set if we should delete source file after successfully moving it to the tv
  store or if we should rename it to $file.done


  The default is false and the file is simply renamed.

  Return undef if the varible passed to the function is not valid. Do not change
  the current state of delete.

recursion

  Arguments None,0,1

  $obj->recursion returns the current true or false state (1 or 0)
  $obj->recursion(0) set recursion to false
  $obj->recursion(1) set recursion to true

  This controls the behaviour of process_new_shows();

season_folder

  Arguments: None,0,1

  $obj->season_folder return the current true or false state (1 or 0)
  $obj->season_folder(0) or season_folder(1) sets and returns the new value.
  $obj->season_folder() returns undef if the input is invalid and the internal
  state is unchanged.

  if(!defined $obj->season_folder("x")) {
    print "You passed and invalid value\n";
  }

  The default is true.

were_there_errors

  Arguments: None

  $obj->were_there_errors;


  This should be called at the end of the program to report if any file names
  could not be handled correctly resulting in files not being processed. These
  missed files can then be manually moved or their show name can be added to
  the exceptionList variable. Remember to match the NAME preceeding SXX and to
  give the corrected name

  EG S.W.A.T.2017.SXX should get an entry such as:
  exceptionList = "S.W.A.T.2017:S.W.A.T 2017";

create_season_folder

  Arguments: String, Number

  The first argument is the current folder that the file should be moved to
  The second argument is the season number.

  $obj->create_season_folder("/absolute/path/to/show/folder/",$seasonNumber)

  This creates a folder within "/absolute/path/to/show/folder/" by calling
  make_path() returns the newly created path
  "absolute/path/to/show/folder/SeasonX/" or
  "/absolute/path/to/show/folder/Specials/"

  note: "/absolute/path/to/show/folder/" is not verified to be valid and is
  assumed to have been checked before being passed

  Based on SXX
  S01 creates Season1
  S00 creates Specials

verbose

  Arguments: None,0,1

  $obj->verbose();
  $obj->verbose(0);
  $obj->verbose(1);

  Return undef if passed an invalid imput and write to STDERR. Current value
  of verbose is not changed. Return 0 if verbose mode is off. Return 1 if
  verbose mode is on.

  This state is checked by create_season_folder(), move_show()
  This allows to system to give some user feedback on what is being done if you
  want to watch the module working.

Examples

Do not create season folders

  #!/bin/perl

  use strict;
  use warnings;

  use File::TVShow::Organize;

  my $obj = File::TVShow::Organize->new();

  $obj->new_show_folder("/tmp/");
  $obj->show_folder("/absolute/path/to/TV Shows");

  if((!defined $obj->new_show_folder()) || (!defined $obj->show_folder())) {
    print "Verify your paths. Something in wrong\n";
    exit;
  }

  # Create a hash for matching file name to Folders
  $obj->create_show_hash();

  # Don't create sub Season folders under the root show name folder.
  # Instead just dump them all into the root folder
  $obj->season_folder(0);

  # Batch process a folder containing TVShow files
  $obj->process_new_shows();

  # Report any file names which could not be handled automatically.
  $obj->were_there_errors();

Process two different source folders.

  #!/bin/perl

  use strict;
  use warnings;

  use File::TVShow::Organize;
  my $obj = File::TVShow::Organize->new();

  $obj->new_show_folder("/tmp/");
  $obj->show_folder("/absolute/path/to/TV Shows");

  if((!defined $obj->new_show_folder()) || (!defined $obj->show_folder())) {
    print "Verify your paths. Something in wrong\n";
    exit;
  }

  # Create a hash for matching file name to Folders
  $obj->create_show_hash();

  # Batch process first folder containing TVShow files
  $obj->new_show_folder("/tmp/");
  $obj->process_new_shows();

  # Batch process second folder containing TVShow files.
  $obj->new_show_folder("/tmp2/");
  $obj->process_new_shows();

  # Report any file names which could not be handled automatically.
  $obj->were_there_errors();

INCOMPATIBILITIES

This has not been tested on a windows system and I expect it will not actually work.

I have not tested anycases where file names might be "showname.(US).(2003).S0XE0X.avi" as I have no such cases myself.

SEE ALSO

File::Path
File::Copy
File::TVShow::Info

AUTHOR

Adam Spann, <bans@cpan.org>

COPYRIGHT AND LICENSE

Copyright (C) 2018 by Adam Spann

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.12.4 or, at your option, any later version of Perl 5 you may have available.

DISCLAIMER OF WARRANTIES

THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.