Updated: 2013-03-04 – Fixed bug that prevented names containing 720p/1080p from being parsed properly.
This is a perl script to normalize video file names for TV series, it will run under windows or linux.
Note: I have tested this and do use it however I may have missed something or there may be a bug I have not encountered, I would advise backing up the files to be renamed before running the script and then verify that it has worked as expected. If so delete the backups if not please leave a comment detailing what has happened and I’ll try to fix it asap.
Disclaimer: I am not responsible if this causes WWIII or nukes your computer, use at your own risk.
Supported naming schemes
Name
- Single
- Double.1
- Double 2
- Double-3
- Double_4
Season-Episode
- 101
- 0102
- 1×03
- 1×4
- 01×05
- S1E06-e7
- s1e8-9
- S1e10-11
- S01.E12
- S01 E13
- S01E14-E15
Supported file extensions
- .avi
- .mkv
- .mpg
- .mpeg
- .mp4
Example “Double.1.1×1.XXX.XXX.XXX.mkv” would get normalized to “Double 1 S01E01.mkv”
and
Example “Double.1.1×1.720p.XXX.XXX.XXX.mkv” would get normalized to “Double 1 S01E01.mkv”
Requirements
All users need perl, windows users can get it (free) from http://www.activestate.com/Products/activeperl/
Usage
perl rname.pl <dirToParse> – Will parse the directory given as an argument
perl rname.pl – Will ask the user to specify the directory to parse on the commandline
Files
- [Download not found]
- [Download not found]
- [Download not found]
Changelog
Code
rname.pl#!/bin/perl -w <pre>#!/usr/bin/perl -w use warnings; use strict; my($verison) = "0.20"; print"\nrname.pl Version $verison...\n"; print"Copyright 2007 Ryan McLean\n\n"; use File::Copy; my($input) = ""; if ($ARGV[0]) { $input = $ARGV[0]; } else { print "Enter directory to parse: "; $input = <STDIN>; # Get Dir to parse chomp($input); # remove /r/n from input } my($dir) = "$input"; # Create log file open (LOG, ">>$dir/rname.log"); printf LOG "\nParsing Files...\n\n"; print "\nParsing Files...\n\n"; opendir(THISDIR, "$dir") or die ("Error: $!"); my(@allfiles); @allfiles = readdir THISDIR; closedir THISDIR; foreach my $fname (@allfiles) { # List contents of directory if (! -d "$dir/$fname" && ! -l "$dir/$fname") { # If not a dir & not a symlink to dir my($tmpName) = ""; my($tmpSE) = ""; my($addEp) = ""; my($ext) = ""; my(@chararr); my($p1) = ""; my($p2) = ""; if ( ($fname =~ m/^.*\.(avi$|ogg$|mpg$|mpeg$|mkv$|mp4$)/) && (! -d $fname) ) { # Only get files with .avi, .ogg, .mpg, .mpeg, .mkv, .mp4 extension $fname =~ m/^(.+?)[-\s_\.](S[0-9]+[\.\s]?E[0-9]+|[0-9]+x[0-9]+|[0-9]{3,4})(-[0-9]+|-E[0-9]+)?(\.[0-9]{3,4}[i|p])?.*\.(avi$|ogg$|mpg$|mpeg$|mkv$|mp4$)/i; # Match S01 E01 & S01E01 & 1x01 & 101 & 1001 $tmpName = $1; $tmpSE = $2; $addEp = $3; $ext = $5; # Subsitute seperators with a single space, for files with multipart names $tmpName =~ s/[-\s_\.]/ /i; if (!$tmpSE) { $tmpSE = ""; } # if $tmpSE is not set set it if (!$tmpName) { $tmpName = ""; } # if $tmpName is not set set it if (!$addEp) { $addEp = "" } # If there are no additional episodes then set it to null. if ($tmpSE =~ m/^[0-9]{3,3}$/i) { @chararr = split(//, $tmpSE); $tmpSE = "\L$chararr[0]x$chararr[1]$chararr[2]"; } elsif ($tmpSE =~ m/^[0-9]{4,4}$/i) { @chararr = split(//, $tmpSE); $tmpSE = "\L$chararr[0]$chararr[1]x$chararr[2]$chararr[3]"; } elsif ($tmpSE =~ m/^(.+)([xe][0-9]$)/i) { # Catch S01E1 or 01x1 types and fix them to be E01 or x01 $tmpSE =~ m/^(.+)([xe][0-9]$)/i; $p1 = $1; $p2 = $2; @chararr = split(//, $p2); $tmpSE = "\L${p1}${chararr[0]}0${chararr[1]}"; } elsif ($tmpSE =~ m/^(S[0-9]+)[\.\s](E[0-9]+)$/i) { $tmpSE = "\L${1}${2}"; } if ($addEp =~ m/^-E?([0-9])$/i) { $addEp = "-0${1}"; } elsif ($addEp =~ m/^-E([0-9]+)$/i) { $addEp = "-${1}"; } my($name) = "\u$tmpName \L$tmpSE$addEp.$ext"; if ($name =~ m/^[a-z0-9]+.*\.(avi$|ogg$|mpg$|mpeg$|mkv$|mp4$)/i) { move("$dir/$fname", "$dir/$name"); # rename the files print "Moved \"$dir/$fname\" -> \"$dir/$name\"\n"; printf LOG "Moved \"$dir/$fname\" -> \"$dir/$name\"\n"; } } } } printf LOG "Filenames shortened...\n"; print "Filenames shortened...\n"; printf LOG "Using same naming convention for all files..\n"; print "Using same naming convention for all files..\n"; opendir(THISDIR, "$dir") or die ("Error: $!"); @allfiles = readdir THISDIR; closedir THISDIR; foreach my $fname (@allfiles) { # List contents of directory if (! -d "$dir/$fname" && ! -l "$dir/$fname") { # If not a dir & not a symlink to dir my($tmpName) = ""; my($tmpSE) = ""; my($ext) = ""; my(@chararr); my($SE) = ""; $fname =~ s!^.*(/.*)$!$1!; # remove file path from file name $fname =~ s!^/(.*)$!$1!; # remove leading / from file name $fname =~ m/^(.+)\s(S[0-9]+E[0-9]+|[0-9]+x[0-9]+)\.(avi$|ogg$|mpg$|mpeg$|mkv$|mp4$)/i; $tmpName = $1; $tmpSE = $2; $ext = $3; if (!$tmpSE) { $tmpSE = ""; } # if $tmpSE is not set set it if (!$tmpName) { $tmpName = ""; } # if $tmpName is not set set it # Determine naming convention and rename if ($tmpSE =~ m/^[0-9]+x[0-9]+$/i) { @chararr = split(//, $tmpSE); if ($chararr[1] eq "x") { $SE = "\LS0$chararr[0]E$chararr[2]$chararr[3]"; } elsif ($chararr[2] eq "x") { $SE = "\LS$chararr[0]$chararr[1]E$chararr[3]$chararr[4]"; } else { die; } } elsif ($tmpSE =~ m/^S[0-9]E[0-9]+$/i) { @chararr = split(//, $tmpSE); $SE = "\LS0$chararr[1]E$chararr[3]$chararr[4]"; } else { $SE = " "; } # Rename the files if ($SE =~ m/^[^\s]/i) { my($nname) = "$tmpName $SE.$ext"; move("$dir/$fname", "$dir/$nname"); # rename the files printf LOG "Moved \"$dir/$fname\" -> \"$dir/$nname\"\n"; print "Moved \"$dir/$fname\" -> \"$dir/$nname\"\n"; } } } printf LOG "\nFinished..\n"; close LOG; print "\nFinished..\n";rnameTest.pl
# Test Suite for rname.pl # version 1.4 $dir = "test 1"; $dir2 = "test 3"; $subdir = "test 2"; if ( ($ARGV[0]) && ($ARGV[0] = "-d" ) ) { opendir(THISDIR, "$dir") or die ("Error: $!"); @files = readdir THISDIR; closedir THISDIR; open (LOG, ">>$dir/rnameTest.log"); foreach my $fname (@files) { # List contents of directory if (! -d "$dir/$fname" && ! -l "$dir/$fname") { # If not a dir & not a symlink to dir if ($fname =~ m/^[a-z]+(\s[0-9])?\ss[0-9]+e[0-9]+(-\d+)?\.(avi$|ogg$|mpg$|mpeg$|mkv$)/i) { printf LOG "$fname\n"; unlink("$dir/$fname"); } } } close LOG; } else { # File Name arrys @name = ("Single", "Double.1", "double 2", "Double-3", "Double_4"); @SE = ("101", "0102", "1x03", "1x4", "01x05", "S1E06-e7", "s1e8-9", "S01e10-11", "S01.E12", "S01 E13", "S01E14-E15"); @ext = (".avi", ".ogg", ".mpg", ".mpeg", ".mkv"); $crap = "zzzz.ssss"; # Make Directory mkdir "$dir"; mkdir "$dir2"; mkdir "$dir/$subdir"; # Create files foreach $fname (@name) { foreach $fse (@SE) { foreach $fext (@ext) { $nfname = "${fname}.${fse}-${crap}${fext}"; open F, ">$dir/$nfname"; close F; } } } foreach $fname (@name) { foreach $fse (@SE) { foreach $fext (@ext) { $nfname = "${fname}.${fse}-${crap}${fext}"; open F, ">$dir2/$nfname"; close F; } } } # File noise (to ensure just the $ext files are processed) open F, ">$dir/test.log"; close F; open F, ">$dir/test.av"; close F; open F, ">$dir/test.htm"; close F; open F, ">$dir2/test.log"; close F; open F, ">$dir2/test.av"; close F; open F, ">$dir2/test.htm"; close F; }test2.bat
@echo off rem C:\rname\test.pl "dir1" "dir2" "dir3" "dir4" "dir5" "dir6" "dir7" perl C:\rname\rnameTest.pl perl C:\rname\rname.pl "test 1" "test 3" perl C:\rname\rnameTest.pl "-d" pause