Browse Source

Add usage of NZB-meta data and X-headers for Sorting.

Meta records: "episodename", "propername" and "year".
X-headers: "x-dnzb-episodename", "x-dnzb-propername" and "x-dnzb-year".
Controlled by an option.
pull/121/head
shypike 12 years ago
parent
commit
3b3759e81e
  1. 1
      sabnzbd/cfg.py
  2. 4
      sabnzbd/interface.py
  3. 5
      sabnzbd/postproc.py
  4. 199
      sabnzbd/tvsort.py
  5. 6
      sabnzbd/urlgrabber.py

1
sabnzbd/cfg.py

@ -128,6 +128,7 @@ folder_rename = OptionBool('misc', 'folder_rename', True)
folder_max_length = OptionNumber('misc', 'folder_max_length', DEF_FOLDER_MAX, 20, 65000) folder_max_length = OptionNumber('misc', 'folder_max_length', DEF_FOLDER_MAX, 20, 65000)
pause_on_pwrar = OptionBool('misc', 'pause_on_pwrar', True) pause_on_pwrar = OptionBool('misc', 'pause_on_pwrar', True)
prio_sort_list = OptionList('misc', 'prio_sort_list') prio_sort_list = OptionList('misc', 'prio_sort_list')
enable_meta = OptionBool('misc', 'enable_meta', True)
safe_postproc = OptionBool('misc', 'safe_postproc', True) safe_postproc = OptionBool('misc', 'safe_postproc', True)
empty_postproc = OptionBool('misc', 'empty_postproc', False) empty_postproc = OptionBool('misc', 'empty_postproc', False)

4
sabnzbd/interface.py

@ -1231,8 +1231,8 @@ SPECIAL_BOOL_LIST = \
'never_repair', 'allow_streaming', 'ignore_unrar_dates', 'rss_filenames', 'never_repair', 'allow_streaming', 'ignore_unrar_dates', 'rss_filenames',
'osx_menu', 'osx_speed', 'win_menu', 'uniconfig', 'use_pickle', 'allow_incomplete_nzb', 'osx_menu', 'osx_speed', 'win_menu', 'uniconfig', 'use_pickle', 'allow_incomplete_nzb',
'randomize_server_ip', 'no_ipv6', 'keep_awake', 'overwrite_files', 'empty_postproc', 'randomize_server_ip', 'no_ipv6', 'keep_awake', 'overwrite_files', 'empty_postproc',
'web_watchdog', 'wait_for_dfolder', 'warn_empty_nzb', 'enable_recursive', 'sanitize_safe' 'web_watchdog', 'wait_for_dfolder', 'warn_empty_nzb', 'enable_recursive', 'sanitize_safe',
'enable_meta'
) )
SPECIAL_VALUE_LIST = \ SPECIAL_VALUE_LIST = \
( 'size_limit', 'folder_max_length', 'fsys_type', 'movie_rename_limit', 'nomedia_marker', ( 'size_limit', 'folder_max_length', 'fsys_type', 'movie_rename_limit', 'nomedia_marker',

5
sabnzbd/postproc.py

@ -307,7 +307,10 @@ def process_job(nzo):
complete_dir = real_path(cfg.complete_dir.get_path(), catdir) complete_dir = real_path(cfg.complete_dir.get_path(), catdir)
## TV/Movie/Date Renaming code part 1 - detect and construct paths ## TV/Movie/Date Renaming code part 1 - detect and construct paths
file_sorter = Sorter(cat) if cfg.enable_meta():
file_sorter = Sorter(nzo, cat)
else:
file_sorter = Sorter(None, cat)
complete_dir = file_sorter.detect(dirname, complete_dir) complete_dir = file_sorter.detect(dirname, complete_dir)
if file_sorter.sort_file: if file_sorter.sort_file:
one_folder = False one_folder = False

199
sabnzbd/tvsort.py

@ -31,7 +31,7 @@ from sabnzbd.misc import move_to_path, cleanup_empty_directories, get_unique_pat
get_unique_filename, get_ext, renamer, sanitize_foldername get_unique_filename, get_ext, renamer, sanitize_foldername
from sabnzbd.constants import series_match, date_match, year_match, sample_match from sabnzbd.constants import series_match, date_match, year_match, sample_match
import sabnzbd.cfg as cfg import sabnzbd.cfg as cfg
from sabnzbd.encoding import titler from sabnzbd.encoding import titler, latin1
RE_SAMPLE = re.compile(sample_match, re.I) RE_SAMPLE = re.compile(sample_match, re.I)
# Do not rename .vob files as they are usually DVD's # Do not rename .vob files as they are usually DVD's
@ -96,31 +96,32 @@ def move_to_parent_folder(workdir):
class Sorter(object): class Sorter(object):
""" Generic Sorter class """ Generic Sorter class
""" """
def __init__(self, cat): def __init__(self, nzo, cat):
self.sorter = None self.sorter = None
self.type = None self.type = None
self.sort_file = False self.sort_file = False
self.nzo = nzo
self.cat = cat self.cat = cat
self.ext = '' self.ext = ''
def detect(self, dirname, complete_dir): def detect(self, dirname, complete_dir):
""" Detect which kind of sort applies """ Detect which kind of sort applies
""" """
self.sorter = SeriesSorter(dirname, complete_dir, self.cat) self.sorter = SeriesSorter(self.nzo, dirname, complete_dir, self.cat)
if self.sorter.matched: if self.sorter.matched:
complete_dir = self.sorter.get_final_path() complete_dir = self.sorter.get_final_path()
self.type = 'tv' self.type = 'tv'
self.sort_file = True self.sort_file = True
return complete_dir return complete_dir
self.sorter = DateSorter(dirname, complete_dir, self.cat) self.sorter = DateSorter(self.nzo, dirname, complete_dir, self.cat)
if self.sorter.matched: if self.sorter.matched:
complete_dir = self.sorter.get_final_path() complete_dir = self.sorter.get_final_path()
self.type = 'date' self.type = 'date'
self.sort_file = True self.sort_file = True
return complete_dir return complete_dir
self.sorter = GenericSorter(dirname, complete_dir, self.cat) self.sorter = GenericSorter(self.nzo, dirname, complete_dir, self.cat)
if self.sorter.matched: if self.sorter.matched:
complete_dir = self.sorter.get_final_path() complete_dir = self.sorter.get_final_path()
self.type = 'movie' self.type = 'movie'
@ -182,11 +183,12 @@ class Sorter(object):
class SeriesSorter(object): class SeriesSorter(object):
""" Methods for Series Sorting """ Methods for Series Sorting
""" """
def __init__(self, dirname, path, cat): def __init__(self, nzo, dirname, path, cat):
self.matched = False self.matched = False
self.original_dirname = dirname self.original_dirname = dirname
self.original_path = path self.original_path = path
self.nzo = nzo
self.cat = cat self.cat = cat
self.sort_string = cfg.tv_sort_string() self.sort_string = cfg.tv_sort_string()
self.cats = cfg.tv_categories() self.cats = cfg.tv_categories()
@ -252,8 +254,8 @@ class SeriesSorter(object):
def get_shownames(self): def get_shownames(self):
''' Get the show name from the match object and format it ''' ''' Get the show name from the match object and format it '''
# Get the formatted title and alternate title formats # Get the formatted title and alternate title formats
self.show_info['show_tname'], self.show_info['show_tname_two'], self.show_info['show_tname_three'] = get_titles(self.match_obj, self.original_dirname, True) self.show_info['show_tname'], self.show_info['show_tname_two'], self.show_info['show_tname_three'] = get_titles(self.nzo, self.match_obj, self.original_dirname, True)
self.show_info['show_name'], self.show_info['show_name_two'], self.show_info['show_name_three'] = get_titles(self.match_obj, self.original_dirname) self.show_info['show_name'], self.show_info['show_name_two'], self.show_info['show_name_three'] = get_titles(self.nzo, self.match_obj, self.original_dirname)
def get_seasons(self): def get_seasons(self):
@ -302,7 +304,7 @@ class SeriesSorter(object):
def get_showdescriptions(self): def get_showdescriptions(self):
''' Get the show descriptions from the match object and format them ''' ''' Get the show descriptions from the match object and format them '''
self.show_info['ep_name'], self.show_info['ep_name_two'], self.show_info['ep_name_three'] = get_descriptions(self.match_obj, self.original_dirname) self.show_info['ep_name'], self.show_info['ep_name_two'], self.show_info['ep_name_three'] = get_descriptions(self.nzo, self.match_obj, self.original_dirname)
def get_values(self): def get_values(self):
@ -518,7 +520,7 @@ def check_for_sequence(regex, files):
class GenericSorter(object): class GenericSorter(object):
""" Methods for Generic Sorting """ Methods for Generic Sorting
""" """
def __init__(self, dirname, path, cat): def __init__(self, nzo, dirname, path, cat):
self.matched = False self.matched = False
self.original_dirname = dirname self.original_dirname = dirname
@ -527,6 +529,7 @@ class GenericSorter(object):
self.extra = cfg.movie_sort_extra() self.extra = cfg.movie_sort_extra()
self.cats = cfg.movie_categories() self.cats = cfg.movie_categories()
self.cat = cat self.cat = cat
self.nzo = nzo
self.filename_set = '' self.filename_set = ''
self.fname = '' # Value for %fn substitution in folders self.fname = '' # Value for %fn substitution in folders
self.final_path = '' self.final_path = ''
@ -567,23 +570,30 @@ class GenericSorter(object):
""" Collect and construct all the values needed for path replacement """ Collect and construct all the values needed for path replacement
""" """
## - Get Year ## - Get Year
dirname = self.original_dirname.replace('_', ' ') if self.nzo:
RE_YEAR = re.compile(year_match, re.I) year = self.nzo.nzo_info.get('year') or self.nzo.meta.get('year')[0]
year_m = RE_YEAR.search(dirname)
if year_m:
# Find the last matched date
# Keep year_m to use in get_titles
year = RE_YEAR.findall(dirname)[-1][0]
self.movie_info['year'] = year
else: else:
self.movie_info['year'] = '' year = ''
if year:
year_m = None
else:
dirname = self.original_dirname.replace('_', ' ')
RE_YEAR = re.compile(year_match, re.I)
year_m = RE_YEAR.search(dirname)
if year_m:
# Find the last matched date
# Keep year_m to use in get_titles
year = RE_YEAR.findall(dirname)[-1][0]
else:
year = ''
self.movie_info['year'] = year
## - Get Decades ## - Get Decades
self.movie_info['decade'], self.movie_info['decade_two'] = get_decades(self.movie_info['year']) self.movie_info['decade'], self.movie_info['decade_two'] = get_decades(year)
## - Get Title ## - Get Title
self.movie_info['ttitle'], self.movie_info['ttitle_two'], self.movie_info['ttitle_three'] = get_titles(year_m, self.original_dirname, True) self.movie_info['ttitle'], self.movie_info['ttitle_two'], self.movie_info['ttitle_three'] = get_titles(self.nzo, year_m, self.original_dirname, True)
self.movie_info['title'], self.movie_info['title_two'], self.movie_info['title_three'] = get_titles(year_m, self.original_dirname) self.movie_info['title'], self.movie_info['title_two'], self.movie_info['title_three'] = get_titles(self.nzo, year_m, self.original_dirname)
return True return True
@ -721,7 +731,7 @@ class GenericSorter(object):
class DateSorter(object): class DateSorter(object):
""" Methods for Date Sorting """ Methods for Date Sorting
""" """
def __init__(self, dirname, path, cat): def __init__(self, nzo, dirname, path, cat):
self.matched = False self.matched = False
self.original_dirname = dirname self.original_dirname = dirname
@ -729,6 +739,7 @@ class DateSorter(object):
self.sort_string = cfg.date_sort_string() self.sort_string = cfg.date_sort_string()
self.cats = cfg.date_categories() self.cats = cfg.date_categories()
self.cat = cat self.cat = cat
self.nzo = nzo
self.filename_set = '' self.filename_set = ''
self.fname = '' # Value for %fn substitution in folders self.fname = '' # Value for %fn substitution in folders
@ -791,10 +802,10 @@ class DateSorter(object):
self.date_info['decade'], self.date_info['decade_two'] = get_decades(self.date_info['year']) self.date_info['decade'], self.date_info['decade_two'] = get_decades(self.date_info['year'])
## - Get Title ## - Get Title
self.date_info['ttitle'], self.date_info['ttitle_two'], self.date_info['ttitle_three'] = get_titles(self.match_obj, self.original_dirname, True) self.date_info['ttitle'], self.date_info['ttitle_two'], self.date_info['ttitle_three'] = get_titles(self.nzo, self.match_obj, self.original_dirname, True)
self.date_info['title'], self.date_info['title_two'], self.date_info['title_three'] = get_titles(self.match_obj, self.original_dirname) self.date_info['title'], self.date_info['title_two'], self.date_info['title_three'] = get_titles(self.nzo, self.match_obj, self.original_dirname)
self.date_info['ep_name'], self.date_info['ep_name_two'], self.date_info['ep_name_three'] = get_descriptions(self.match_obj, self.original_dirname) self.date_info['ep_name'], self.date_info['ep_name_two'], self.date_info['ep_name_three'] = get_descriptions(self.nzo, self.match_obj, self.original_dirname)
return True return True
@ -927,7 +938,7 @@ def path_subst(path, mapping):
return ''.join(newpath) return ''.join(newpath)
def get_titles(match, name, titleing=False): def get_titles(nzo, match, name, titleing=False):
''' '''
The title will be the part before the match The title will be the part before the match
Clean it up and title() it Clean it up and title() it
@ -935,59 +946,64 @@ def get_titles(match, name, titleing=False):
''.title() isn't very good under python so this contains ''.title() isn't very good under python so this contains
a lot of little hacks to make it better and for more control a lot of little hacks to make it better and for more control
''' '''
if match: if nzo:
name = name[:match.start()] title = latin1(nzo.nzo_info.get('propername') or nzo.meta.get('propername')[0])
else:
# Replace .US. with (US) title = ''
if cfg.tv_sort_countries() == 1: if not title:
for rep in COUNTRY_REP: if match:
# (us) > (US) name = name[:match.start()]
name = replace_word(name, rep.lower(), rep)
# (Us) > (US) # Replace .US. with (US)
name = replace_word(name, titler(rep), rep)
# .US. > (US)
dotted_country = '.%s.' % (rep.strip('()'))
name = replace_word(name, dotted_country, rep)
# Remove .US. and (US)
elif cfg.tv_sort_countries() == 2:
for rep in COUNTRY_REP:
# Remove (US)
name = replace_word(name, rep, '')
dotted_country = '.%s.' % (rep.strip('()'))
# Remove .US.
name = replace_word(name, dotted_country, '.')
title = name.replace('.', ' ').replace('_', ' ')
title = title.strip().strip('(').strip('_').strip('-').strip().strip('_')
if titleing:
title = titler(title) # title the show name so it is in a consistant letter case
#title applied uppercase to 's Python bug?
title = title.replace("'S", "'s")
# Replace titled country names, (Us) with (US) and so on
if cfg.tv_sort_countries() == 1: if cfg.tv_sort_countries() == 1:
for rep in COUNTRY_REP: for rep in COUNTRY_REP:
title = title.replace(titler(rep), rep) # (us) > (US)
# Remove country names, ie (Us) name = replace_word(name, rep.lower(), rep)
# (Us) > (US)
name = replace_word(name, titler(rep), rep)
# .US. > (US)
dotted_country = '.%s.' % (rep.strip('()'))
name = replace_word(name, dotted_country, rep)
# Remove .US. and (US)
elif cfg.tv_sort_countries() == 2: elif cfg.tv_sort_countries() == 2:
for rep in COUNTRY_REP: for rep in COUNTRY_REP:
title = title.replace(titler(rep), '').strip() # Remove (US)
name = replace_word(name, rep, '')
# Make sure some words such as 'and' or 'of' stay lowercased. dotted_country = '.%s.' % (rep.strip('()'))
for x in LOWERCASE: # Remove .US.
xtitled = titler(x) name = replace_word(name, dotted_country, '.')
title = replace_word(title, xtitled, x)
title = name.replace('.', ' ').replace('_', ' ')
# Make sure some words such as 'III' or 'IV' stay uppercased. title = title.strip().strip('(').strip('_').strip('-').strip().strip('_')
for x in UPPERCASE:
xtitled = titler(x) if titleing:
title = replace_word(title, xtitled, x) title = titler(title) # title the show name so it is in a consistant letter case
# Make sure the first letter of the title is always uppercase #title applied uppercase to 's Python bug?
if title: title = title.replace("'S", "'s")
title = titler(title[0]) + title[1:]
# Replace titled country names, (Us) with (US) and so on
if cfg.tv_sort_countries() == 1:
for rep in COUNTRY_REP:
title = title.replace(titler(rep), rep)
# Remove country names, ie (Us)
elif cfg.tv_sort_countries() == 2:
for rep in COUNTRY_REP:
title = title.replace(titler(rep), '').strip()
# Make sure some words such as 'and' or 'of' stay lowercased.
for x in LOWERCASE:
xtitled = titler(x)
title = replace_word(title, xtitled, x)
# Make sure some words such as 'III' or 'IV' stay uppercased.
for x in UPPERCASE:
xtitled = titler(x)
title = replace_word(title, xtitled, x)
# Make sure the first letter of the title is always uppercase
if title:
title = titler(title[0]) + title[1:]
# The title with spaces replaced by dots # The title with spaces replaced by dots
dots = title.replace(" - ", "-").replace(' ','.').replace('_','.') dots = title.replace(" - ", "-").replace(' ','.').replace('_','.')
@ -1007,22 +1023,27 @@ def replace_word(input, one, two):
input = input.replace(one, two) input = input.replace(one, two)
return input return input
def get_descriptions(match, name): def get_descriptions(nzo, match, name):
''' '''
If present, get a description from the nzb name. If present, get a description from the nzb name.
A description has to be after the matched item, seperated either A description has to be after the matched item, seperated either
like ' - Description' or '_-_Description' like ' - Description' or '_-_Description'
''' '''
if match: if nzo:
ep_name = name[match.end():] # Need to improve for multi ep support ep_name = latin1(nzo.nzo_info.get('episodename') or nzo.meta.get('episodename')[0])
else: else:
ep_name = name ep_name = ''
ep_name = ep_name.strip(' _.') if not ep_name:
if ep_name.startswith('-'): if match:
ep_name = ep_name.strip('- _.') ep_name = name[match.end():] # Need to improve for multi ep support
if '.' in ep_name and ' ' not in ep_name: else:
ep_name = ep_name.replace('.', ' ') ep_name = name
ep_name = ep_name.replace('_', ' ') ep_name = ep_name.strip(' _.')
if ep_name.startswith('-'):
ep_name = ep_name.strip('- _.')
if '.' in ep_name and ' ' not in ep_name:
ep_name = ep_name.replace('.', ' ')
ep_name = ep_name.replace('_', ' ')
ep_name2 = ep_name.replace(" - ", "-").replace(" ", ".") ep_name2 = ep_name.replace(" - ", "-").replace(" ", ".")
ep_name3 = ep_name.replace(" ", "_") ep_name3 = ep_name.replace(" ", "_")
return ep_name, ep_name2, ep_name3 return ep_name, ep_name2, ep_name3
@ -1189,13 +1210,13 @@ def eval_sort(sorttype, expression, name=None, multipart=''):
name = sanitize_foldername(name) name = sanitize_foldername(name)
if sorttype == 'series': if sorttype == 'series':
name = name or ('%s S01E05 - %s [DTS]' % (Ttemplate('show-name'), Ttemplate('ep-name'))) name = name or ('%s S01E05 - %s [DTS]' % (Ttemplate('show-name'), Ttemplate('ep-name')))
sorter = sabnzbd.tvsort.SeriesSorter(name, path, 'tv') sorter = sabnzbd.tvsort.SeriesSorter(None, name, path, 'tv')
elif sorttype == 'generic': elif sorttype == 'generic':
name = name or (Ttemplate('movie-sp-name') + ' (2009)') name = name or (Ttemplate('movie-sp-name') + ' (2009)')
sorter = sabnzbd.tvsort.GenericSorter(name, path, 'tv') sorter = sabnzbd.tvsort.GenericSorter(None, name, path, 'tv')
elif sorttype == 'date': elif sorttype == 'date':
name = name or (Ttemplate('show-name') + ' 2009-01-02') name = name or (Ttemplate('show-name') + ' 2009-01-02')
sorter = sabnzbd.tvsort.DateSorter(name, path, 'tv') sorter = sabnzbd.tvsort.DateSorter(None, name, path, 'tv')
else: else:
return None return None
sorter.sort_string = expression sorter.sort_string = expression

6
sabnzbd/urlgrabber.py

@ -147,6 +147,12 @@ class URLGrabber(Thread):
filename = value filename = value
if not filename.endswith('.nzb'): if not filename.endswith('.nzb'):
filename += '.nzb' filename += '.nzb'
elif item == 'x-dnzb-propername':
nzo_info['propername'] = value
elif item == 'x-dnzb-episodename':
nzo_info['episodename'] = value
elif item == 'x-dnzb-year':
nzo_info['year'] = value
elif item in ('content-length',): elif item in ('content-length',):
length = misc.int_conv(value) length = misc.int_conv(value)

Loading…
Cancel
Save