Alt.Binz forum
New Alt.Binz versions => Requests => Topic started by: somestranger26 on November 28, 2010, 07:44:52 am
-
This is probably not the easiest feature to implement, but I think it would be great if AltBinz could sort and rename TV shows by parsing the filename for needed information.
I have written a script to work in conjunction with the RSS feature, but I have to manually specify the unrar folders and then tell my script what show and season it was. I consider this fairly hackish (and it doesn't match some files that SABnzbd does) and would love an easier way to organize my files.
In SABnzbd you can choose to have it do this with zero user intervention; it uses regex to parse the filename for necessary information and then sorts/renames based on a format string that users can edit. Since SABnzbd is open source, I figure it would not be too difficult to adapt their tvsort algorithm to AltBinz.
For example, the default format string in SABnzbd "%sn/Season %s/%sn - S%0sE%0e.%ext" renames a file to be like "The Sausage S01 E01.mkv" and then puts it into "<unrar directory>/Season 1/The Sausage/"
-
File renaming is evil. However, there are quite a few tools already for doing this that also do lookups for metadata, e.g.:
http://tvrenamer.com/
To be incorporated into Alt.Binz, first I think we'd really need something like SABnzbd Categories, about which there are a few open requests. Or of course you can launch any script you want from Alt.Binz to do post-processing, including python (for the SABnzbd code). If you don't want to install a scripting language and you're on Windows, try Powershell (installed with Windows 7 by default) - it allows regex matching & file renaming quite trivially.
Info: http://powershell.com/cs/blogs/ebook/archive/2009/03/30/chapter-13-text-and-regular-expressions.aspx#forming-groups
Example:
$file = "The.Sausage.S01E01.720p.HDTV.x264-FOO"
$pattern = "^(?<title>.*?)\.S(?<season>\d+)\.?E(?<episode>\d+)\.(?<tail>.*)$"
if ($file -match $pattern)
{
write ("Title: " + ($matches.title -replace "[\._]"," "))
write ("Season: " + $matches.season)
write ("Episode: " + $matches.episode)
}
else
{
write "No matches"
}
-
I ended up writing a much improved version of my python script that does what SABnzbd's sorter does without relying on categories. I'll post it here if anyone else is interested in using it, it will look through all files in the START_PATH directory and rename them if it recognizes them as TV shows, it matches most naming formats. It will then move them to DESTINATION_PATH\Show\Season X (if it has seasons)\Show S0XEXX.ext. So if a show has a date it will be put into the Dest_path\Show\ directory. It will also rename and move any files that it recognizes as being in the wrong place.
My script should be Linux/OSX compatible too since it's written in Python.
Edit: I made some refinements to the code so it will also match files with spaces and underscores and removed some unnecessary parts
Edit2: I fixed a bug where the script crashed if movies were present. Added movie rename support since it coincided with fixing this bug.
Edit3: Added support for shows like S01E02E03
# This script sorts tv and movie files by identifying frequently used naming schemes
#
# TV files sent to tvdest\show\season\Show S0x E0x.ext
# Movie files sent to movdest\Movie Title.ext
#
# Read comments & edit formatting section of script yourself to modify the file name
# Edit the main loop to change how directories are laid out
#
# Edit the paths below to specify the starting path, tv and movie destination directories.
TV_DESTINATION = "D:\\Videos\\TV"
MOV_DESTINATION = "D:\\Videos\\Movies"
START_PATH = "D:\\Downloads\\Usenet"
##################################################################################
import os
MONTHS = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]
def generate_newname(filename):
""" Parses a filename and returns the new filename if its destination is different from the old location """
global TITLE, SEASON, TYPE
elems = str.split(str.lower(filename.replace("_", ".").replace(" ", ".")), '.') # split the filename by periods etc.
ext = elems[-1]
if ext in ["mkv", "avi", "ts"]: # Edit what extensions you want it to match
TYPE = "tv"
showname = []
for elem in elems[0:-1]: # parse the elements
if not str.isdigit(elem[0:4]): # shows w/o a date
if elem[0] == 's' and elem[1].isdigit():
temp = elem.replace('s', '').split('e')
season = temp[0]
episode = temp[1] if len(temp) == 2 else 'E'.join(temp[1:3]) # s01e01 OR s01e01e02
break
elif elem[0].isdigit():
if elem[1].isdigit():
if elem[2].isdigit() and elem[-1] != 'p' and elem[-1] != 'i': # do not match 720p, 1080i, etc.
if elem[1] == '0': # 101
season = '0' + elem[0]
episode = elem[1:3]
elif len(elem) == 4: # 0101
season = elem[0:2]
episode = elem[2:4]
elif elem[2] == 'x': # 01x01
season, episode = elem.split('x')
elif elem[1] == 'x': # 1x01
season, episode = elem.split('x')
season = '0' + season
break # season found, showname should be before it so break
else: # assume this is a part of showname
showname.append(elem)
else: # this show is probably named using a date, assume year.month.day format
i = elems.index(elem)
TITLE = titler(' '.join(showname))
SEASON = ""
if 'p' in elems[i+1] or 'i' in elems[i+1]: # Looks like a movie. Handle as movie.title.year.quality.otherstuff.ext
TYPE = "movie"
return "%s.%s" % (TITLE, ext) # Rename to Movie Title.ext, elems[i] is year and elems[i+1] is quality
return "%s %s %s %s.%s" % (TITLE, MONTHS[int(elems[i+1]) - 1], elems[i+2], elems[i], ext)
TITLE = titler(' '.join(showname))
SEASON = "Season %s" % (season[-1] if season[0] == '0' else season)
return "%s S%s E%s.%s" % (TITLE, season, episode, ext)
def titler(x): # python's title capitalizes after apostrophes... deal with it for certain cases
return str.title(x).replace("'S", "'s").replace(" In", " in").replace("'T", "'t").replace("'V", "'v").\
replace(" Of ", " of ").replace("Its", "It's").replace("Us", "").strip(" ")
for root, dirs, files in os.walk(START_PATH): # Processes all the files in START_PATH
for old in files:
new = generate_newname(old)
if new:
if TYPE == "tv":
newdir = os.path.join(TV_DESTINATION, TITLE)
if not os.path.exists(newdir): # If the new show directory doesn't exist, create it
os.mkdir(newdir)
newdir = os.path.join(newdir, SEASON)
if not os.path.exists(newdir) and SEASON != "": # If the new season directory doesn't exist, create it
os.mkdir(newdir)
new = os.path.join(newdir, new)
else: # movie
new = os.path.join(MOV_DESTINATION, new)
old = os.path.join(root, old)
if not (os.path.exists(new) or TITLE[0].isdigit()): # make sure there isn't a duplicate file,
try:
os.rename(old, new)
except:
pass
-
Somestranger26's script is awesome. I would love to have this feature in altbinz. Please add. :)
-
Would be good if is built in, if user can also modify this naming "The Sausage S01 E01.mkv" to some other ;)
-
Someone told me shows like "23" and "30 Sausage" didn't work and I implemented a fix. If you don't want a shell window to open when the script runs you can use the included vbs script in conjunction with the python one. To do that put in same folder and run turbo_sort.vbs instead of turbo_sort.py.
turbo_sort.py:
# This script sorts tv and movie files by identifying frequently used naming schemes
#
# TV files sent to tvdest\show\season\Show S0x E0x.ext
# Movie files sent to movdest\Movie Title.ext
#
# Read comments & edit formatting section of script yourself to modify the file name
# Edit the main loop to change how directories are laid out
#
# Edit the paths below to specify the starting path, tv and movie destination directories.
TV_DESTINATION = "D:\\Videos\\TV"
MOV_DESTINATION = "D:\\Videos\\Movies"
START_PATH = "D:\\Downloads\\Usenet"
##################################################################################
import os
MONTHS = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]
def generate_newname(filename):
""" Parses a filename and returns the new filename if its destination is different from the old location """
global TITLE, SEASON, TYPE
elems = str.split(str.lower(filename.replace("_", ".").replace(" ", ".")), '.') # split the filename by periods etc.
ext = elems[-1]
if ext in ["mkv", "avi", "ts"]: # Edit what extensions you want it to match
TYPE = "tv"
showname = []
for elem in elems[0:-1]: # parse the elements
if not str.isdigit(elem[0:4]): # shows w/o a date
if elem[0] == 's' and elem[1].isdigit():
temp = elem.replace('s', '').split('e')
season = temp[0]
episode = temp[1] if len(temp) == 2 else 'E'.join(temp[1:3]) # s01e01 OR s01e01e02
break
elif elem[0].isdigit():
if elem[1].isdigit():
if elem[2].isdigit() and elem[-1] != 'p' and elem[-1] != 'i': # do not match 720p, 1080i, etc.
if elem[1] == '0': # 101
season = '0' + elem[0]
episode = elem[1:3]
elif len(elem) == 4: # 0101
season = elem[0:2]
episode = elem[2:4]
elif elem[2] == 'x': # 01x01
season, episode = elem.split('x')
elif elem[1] == 'x': # 1x01
season, episode = elem.split('x')
season = '0' + season
break # season found, showname should be before it so break
else: # assume this is a part of showname
showname.append(elem)
elif elem in ["24", "30"]: # special case for shows that start w/ number like 30 rock and 24
showname.append(elem)
else: # this show is probably named using a date, assume year.month.day format
i = elems.index(elem)
TITLE = titler(' '.join(showname))
SEASON = ""
if 'p' in elems[i+1] or 'i' in elems[i+1]: # Looks like a movie. Handle as movie.title.year.quality.otherstuff.ext
TYPE = "movie"
return "%s.%s" % (TITLE, ext) # Rename to Movie Title.ext, elems[i] is year and elems[i+1] is quality
return "%s %s %s %s.%s" % (TITLE, MONTHS[int(elems[i+1]) - 1], elems[i+2], elems[i], ext)
TITLE = titler(' '.join(showname))
try:
SEASON = "Season %s" % (season[-1] if season[0] == '0' else season)
return "%s S%s E%s.%s" % (TITLE, season, episode, ext)
except UnboundLocalError:
print "An error occured parsing " + filename
return None
def titler(x): # python's title capitalizes after apostrophes... deal with it for certain cases
return str.title(x).replace("'S", "'s").replace(" In", " in").replace("'T", "'t").replace("'V", "'v").\
replace(" Of ", " of ").replace("Its", "It's").replace("Us", "").strip(" ")
for root, dirs, files in os.walk(START_PATH): # Processes all the files in START_PATH
for old in files:
new = generate_newname(old)
if new:
if TYPE == "tv":
newdir = os.path.join(TV_DESTINATION, TITLE)
if not os.path.exists(newdir): # If the new show directory doesn't exist, create it
os.mkdir(newdir)
newdir = os.path.join(newdir, SEASON)
if not os.path.exists(newdir) and SEASON != "": # If the new season directory doesn't exist, create it
os.mkdir(newdir)
new = os.path.join(newdir, new)
else: # movie
new = os.path.join(MOV_DESTINATION, new)
old = os.path.join(root, old)
print old + " ---> " + new
if not os.path.exists(new): # make sure there isn't a duplicate file
# or TITLE[0].isdigit()): <-- can't remember what this was for but caused problem with 30 rock/24
try:
os.rename(old, new)
except:
pass
turbo_sort.vbs:
Set WinScriptHost = CreateObject("WScript.Shell")
WinScriptHost.Run Chr(34) & "turbo_sort.py" & Chr(34), 0
Set WinScriptHost = Nothing
-
Just a pointer in case someone else finds this thread and is looking for similar functionality...
It's worth checking out SickBeard, as you can have it monitoring an unrar folder for shows and doing rename/move for you.
http://sickbeard.com (http://sickbeard.com)