2014-03-10 05:18:05 +00:00
# Author: Nic Wolfe <nic@wolfeden.ca>
# URL: http://code.google.com/p/sickbeard/
#
2014-05-23 12:37:22 +00:00
# This file is part of SickRage.
2014-03-10 05:18:05 +00:00
#
2014-05-23 12:37:22 +00:00
# SickRage is free software: you can redistribute it and/or modify
2014-03-10 05:18:05 +00:00
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
2014-05-23 12:37:22 +00:00
# SickRage is distributed in the hope that it will be useful,
2014-03-10 05:18:05 +00:00
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2014-05-26 06:29:22 +00:00
# GNU General Public License for more details.
2014-03-10 05:18:05 +00:00
#
# You should have received a copy of the GNU General Public License
2014-05-23 12:37:22 +00:00
# along with SickRage. If not, see <http://www.gnu.org/licenses/>.
2014-03-10 05:18:05 +00:00
import datetime
import os . path
import re
2014-05-07 07:50:49 +00:00
import threading
2014-03-10 05:18:05 +00:00
import regexes
2014-05-06 21:35:37 +00:00
import time
2014-03-10 05:18:05 +00:00
import sickbeard
2014-05-27 07:44:23 +00:00
from sickbeard import logger , helpers , scene_numbering , db
2014-05-26 06:29:22 +00:00
from sickbeard . exceptions import EpisodeNotFoundByAbsoluteNumberException
2014-04-30 22:07:18 +00:00
from dateutil import parser
2014-03-25 05:57:24 +00:00
2014-05-07 07:50:49 +00:00
nameparser_lock = threading . Lock ( )
2014-03-10 05:18:05 +00:00
class NameParser ( object ) :
2014-04-29 04:55:59 +00:00
ALL_REGEX = 0
NORMAL_REGEX = 1
SPORTS_REGEX = 2
2014-05-26 06:29:22 +00:00
ANIME_REGEX = 3
2014-04-28 09:15:29 +00:00
2014-05-26 06:29:22 +00:00
def __init__ ( self , file_name = True , show = None , useIndexers = False ) :
regexMode = self . ALL_REGEX
if show and show . is_anime :
regexMode = self . ANIME_REGEX
elif show and show . is_sports :
regexMode = self . SPORTS_REGEX
elif show and not show . is_anime and not show . is_sports :
regexMode = self . NORMAL_REGEX
2014-03-10 05:18:05 +00:00
self . file_name = file_name
2014-04-28 09:15:29 +00:00
self . regexMode = regexMode
2014-05-26 06:29:22 +00:00
self . compiled_regexes = { }
2014-04-30 22:07:18 +00:00
self . _compile_regexes ( self . regexMode )
2014-05-26 06:29:22 +00:00
self . showList = sickbeard . showList
self . useIndexers = useIndexers
2014-05-27 07:44:23 +00:00
self . show = show
2014-03-10 05:18:05 +00:00
def clean_series_name ( self , series_name ) :
""" Cleans up series name by removing any . and _
characters , along with any trailing hyphens .
2014-05-29 00:44:00 +00:00
2014-03-10 05:18:05 +00:00
Is basically equivalent to replacing all _ and . with a
space , but handles decimal numbers in string , for example :
2014-05-29 00:44:00 +00:00
2014-03-10 05:18:05 +00:00
>> > cleanRegexedSeriesName ( " an.example.1.0.test " )
' an example 1.0 test '
>> > cleanRegexedSeriesName ( " an_example_1.0_test " )
' an example 1.0 test '
2014-05-29 00:44:00 +00:00
2014-03-10 05:18:05 +00:00
Stolen from dbr ' s tvnamer
"""
2014-03-25 05:57:24 +00:00
2014-03-10 05:18:05 +00:00
series_name = re . sub ( " ( \ D) \ .(?! \ s)( \ D) " , " \\ 1 \\ 2 " , series_name )
2014-03-25 05:57:24 +00:00
series_name = re . sub ( " ( \ d) \ .( \ d {4} ) " , " \\ 1 \\ 2 " , series_name ) # if it ends in a year then don't keep the dot
2014-03-10 05:18:05 +00:00
series_name = re . sub ( " ( \ D) \ .(?! \ s) " , " \\ 1 " , series_name )
series_name = re . sub ( " \ .(?! \ s)( \ D) " , " \\ 1 " , series_name )
series_name = series_name . replace ( " _ " , " " )
series_name = re . sub ( " -$ " , " " , series_name )
2014-03-25 05:57:24 +00:00
series_name = re . sub ( " ^ \ [.* \ ] " , " " , series_name )
2014-03-10 05:18:05 +00:00
return series_name . strip ( )
2014-04-28 09:15:29 +00:00
def _compile_regexes ( self , regexMode ) :
if regexMode < = self . ALL_REGEX :
2014-05-07 07:50:49 +00:00
logger . log ( u " Using ALL regexs " , logger . DEBUG )
2014-05-26 06:29:22 +00:00
uncompiled_regex = [ regexes . anime_regexes , regexes . sports_regexs , regexes . normal_regexes ]
2014-04-28 09:15:29 +00:00
elif regexMode == self . NORMAL_REGEX :
2014-05-07 07:50:49 +00:00
logger . log ( u " Using NORMAL regexs " , logger . DEBUG )
2014-05-26 06:29:22 +00:00
uncompiled_regex = [ regexes . normal_regexes ]
2014-04-28 09:15:29 +00:00
elif regexMode == self . SPORTS_REGEX :
2014-05-07 07:50:49 +00:00
logger . log ( u " Using SPORTS regexs " , logger . DEBUG )
2014-05-26 06:29:22 +00:00
uncompiled_regex = [ regexes . sports_regexs ]
elif regexMode == self . ANIME_REGEX :
logger . log ( u " Using ANIME regexs " , logger . DEBUG )
uncompiled_regex = [ regexes . anime_regexes , regexes . normal_regexes ]
2014-04-28 09:15:29 +00:00
else :
2014-05-07 07:50:49 +00:00
logger . log ( u " This is a programing ERROR. Fallback Using NORMAL regexs " , logger . ERROR )
2014-05-26 06:29:22 +00:00
uncompiled_regex = [ regexes . normal_regexes ]
2014-03-10 05:18:05 +00:00
2014-05-26 06:29:22 +00:00
for regexItem in uncompiled_regex :
for regex_type , regex in regexItem . items ( ) :
try :
self . compiled_regexes [ regex_type ]
except :
self . compiled_regexes [ regex_type ] = { }
2014-03-25 05:57:24 +00:00
2014-05-26 06:29:22 +00:00
for ( cur_pattern_name , cur_pattern ) in regex :
try :
cur_regex = re . compile ( cur_pattern , re . VERBOSE | re . IGNORECASE )
except re . error , errormsg :
logger . log ( u " WARNING: Invalid episode_pattern, %s . %s " % ( errormsg , cur_pattern ) )
else :
self . compiled_regexes [ regex_type ] . update ( { cur_pattern_name : cur_regex } )
2014-03-25 05:57:24 +00:00
2014-05-26 06:29:22 +00:00
def _parse_string ( self , name ) :
2014-05-28 21:13:29 +00:00
if not name :
return
for cur_regex_type , cur_regexes in self . compiled_regexes . items ( ) :
2014-05-26 06:29:22 +00:00
for cur_regex_name , cur_regex in cur_regexes . items ( ) :
match = cur_regex . match ( name )
2014-05-17 09:27:17 +00:00
2014-05-26 06:29:22 +00:00
if not match :
continue
2014-03-10 05:18:05 +00:00
2014-05-26 06:29:22 +00:00
result = ParseResult ( name )
result . which_regex = [ cur_regex_name ]
2014-03-25 05:57:24 +00:00
2014-05-26 06:29:22 +00:00
named_groups = match . groupdict ( ) . keys ( )
2014-03-25 05:57:24 +00:00
2014-05-26 06:29:22 +00:00
if ' series_name ' in named_groups :
result . series_name = match . group ( ' series_name ' )
if result . series_name :
result . series_name = self . clean_series_name ( result . series_name )
2014-03-10 05:18:05 +00:00
2014-05-28 21:13:29 +00:00
cur_show = helpers . get_show_by_name ( result . series_name , useIndexer = self . useIndexers )
if self . show and cur_show :
if self . show . indexerid != cur_show . indexerid :
logger . log (
u " I expected an episode of the show " + self . show . name + " but the parser thinks its the show " + cur_show . name + " . I will continue thinking its " + self . show . name ,
logger . WARNING )
else :
result . show = self . show
2014-05-26 06:29:22 +00:00
if ' season_num ' in named_groups :
tmp_season = int ( match . group ( ' season_num ' ) )
if cur_regex_name == ' bare ' and tmp_season in ( 19 , 20 ) :
continue
result . season_number = tmp_season
if ' ep_num ' in named_groups :
ep_num = self . _convert_number ( match . group ( ' ep_num ' ) )
if ' extra_ep_num ' in named_groups and match . group ( ' extra_ep_num ' ) :
result . episode_numbers = range ( ep_num , self . _convert_number ( match . group ( ' extra_ep_num ' ) ) + 1 )
else :
result . episode_numbers = [ ep_num ]
if ' ep_ab_num ' in named_groups :
ep_ab_num = self . _convert_number ( match . group ( ' ep_ab_num ' ) )
if ' extra_ab_ep_num ' in named_groups and match . group ( ' extra_ab_ep_num ' ) :
result . ab_episode_numbers = range ( ep_ab_num ,
self . _convert_number ( match . group ( ' extra_ab_ep_num ' ) ) + 1 )
else :
result . ab_episode_numbers = [ ep_ab_num ]
if ' sports_event_id ' in named_groups :
sports_event_id = match . group ( ' sports_event_id ' )
if sports_event_id :
result . sports_event_id = int ( match . group ( ' sports_event_id ' ) )
if ' sports_event_name ' in named_groups :
result . sports_event_name = match . group ( ' sports_event_name ' )
if result . sports_event_name :
result . sports_event_name = self . clean_series_name ( result . sports_event_name )
if ' sports_event_date ' in named_groups :
sports_event_date = match . group ( ' sports_event_date ' )
if sports_event_date :
try :
result . sports_event_date = parser . parse ( sports_event_date , fuzzy = True ) . date ( )
except :
continue
if ' air_year ' in named_groups and ' air_month ' in named_groups and ' air_day ' in named_groups :
year = int ( match . group ( ' air_year ' ) )
month = int ( match . group ( ' air_month ' ) )
day = int ( match . group ( ' air_day ' ) )
2014-04-30 12:10:13 +00:00
2014-04-30 22:07:18 +00:00
try :
2014-05-26 06:29:22 +00:00
dtStr = ' %s - %s - %s ' % ( year , month , day )
result . air_date = datetime . datetime . strptime ( dtStr , " % Y- % m- %d " ) . date ( )
2014-05-02 00:57:51 +00:00
except :
continue
2014-04-30 22:07:18 +00:00
2014-05-26 06:29:22 +00:00
if ' extra_info ' in named_groups :
tmp_extra_info = match . group ( ' extra_info ' )
2014-03-25 05:57:24 +00:00
2014-05-26 06:29:22 +00:00
# Show.S04.Special or Show.S05.Part.2.Extras is almost certainly not every episode in the season
if tmp_extra_info and cur_regex_name == ' season_only ' and re . search (
r ' ([. _-]|^)(special|extra)s? \ w*([. _-]|$) ' , tmp_extra_info , re . I ) :
continue
result . extra_info = tmp_extra_info
2014-03-25 05:57:24 +00:00
2014-05-26 06:29:22 +00:00
if ' release_group ' in named_groups :
result . release_group = match . group ( ' release_group ' )
2014-03-10 05:18:05 +00:00
2014-05-28 21:13:29 +00:00
if result . show and result . show . is_anime and cur_regex_type in [ ' anime ' , ' normal ' ] :
2014-05-26 06:29:22 +00:00
return result
2014-05-28 21:13:29 +00:00
elif result . show and result . show . is_sports and cur_regex_type == ' sports ' :
2014-05-26 06:29:22 +00:00
return result
elif cur_regex_type == ' normal ' :
return result
2014-03-25 05:57:24 +00:00
2014-03-10 05:18:05 +00:00
return None
def _combine_results ( self , first , second , attr ) :
# if the first doesn't exist then return the second or nothing
if not first :
if not second :
return None
else :
return getattr ( second , attr )
# if the second doesn't exist then return the first
if not second :
return getattr ( first , attr )
2014-03-25 05:57:24 +00:00
2014-03-10 05:18:05 +00:00
a = getattr ( first , attr )
b = getattr ( second , attr )
2014-03-25 05:57:24 +00:00
2014-03-10 05:18:05 +00:00
# if a is good use it
2014-03-20 18:03:22 +00:00
if a != None or ( type ( a ) == list and len ( a ) ) :
2014-03-10 05:18:05 +00:00
return a
# if not use b (if b isn't set it'll just be default)
else :
return b
2014-03-25 05:57:24 +00:00
def _unicodify ( self , obj , encoding = " utf-8 " ) :
2014-03-10 05:18:05 +00:00
if isinstance ( obj , basestring ) :
if not isinstance ( obj , unicode ) :
obj = unicode ( obj , encoding )
return obj
2014-05-28 21:13:29 +00:00
def _convert_number ( self , org_number ) :
"""
Convert org_number into an integer
org_number : integer or representation of a number : string or unicode
Try force converting to int first , on error try converting from Roman numerals
returns integer or 0
"""
2014-03-25 05:57:24 +00:00
2014-03-10 05:18:05 +00:00
try :
2014-05-28 21:13:29 +00:00
# try forcing to int
if org_number :
number = int ( org_number )
else :
number = 0
2014-03-10 05:18:05 +00:00
except :
2014-05-28 21:13:29 +00:00
# on error try converting from Roman numerals
roman_to_int_map = ( ( ' M ' , 1000 ) , ( ' CM ' , 900 ) , ( ' D ' , 500 ) , ( ' CD ' , 400 ) , ( ' C ' , 100 ) ,
( ' XC ' , 90 ) , ( ' L ' , 50 ) , ( ' XL ' , 40 ) , ( ' X ' , 10 ) ,
( ' IX ' , 9 ) , ( ' V ' , 5 ) , ( ' IV ' , 4 ) , ( ' I ' , 1 )
2014-03-10 05:18:05 +00:00
)
2014-03-25 05:57:24 +00:00
2014-05-28 21:13:29 +00:00
roman_numeral = str ( org_number ) . upper ( )
number = 0
index = 0
2014-03-25 05:57:24 +00:00
2014-05-28 21:13:29 +00:00
for numeral , integer in roman_to_int_map :
while roman_numeral [ index : index + len ( numeral ) ] == numeral :
number + = integer
index + = len ( numeral )
2014-03-25 05:57:24 +00:00
2014-05-28 21:13:29 +00:00
return number
2014-03-10 05:18:05 +00:00
2014-05-26 10:42:34 +00:00
def parse ( self , name , cache_result = True ) :
2014-03-10 05:18:05 +00:00
name = self . _unicodify ( name )
cached = name_parser_cache . get ( name )
if cached :
return cached
# break it into parts if there are any (dirname, file name, extension)
dir_name , file_name = os . path . split ( name )
ext_match = re . match ( ' (.*) \ . \ w { 3,4}$ ' , file_name )
if ext_match and self . file_name :
base_file_name = ext_match . group ( 1 )
else :
base_file_name = file_name
# use only the direct parent dir
dir_name = os . path . basename ( dir_name )
# set up a result to use
final_result = ParseResult ( name )
# try parsing the file name
file_name_result = self . _parse_string ( base_file_name )
# parse the dirname for extra info if needed
dir_name_result = self . _parse_string ( dir_name )
# build the ParseResult object
final_result . air_date = self . _combine_results ( file_name_result , dir_name_result , ' air_date ' )
2014-05-26 06:29:22 +00:00
final_result . ab_episode_numbers = self . _combine_results ( file_name_result , dir_name_result , ' ab_episode_numbers ' )
2014-04-30 12:10:13 +00:00
# sports event title
2014-05-02 03:03:44 +00:00
final_result . sports_event_id = self . _combine_results ( file_name_result , dir_name_result , ' sports_event_id ' )
final_result . sports_event_name = self . _combine_results ( file_name_result , dir_name_result , ' sports_event_name ' )
2014-04-30 22:07:18 +00:00
final_result . sports_event_date = self . _combine_results ( file_name_result , dir_name_result , ' sports_event_date ' )
2014-03-10 05:18:05 +00:00
if not final_result . air_date :
final_result . season_number = self . _combine_results ( file_name_result , dir_name_result , ' season_number ' )
final_result . episode_numbers = self . _combine_results ( file_name_result , dir_name_result , ' episode_numbers ' )
# if the dirname has a release group/show name I believe it over the filename
final_result . series_name = self . _combine_results ( dir_name_result , file_name_result , ' series_name ' )
2014-05-02 00:57:51 +00:00
2014-03-10 05:18:05 +00:00
final_result . extra_info = self . _combine_results ( dir_name_result , file_name_result , ' extra_info ' )
final_result . release_group = self . _combine_results ( dir_name_result , file_name_result , ' release_group ' )
final_result . which_regex = [ ]
if final_result == file_name_result :
final_result . which_regex = file_name_result . which_regex
elif final_result == dir_name_result :
final_result . which_regex = dir_name_result . which_regex
else :
if file_name_result :
final_result . which_regex + = file_name_result . which_regex
if dir_name_result :
final_result . which_regex + = dir_name_result . which_regex
2014-05-26 06:29:22 +00:00
final_result . show = self . _combine_results ( file_name_result , dir_name_result , ' show ' )
2014-03-10 05:18:05 +00:00
# if there's no useful info in it then raise an exception
2014-03-20 18:03:22 +00:00
if final_result . season_number == None and not final_result . episode_numbers and final_result . air_date == None and not final_result . series_name :
2014-03-10 05:18:05 +00:00
raise InvalidNameException ( " Unable to parse " + name . encode ( sickbeard . SYS_ENCODING , ' xmlcharrefreplace ' ) )
2014-05-26 10:42:34 +00:00
if cache_result :
name_parser_cache . add ( name , final_result )
2014-03-10 05:18:05 +00:00
return final_result
2014-03-25 05:57:24 +00:00
2014-03-10 05:18:05 +00:00
class ParseResult ( object ) :
def __init__ ( self ,
original_name ,
series_name = None ,
2014-05-02 03:03:44 +00:00
sports_event_id = None ,
sports_event_name = None ,
2014-04-30 22:07:18 +00:00
sports_event_date = None ,
2014-03-10 05:18:05 +00:00
season_number = None ,
episode_numbers = None ,
extra_info = None ,
release_group = None ,
2014-04-28 09:15:29 +00:00
air_date = None ,
2014-05-26 06:29:22 +00:00
ab_episode_numbers = None ,
show = None
2014-03-25 05:57:24 +00:00
) :
2014-03-10 05:18:05 +00:00
self . original_name = original_name
2014-03-25 05:57:24 +00:00
2014-03-10 05:18:05 +00:00
self . series_name = series_name
self . season_number = season_number
if not episode_numbers :
self . episode_numbers = [ ]
else :
self . episode_numbers = episode_numbers
2014-05-26 06:29:22 +00:00
if not ab_episode_numbers :
self . ab_episode_numbers = [ ]
else :
self . ab_episode_numbers = ab_episode_numbers
2014-03-10 05:18:05 +00:00
self . extra_info = extra_info
self . release_group = release_group
2014-03-25 05:57:24 +00:00
2014-03-10 05:18:05 +00:00
self . air_date = air_date
2014-04-30 12:10:13 +00:00
2014-05-02 03:03:44 +00:00
self . sports_event_id = sports_event_id
self . sports_event_name = sports_event_name
2014-04-30 22:07:18 +00:00
self . sports_event_date = sports_event_date
2014-03-25 05:57:24 +00:00
2014-03-10 05:18:05 +00:00
self . which_regex = None
2014-05-26 06:29:22 +00:00
self . show = show
2014-03-25 05:57:24 +00:00
2014-03-10 05:18:05 +00:00
def __eq__ ( self , other ) :
if not other :
return False
2014-03-25 05:57:24 +00:00
2014-03-10 05:18:05 +00:00
if self . series_name != other . series_name :
return False
if self . season_number != other . season_number :
return False
if self . episode_numbers != other . episode_numbers :
return False
if self . extra_info != other . extra_info :
return False
if self . release_group != other . release_group :
return False
if self . air_date != other . air_date :
return False
2014-05-02 03:03:44 +00:00
if self . sports_event_id != other . sports_event_id :
return False
if self . sports_event_name != other . sports_event_name :
return False
2014-04-30 22:07:18 +00:00
if self . sports_event_date != other . sports_event_date :
return False
2014-05-26 06:29:22 +00:00
if self . ab_episode_numbers != other . ab_episode_numbers :
return False
if self . show != other . show :
return False
2014-03-25 05:57:24 +00:00
2014-03-10 05:18:05 +00:00
return True
def __str__ ( self ) :
2014-03-20 18:03:22 +00:00
if self . series_name != None :
2014-03-10 05:18:05 +00:00
to_return = self . series_name + u ' - '
else :
to_return = u ' '
2014-03-20 18:03:22 +00:00
if self . season_number != None :
2014-03-25 05:57:24 +00:00
to_return + = ' S ' + str ( self . season_number )
2014-03-10 05:18:05 +00:00
if self . episode_numbers and len ( self . episode_numbers ) :
for e in self . episode_numbers :
2014-03-25 05:57:24 +00:00
to_return + = ' E ' + str ( e )
2014-03-10 05:18:05 +00:00
if self . air_by_date :
to_return + = str ( self . air_date )
2014-04-28 09:15:29 +00:00
if self . sports :
2014-05-02 03:03:44 +00:00
to_return + = str ( self . sports_event_name )
to_return + = str ( self . sports_event_id )
2014-04-30 22:07:18 +00:00
to_return + = str ( self . sports_event_date )
2014-05-26 06:29:22 +00:00
if self . ab_episode_numbers :
to_return + = ' absolute_numbers: ' + str ( self . ab_episode_numbers )
2014-03-10 05:18:05 +00:00
if self . extra_info :
to_return + = ' - ' + self . extra_info
if self . release_group :
to_return + = ' ( ' + self . release_group + ' ) '
2014-03-25 05:57:24 +00:00
to_return + = ' [ABD: ' + str ( self . air_by_date ) + ' ] '
2014-04-28 09:15:29 +00:00
to_return + = ' [SPORTS: ' + str ( self . sports ) + ' ] '
2014-05-26 06:29:22 +00:00
to_return + = ' [ANIME: ' + str ( self . is_anime ) + ' ] '
2014-04-28 09:15:29 +00:00
to_return + = ' [whichReg: ' + str ( self . which_regex ) + ' ] '
2014-03-10 05:18:05 +00:00
return to_return . encode ( ' utf-8 ' )
2014-05-26 10:42:34 +00:00
def convert ( self ) :
if not self . show : return self # need show object
2014-05-14 08:01:36 +00:00
if self . air_by_date or self . sports : return self # scene numbering does not apply to air-by-date
2014-05-03 09:23:26 +00:00
2014-05-28 21:13:29 +00:00
# check if show is anime
if self . show . is_anime and not ( len ( self . episode_numbers ) or self . season_number ) and not len ( self . ab_episode_numbers ) :
return self # can't work without a season
elif not self . show . _is_anime and not ( len ( self . episode_numbers ) or self . season_number ) :
return self
2014-05-01 11:36:16 +00:00
new_episode_numbers = [ ]
new_season_numbers = [ ]
2014-05-26 06:29:22 +00:00
new_absolute_numbers = [ ]
2014-05-28 21:13:29 +00:00
if len ( self . ab_episode_numbers ) and not len ( self . episode_numbers ) :
for epAbNo in self . ab_episode_numbers :
( s , e , a ) = scene_numbering . get_absolute_numbering ( self . show . indexerid , self . show . indexer , epAbNo )
if ( s or e or a ) :
new_episode_numbers . append ( e )
new_season_numbers . append ( s )
new_absolute_numbers . append ( a )
else :
for epNo in self . episode_numbers :
( s , e , a ) = scene_numbering . get_indexer_numbering ( self . show . indexerid , self . show . indexer ,
self . season_number ,
epNo , None )
new_episode_numbers . append ( e )
new_season_numbers . append ( s )
new_absolute_numbers . append ( a )
2014-05-01 11:36:16 +00:00
# need to do a quick sanity check here. It's possible that we now have episodes
# from more than one season (by tvdb numbering), and this is just too much
# for sickbeard, so we'd need to flag it.
new_season_numbers = list ( set ( new_season_numbers ) ) # remove duplicates
if len ( new_season_numbers ) > 1 :
raise InvalidNameException ( " Scene numbering results episodes from "
" seasons %s , (i.e. more than one) and "
2014-05-23 12:37:22 +00:00
" sickrage does not support this. "
2014-05-01 11:36:16 +00:00
" Sorry. " % ( str ( new_season_numbers ) ) )
# I guess it's possible that we'd have duplicate episodes too, so lets
# eliminate them
new_episode_numbers = list ( set ( new_episode_numbers ) )
new_episode_numbers . sort ( )
2014-05-26 06:29:22 +00:00
# dedupe absolute numbers
new_absolute_numbers = list ( set ( new_absolute_numbers ) )
new_absolute_numbers . sort ( )
self . ab_episode_numbers = new_absolute_numbers
2014-05-01 11:36:16 +00:00
self . episode_numbers = new_episode_numbers
self . season_number = new_season_numbers [ 0 ]
2014-04-30 12:10:13 +00:00
2014-04-30 12:21:16 +00:00
return self
2014-04-30 12:18:20 +00:00
2014-03-10 05:18:05 +00:00
def _is_air_by_date ( self ) :
2014-04-30 12:10:13 +00:00
if self . season_number == None and len ( self . episode_numbers ) == 0 and self . air_date :
2014-03-10 05:18:05 +00:00
return True
return False
2014-05-07 07:50:49 +00:00
2014-03-10 05:18:05 +00:00
air_by_date = property ( _is_air_by_date )
2014-03-25 05:57:24 +00:00
2014-05-26 06:29:22 +00:00
def _is_anime ( self ) :
if self . ab_episode_numbers :
2014-05-26 10:42:34 +00:00
if self . show and self . show . is_anime :
return True
2014-05-26 06:29:22 +00:00
return False
is_anime = property ( _is_anime )
2014-04-28 09:15:29 +00:00
def _is_sports ( self ) :
2014-05-02 00:57:51 +00:00
if self . sports_event_date :
2014-04-28 09:15:29 +00:00
return True
return False
2014-05-07 07:50:49 +00:00
2014-04-28 09:15:29 +00:00
sports = property ( _is_sports )
2014-05-07 07:50:49 +00:00
2014-03-10 05:18:05 +00:00
class NameParserCache ( object ) :
_previous_parsed = { }
_cache_size = 100
2014-03-25 05:57:24 +00:00
2014-05-01 22:53:37 +00:00
def add ( self , name , parse_result ) :
2014-05-11 14:17:11 +00:00
self . _previous_parsed [ name ] = parse_result
_current_cache_size = len ( self . _previous_parsed )
if _current_cache_size > self . _cache_size :
for i in range ( _current_cache_size - self . _cache_size ) :
del self . _previous_parsed [ self . _previous_parsed . keys ( ) [ 0 ] ]
2014-03-25 05:57:24 +00:00
2014-05-01 22:53:37 +00:00
def get ( self , name ) :
2014-05-11 14:17:11 +00:00
if name in self . _previous_parsed :
logger . log ( " Using cached parse result for: " + name , logger . DEBUG )
return self . _previous_parsed [ name ]
2014-03-10 05:18:05 +00:00
2014-03-25 05:57:24 +00:00
2014-03-10 05:18:05 +00:00
name_parser_cache = NameParserCache ( )
2014-03-25 05:57:24 +00:00
2014-03-10 05:18:05 +00:00
class InvalidNameException ( Exception ) :
" The given name is not valid "
2014-05-27 07:44:23 +00:00
class MultipleSceneShowResults ( Exception ) :
pass
class MultipleSceneEpisodeResults ( Exception ) :
pass