The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

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

VERSION

VERSION 0.360.1

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.