Added Black and White list for release groups for anime shows.

Fixed anime show list splitter.

Added check for anime shows and providers so we don't perform searches with a provider that doesn't support the type of show genre we are searching.
This commit is contained in:
echel0n 2014-05-27 00:44:23 -07:00
parent c217a4fc57
commit 34da3f53d8
19 changed files with 819 additions and 108 deletions

View file

@ -176,6 +176,25 @@
#if $anyQualities + $bestQualities #if $anyQualities + $bestQualities
<tr><td class="showLegend">Archive First Match: </td><td><img src="$sbRoot/images/#if int($show.archive_firstmatch) == 1 then "yes16.png\" alt=\"Y" else "no16.png\" alt=\"N"#" width="16" height="16" /></td></tr> <tr><td class="showLegend">Archive First Match: </td><td><img src="$sbRoot/images/#if int($show.archive_firstmatch) == 1 then "yes16.png\" alt=\"Y" else "no16.png\" alt=\"N"#" width="16" height="16" /></td></tr>
#end if #end if
#if $bwl.get_white_keywords_for("gloabl"):
<tr><td class="showLegend">Whitelist: </td><td>#echo ', '.join($bwl.get_white_keywords_for("gloabl"))#</td></tr>
#end if
#if $bwl.get_black_keywords_for("gloabl"):
<tr><td class="showLegend">Blacklist: </td><td>#echo ', '.join($bwl.get_black_keywords_for("gloabl"))#</td></tr>
#end if
#if $bwl.get_white_keywords_for("release_group"):
<tr>
<td class="showLegend">Wanted Group#if len($bwl.get_white_keywords_for("release_group"))>1 then "s" else ""#:</td>
<td>#echo ', '.join($bwl.get_white_keywords_for("release_group"))#</td>
</tr>
#end if
#if $bwl.get_black_keywords_for("release_group"):
<tr>
<td class="showLegend">Unwanted Group#if len($bwl.get_black_keywords_for("release_group"))>1 then "s" else ""#:</td>
<td>#echo ', '.join($bwl.get_black_keywords_for("release_group"))#</td>
</tr>
#end if
</table> </table>
</td> </td>
</tr> </tr>

View file

@ -1,4 +1,5 @@
#import sickbeard #import sickbeard
#import lib.adba as adba
#from sickbeard import common #from sickbeard import common
#from sickbeard import exceptions #from sickbeard import exceptions
#from sickbeard import scene_exceptions #from sickbeard import scene_exceptions
@ -139,6 +140,53 @@ Results without one of these words in the title will be filtered out <br />
Separate words with a comma, e.g. "word1,word2,word3" Separate words with a comma, e.g. "word1,word2,word3"
<br /><br /> <br /><br />
#if $show.is_anime
<p>
Realease Groups:
</p>
<input type="text" id="addToPoolText"/>
<input type="button" value="Add to White" id="addToWhite">
<input type="button" value="Add to Black" id="addToBlack"><br/>
<div class="blackwhiteliste white">
<span>White:</span>
<select multiple id="white">
#for $keyword in $whitelist:
<option value="$keyword">$keyword</option>
#end for
</select>
<br/>
<input id="removeW" value="Remove &gt;&gt;" type="button"/>
</div>
<div class="blackwhiteliste pool">
<span>Pool (Name|Rating|Subed Ep):</span>
<select multiple id="pool">
#for $group in $groups
#if $group not in $whitelist and $group['name'] not in $blacklist:
<option value="$group['name']">$group['name'] | $group['rating'] | $group['range']</option>
#end if
#end for
</select>
<br/>
<input id="addW" value="&lt;&lt; Add" type="button"/>
<input id="addB" value="Add &gt;&gt;" type="button"/>
</div>
<div class="blackwhiteliste black">
<span>Black:</span>
<select multiple id="black">
#for $keyword in $blacklist:
<option value="$keyword">$keyword</option>
#end for
</select>
<br/>
<input id="removeB" value="&lt;&lt; Remove" type="button"/>
</div>
<br style="clear:both;"/>
#end if
<input type="hidden" name="whitelist" id="whitelist"/>
<input type="hidden" name="blacklist" id="blacklist"/>
<input type="submit" id="submit" value="Submit" class="btn btn-primary" /> <input type="submit" id="submit" value="Submit" class="btn btn-primary" />
</form> </form>

View file

@ -98,7 +98,7 @@
\$(this).remove(); \$(this).remove();
}); });
\$("#showListTable:has(tbody tr)").tablesorter({ \$("#showListTableShows:has(tbody tr)").tablesorter({
sortList: [[6,1],[2,0]], sortList: [[6,1],[2,0]],
textExtraction: { textExtraction: {
@ -119,6 +119,26 @@
} }
}); });
\$("#showListTableAnime:has(tbody tr)").tablesorter({
sortList: [[6,1],[2,0]],
textExtraction: {
0: function(node) { return \$(node).find("span").text().toLowerCase(); },
#if ( $layout != 'simple'):
3: function(node) { return \$(node).find("img").attr("alt"); },
#end if
4: function(node) { return \$(node).find("span").text(); },
6: function(node) { return \$(node).find("img").attr("alt"); }
},
widgets: ['saveSort', 'zebra'],
headers: {
0: { sorter: 'cDate' },
2: { sorter: 'loadingNames' },
3: { sorter: 'network' },
4: { sorter: 'quality' },
5: { sorter: 'eps' },
}
});
}); });
//--> //-->
@ -138,7 +158,14 @@
</span> </span>
</div> </div>
<table id="showListTable" class="sickbeardTable tablesorter" cellspacing="1" border="0" cellpadding="0"> #for $curShowlist in $showlists:
#set $curListType = $curShowlist[0]
#set $myShowList = $list($curShowlist[1])
#if $curListType == "Anime":
<h2>Anime List</h2>
#end if
<table id="showListTable$curListType" class="sickbeardTable tablesorter" cellspacing="1" border="0" cellpadding="0">
<thead><tr><th class="nowrap">Next Ep</th>#if $layout=="poster" then "<th>Poster</th>" else "<th style='display: none;'></th>"#<th>Show</th><th>Network</th><th>Quality</th><th>Downloads</th><th>Active</th><th>Status</th></tr></thead> <thead><tr><th class="nowrap">Next Ep</th>#if $layout=="poster" then "<th>Poster</th>" else "<th style='display: none;'></th>"#<th>Show</th><th>Network</th><th>Quality</th><th>Downloads</th><th>Active</th><th>Status</th></tr></thead>
<tfoot> <tfoot>
@ -173,7 +200,6 @@
</tr> </tr>
#end for #end for
#set $myShowList = $list($sickbeard.showList)
$myShowList.sort(lambda x, y: cmp(x.name, y.name)) $myShowList.sort(lambda x, y: cmp(x.name, y.name))
#for $curShow in $myShowList: #for $curShow in $myShowList:
#set $curEp = $curShow.nextEpisode() #set $curEp = $curShow.nextEpisode()
@ -258,7 +284,7 @@ $myShowList.sort(lambda x, y: cmp(x.name, y.name))
//--> //-->
</script> </script>
</td> </td>
<td align="center"><img src="$sbRoot/images/#if int($curShow.paused) == 0 and $curShow.status != "Ended" then "yes16.png\" alt=\"Y\"" else "no16.png\" alt=\"N\""# width="16" height="16" /></td> <td align="center"><img src="$sbRoot/images/#if int($curShow.paused) == 0 and "Ended" not in $curShow.status then "yes16.png\" alt=\"Y\"" else "no16.png\" alt=\"N\""# width="16" height="16" /></td>
<td align="center" style="color: #555555; font-weight: bold;">$curShow.status</td> <td align="center" style="color: #555555; font-weight: bold;">$curShow.status</td>
</tr> </tr>
@ -266,5 +292,8 @@ $myShowList.sort(lambda x, y: cmp(x.name, y.name))
#end for #end for
</tbody> </tbody>
</table> </table>
#end for
<script type="text/javascript" src="$sbRoot/js/tableClick.js"></script>
#include $os.path.join($sickbeard.PROG_DIR,"gui/slick/interfaces/default/inc_bottom.tmpl") #include $os.path.join($sickbeard.PROG_DIR,"gui/slick/interfaces/default/inc_bottom.tmpl")

View file

@ -82,6 +82,8 @@
<!-- <th>Lang</th>//--> <!-- <th>Lang</th>//-->
<th class="nowrap" style="text-align: left;">Show Name</th> <th class="nowrap" style="text-align: left;">Show Name</th>
<th>Quality</th> <th>Quality</th>
<th>Sports</th>
<th>Anime</th>
<th>Flat Folders</th> <th>Flat Folders</th>
<th>Paused</th> <th>Paused</th>
<th>Status</th> <th>Status</th>
@ -141,7 +143,9 @@ $myShowList.sort(lambda x, y: cmp(x.name, y.name))
<td align="center"><span class="quality $qualityPresetStrings[$curShow.quality]">$qualityPresetStrings[$curShow.quality]</span></td> <td align="center"><span class="quality $qualityPresetStrings[$curShow.quality]">$qualityPresetStrings[$curShow.quality]</span></td>
#else: #else:
<td align="center"><span class="quality Custom">Custom</span></td> <td align="center"><span class="quality Custom">Custom</span></td>
#end if #end if
<td align="center"><img src="$sbRoot/images/#if int($curShow.is_sports) == 1 then "yes16.png\" alt=\"Y\"" else "no16.png\" alt=\"N\""# width="16" height="16" /></td>
<td align="center"><img src="$sbRoot/images/#if int($curShow.is_anime) == 1 then "yes16.png\" alt=\"Y\"" else "no16.png\" alt=\"N\""# width="16" height="16" /></td>
<td align="center"><img src="$sbRoot/images/#if int($curShow.flatten_folders) == 1 then "yes16.png\" alt=\"Y\"" else "no16.png\" alt=\"N\""# width="16" height="16" /></td> <td align="center"><img src="$sbRoot/images/#if int($curShow.flatten_folders) == 1 then "yes16.png\" alt=\"Y\"" else "no16.png\" alt=\"N\""# width="16" height="16" /></td>
<td align="center"><img src="$sbRoot/images/#if int($curShow.paused) == 1 then "yes16.png\" alt=\"Y\"" else "no16.png\" alt=\"N\""# width="16" height="16" /></td> <td align="center"><img src="$sbRoot/images/#if int($curShow.paused) == 1 then "yes16.png\" alt=\"Y\"" else "no16.png\" alt=\"N\""# width="16" height="16" /></td>
<td align="center">$curShow.status</td> <td align="center">$curShow.status</td>

View file

@ -93,6 +93,17 @@
</div><br /> </div><br />
</div> </div>
<div class="optionWrapper">
<span class="selectTitle">Anime</span>
<div class="selectChoices">
<select id="edit_anime" name="anime">
<option value="keep">&lt; keep &gt;</option>
<option value="enable" #if $anime_value then "selected=\"selected\"" else ""#>enable</option>
<option value="disable" #if $anime_value == False then "selected=\"selected\"" else ""#>disable</option>
</select>
</div><br />
</div>
<div class="optionWrapper"> <div class="optionWrapper">
<span class="selectTitle">Subtitles<span class="separator"></span></span> <span class="selectTitle">Subtitles<span class="separator"></span></span>
<div class="selectChoices"> <div class="selectChoices">

View file

@ -0,0 +1,210 @@
# Author: Dennis Lutter <lad1337@gmail.com>
# URL: http://code.google.com/p/sickbeard/
#
# This file is part of Sick Beard.
#
# Sick Beard is free software: you can redistribute it and/or modify
# 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.
#
# Sick Beard is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Sick Beard. If not, see <http://www.gnu.org/licenses/>.
from sickbeard import db, logger
class BlackAndWhiteList(object):
_tableBlack = "blacklist"
_tableWhite = "whitelist"
blackList = []
whiteList = []
blackDict = {}
whiteDict = {}
last_black_valid_result = None
last_white_valid_result = None
def __init__(self, show_id):
if not show_id:
raise BlackWhitelistNoShowIDException()
self.show_id = show_id
self.myDB = db.DBConnection()
self.refresh()
def refresh(self):
logger.log(u"Building black and white list for " + str(self.show_id), logger.DEBUG)
(self.blackList, self.blackDict) = self.load_blacklist()
(self.whiteList, self.whiteDict) = self.load_whitelist()
def load_blacklist(self):
return self._load_list(self._tableBlack)
def load_whitelist(self):
return self._load_list(self._tableWhite)
def get_black_keywords_for(self, range):
if range in self.blackDict:
return self.blackDict[range]
else:
return []
def get_white_keywords_for(self, range):
if range in self.whiteDict:
return self.whiteDict[range]
else:
return []
def set_black_keywords(self, range, values):
self._del_all_black_keywords()
self._add_keywords(self._tableBlack, range, values)
def set_white_keywords(self, range, values):
self._del_all_white_keywords()
self._add_keywords(self._tableWhite, range, values)
def set_black_keywords_for(self, range, values):
self._del_all_black_keywords_for(range)
self._add_keywords(self._tableBlack, range, values)
def set_white_keywords_for(self, range, values):
self._del_all_white_keywords_for(range)
self._add_keywords(self._tableWhite, range, values)
def add_black_keyword(self, range, value):
self._add_keywords(self._tableBlack, range, [value])
def add_white_keyword(self, range, value):
self._add_keywords(self._tableWhite, range, [value])
def get_last_result_msg(self):
blackResult = whiteResult = "Untested"
if self.last_black_valid_result == True:
blackResult = "Valid"
elif self.last_black_valid_result == False:
blackResult = "Invalid"
if self.last_white_valid_result == True:
whiteResult = "Valid"
elif self.last_white_valid_result == False:
whiteResult = "Invalid"
return "Blacklist: " + blackResult + ", Whitelist: " + whiteResult
def _add_keywords(self, table, range, values):
for value in values:
self.myDB.action("INSERT INTO " + table + " (show_id, range , keyword) VALUES (?,?,?)", [self.show_id, range, value])
self.refresh()
def _del_all_black_keywords(self):
self._del_all_keywords(self._tableBlack)
def _del_all_white_keywords(self):
self._del_all_keywords(self._tableWhite)
def _del_all_black_keywords_for(self, range):
self._del_all_keywords_for(self._tableBlack, range)
def _del_all_white_keywords_for(self, range):
self._del_all_keywords_for(self._tableWhite, range)
def _del_all_keywords(self, table):
logger.log(u"Deleting all " + table + " keywords for " + str(self.show_id), logger.DEBUG)
self.myDB.action("DELETE FROM " + table + " WHERE show_id = ?", [self.show_id])
self.refresh()
def _del_all_keywords_for(self, table, range):
logger.log(u"Deleting all " + range + " " + table + " keywords for " + str(self.show_id), logger.DEBUG)
self.myDB.action("DELETE FROM " + table + " WHERE show_id = ? and range = ?", [self.show_id, range])
self.refresh()
def _load_list(self, table):
sqlResults = self.myDB.select("SELECT range,keyword FROM " + table + " WHERE show_id = ? ", [self.show_id])
if not sqlResults or not len(sqlResults):
return ([], {})
list, dict = self._build_keyword_dict(sqlResults)
logger.log("BWL: " + str(self.show_id) + " loaded keywords from " + table + ": " + str(dict), logger.DEBUG)
return list, dict
def _build_keyword_dict(self, sql_result):
list = []
dict = {}
for row in sql_result:
list.append(row["keyword"])
if row["range"] in dict:
dict[row["range"]].append(row["keyword"])
else:
dict[row["range"]] = [row["keyword"]]
return (list, dict)
def is_valid_for_black(self, haystack):
logger.log(u"BWL: " + str(self.show_id) + " is valid black", logger.DEBUG)
result = self._is_valid_for(self.blackDict, False, haystack)
self.last_black_valid_result = result
return result
def is_valid_for_white(self, haystack):
logger.log(u"BWL: " + str(self.show_id) + " is valid white", logger.DEBUG)
result = self._is_valid_for(self.whiteDict, True, haystack)
self.last_white_valid_result = result
return result
def is_valid(self, haystack):
return self.is_valid_for_black(haystack) and self.is_valid_for_white(haystack)
def _is_valid_for(self, list, mood, haystack):
if not len(list):
return True
results = []
for range in list:
for keyword in list[range]:
string = None
if range == "global":
string = haystack.name
elif range in haystack.__dict__:
string = haystack.__dict__[range]
elif not range in haystack.__dict__:
results.append((not mood))
else:
results.append(False)
if string:
results.append(self._is_keyword_in_string(string, keyword) == mood)
# black: mood = False
# white: mood = True
if mood in results:
return mood
else:
return (not mood)
def _is_keyword_in_string(self, fromPost, fromBWList):
"""
will return true if fromBWList is found in fromPost
for now a basic find is used
"""
fromPost = fromPost.lower()
fromBWList = fromBWList.lower()
logger.log(u"BWL: " + str(self.show_id) + " comparing fromPost: " + fromPost + " vs fromBWlist: " + fromBWList, logger.DEBUG)
return (fromPost.find(fromBWList) >= 0)
class BlackWhiteKeyword(object):
range = ""
value = []
def __init__(self, range, values):
self.range = range # "global" or a parser group
self.value = values # a list of values may contain only one item (still a list)
class BlackWhitelistNoShowIDException(Exception):
"No show_id was given"

View file

@ -27,7 +27,7 @@ from sickbeard import encodingKludge as ek
from sickbeard.name_parser.parser import NameParser, InvalidNameException from sickbeard.name_parser.parser import NameParser, InvalidNameException
MIN_DB_VERSION = 9 # oldest db version we support migrating from MIN_DB_VERSION = 9 # oldest db version we support migrating from
MAX_DB_VERSION = 34 MAX_DB_VERSION = 36
class MainSanityCheck(db.DBSanityCheck): class MainSanityCheck(db.DBSanityCheck):
def check(self): def check(self):
@ -834,3 +834,30 @@ class AddSceneAbsoluteNumbering(AddAbsoluteNumbering):
self.addColumn("scene_numbering", "scene_absolute_number", "NUMERIC", "0") self.addColumn("scene_numbering", "scene_absolute_number", "NUMERIC", "0")
self.incDBVersion() self.incDBVersion()
class AddAnimeBlacklistWhitelist(AddSceneAbsoluteNumbering):
def test(self):
return self.checkDBVersion() >= 35
def execute(self):
backupDatabase(35)
ql = []
ql.append(["CREATE TABLE blacklist (show_id INTEGER, range TEXT, keyword TEXT)"])
ql.append(["CREATE TABLE whitelist (show_id INTEGER, range TEXT, keyword TEXT)"])
self.connection.mass_action(ql)
self.incDBVersion()
class AddSceneAbsoluteNumbering(AddAnimeBlacklistWhitelist):
def test(self):
return self.checkDBVersion() >= 36
def execute(self):
backupDatabase(36)
logger.log(u"Adding column scene_absolute_number to tv_episodes")
self.addColumn("tv_episodes", "scene_absolute_number", "NUMERIC", "0")
self.incDBVersion()

View file

@ -680,7 +680,6 @@ def is_anime_in_show_list():
def update_anime_support(): def update_anime_support():
sickbeard.ANIMESUPPORT = is_anime_in_show_list() sickbeard.ANIMESUPPORT = is_anime_in_show_list()
def get_all_episodes_from_absolute_number(show, indexer_id, absolute_numbers): def get_all_episodes_from_absolute_number(show, indexer_id, absolute_numbers):
if len(absolute_numbers) == 0: if len(absolute_numbers) == 0:
raise EpisodeNotFoundByAbsoluteNumberException() raise EpisodeNotFoundByAbsoluteNumberException()

View file

@ -24,7 +24,7 @@ import regexes
import time import time
import sickbeard import sickbeard
from sickbeard import logger, helpers, scene_numbering from sickbeard import logger, helpers, scene_numbering, db
from sickbeard.exceptions import EpisodeNotFoundByAbsoluteNumberException from sickbeard.exceptions import EpisodeNotFoundByAbsoluteNumberException
from dateutil import parser from dateutil import parser
@ -53,6 +53,7 @@ class NameParser(object):
self._compile_regexes(self.regexMode) self._compile_regexes(self.regexMode)
self.showList = sickbeard.showList self.showList = sickbeard.showList
self.useIndexers = useIndexers self.useIndexers = useIndexers
self.show = show
def clean_series_name(self, series_name): def clean_series_name(self, series_name):
"""Cleans up series name by removing any . and _ """Cleans up series name by removing any . and _
@ -194,7 +195,12 @@ class NameParser(object):
if 'release_group' in named_groups: if 'release_group' in named_groups:
result.release_group = match.group('release_group') result.release_group = match.group('release_group')
show = helpers.get_show_by_name(result.series_name, useIndexer=self.useIndexers) # determin show object for correct regex matching
if not self.show:
show = helpers.get_show_by_name(result.series_name, useIndexer=self.useIndexers)
else:
show = self.show
if show and show.is_anime and cur_regex_type in ['anime', 'normal']: if show and show.is_anime and cur_regex_type in ['anime', 'normal']:
result.show = show result.show = show
return result return result
@ -336,6 +342,140 @@ class NameParser(object):
return final_result return final_result
def scene2indexer(self, show, scene_name, season, episodes, absolute_numbers):
if not show: return self # need show object
# TODO: check if adb and make scene2indexer useable with correct numbers
out_season = None
out_episodes = []
out_absolute_numbers = []
# is the scene name a special season ?
# TODO: define if we get scene seasons or indexer seasons ... for now they are mostly the same ... and i will use them as scene seasons
_possible_seasons = sickbeard.scene_exceptions.get_scene_exception_by_name_multiple(scene_name)
# filter possible_seasons
possible_seasons = []
for cur_scene_indexer_id, cur_scene_season in _possible_seasons:
if cur_scene_indexer_id and str(cur_scene_indexer_id) != str(show.indexerid):
logger.log("Indexer ID mismatch: " + str(show.indexerid) + " now: " + str(cur_scene_indexer_id),
logger.ERROR)
raise MultipleSceneShowResults("indexerid mismatch")
# don't add season -1 since this is a generic name and not a real season... or if we get None
# if this was the only result possible_seasons will stay empty and the next parts will look in the general matter
if cur_scene_season == -1 or cur_scene_season == None:
continue
possible_seasons.append(cur_scene_season)
# if not possible_seasons: # no special season name was used or we could not find it
logger.log(
"possible seasons for '" + scene_name + "' (" + str(show.indexerid) + ") are " + str(possible_seasons),
logger.DEBUG)
# lets just get a db connection we will need it anyway
cacheDB = db.DBConnection('cache.db')
# should we use absolute_numbers -> anime or season, episodes -> normal show
if show.is_anime:
logger.log(
u"'" + show.name + "' is an anime i will scene convert the absolute numbers " + str(absolute_numbers),
logger.DEBUG)
if possible_seasons:
# check if we have a scene_absolute_number in the possible seasons
for cur_possible_season in possible_seasons:
# and for all absolute numbers
for cur_ab_number in absolute_numbers:
namesSQlResult = cacheDB.select(
"SELECT season, episode, absolute_number FROM xem_numbering WHERE indexer_id = ? and scene_season = ? and scene_absolute_number = ?",
[show.indexerid, cur_possible_season, cur_ab_number])
if len(namesSQlResult) > 1:
logger.log(
"Multiple episodes for a absolute number and season. check XEM numbering",
logger.ERROR)
raise MultipleSceneEpisodeResults("Multiple episodes for a absolute number and season")
elif len(namesSQlResult) == 0:
break # break out of current absolute_numbers -> next season ... this is not a good sign
# if we are here we found ONE episode for this season absolute number
# logger.log(u"I found matching episode: " + namesSQlResult[0]['name'], logger.DEBUG)
out_episodes.append(int(namesSQlResult[0]['episode']))
out_absolute_numbers.append(int(namesSQlResult[0]['absolute_number']))
out_season = int(namesSQlResult[0][
'season']) # note this will always use the last season we got ... this will be a problem on double episodes that break the season barrier
if out_season: # if we found a episode in the cur_possible_season we dont need / want to look at the other season possibilities
break
else: # no possible seasons from the scene names lets look at this more generic
for cur_ab_number in absolute_numbers:
namesSQlResult = cacheDB.select(
"SELECT season, episode, absolute_number FROM xem_numbering WHERE indexer_id = ? and scene_absolute_number = ?",
[show.indexerid, cur_ab_number])
if len(namesSQlResult) > 1:
logger.log(
"Multiple episodes for a absolute number. this might happend because we are missing a scene name for this season. xem lacking behind ?",
logger.ERROR)
raise MultipleSceneEpisodeResults("Multiple episodes for a absolute number")
elif len(namesSQlResult) == 0:
continue
# if we are here we found ONE episode for this season absolute number
# logger.log(u"I found matching episode: " + namesSQlResult[0]['name'], logger.DEBUG)
out_episodes.append(int(namesSQlResult[0]['episode']))
out_absolute_numbers.append(int(namesSQlResult[0]['absolute_number']))
out_season = int(namesSQlResult[0][
'season']) # note this will always use the last season we got ... this will be a problem on double episodes that break the season barrier
if not out_season: # we did not find anything in the loops ? damit there is no episode
logger.log("No episode found for these scene numbers. asuming indexer numbers", logger.DEBUG)
# we still have to convert the absolute number to sxxexx ... but that is done not here
else:
logger.log(u"'" + show.name + "' is a normal show i will scene convert the season and episodes " + str(
season) + "x" + str(episodes), logger.DEBUG)
out_absolute_numbers = None
if possible_seasons:
# check if we have a scene_absolute_number in the possible seasons
for cur_possible_season in possible_seasons:
# and for all episode
for cur_episode in episodes:
namesSQlResult = cacheDB.select(
"SELECT season, episode FROM xem_numbering WHERE indexer_id = ? and scene_season = ? and scene_episode = ?",
[show.indexerid, cur_possible_season, cur_episode])
if len(namesSQlResult) > 1:
logger.log(
"Multiple episodes for season episode number combination. this should not be check xem configuration",
logger.ERROR)
raise MultipleSceneEpisodeResults("Multiple episodes for season episode number combination")
elif len(namesSQlResult) == 0:
break # break out of current episode -> next season ... this is not a good sign
# if we are here we found ONE episode for this season absolute number
# logger.log(u"I found matching episode: " + namesSQlResult[0]['name'], logger.DEBUG)
out_episodes.append(int(namesSQlResult[0]['episode']))
out_season = int(namesSQlResult[0][
'season']) # note this will always use the last season we got ... this will be a problem on double episodes that break the season barrier
if out_season: # if we found a episode in the cur_possible_season we dont need / want to look at the other posibilites
break
else: # no possible seasons from the scene names lets look at this more generic
for cur_episode in episodes:
namesSQlResult = cacheDB.select(
"SELECT season, episode FROM xem_numbering WHERE indexer_id = ? and scene_episode = ? and scene_season = ?",
[show.indexerid, cur_episode, season])
if len(namesSQlResult) > 1:
logger.log(
"Multiple episodes for season episode number combination. this might happend because we are missing a scene name for this season. xem lacking behind ?",
logger.ERROR)
raise MultipleSceneEpisodeResults("Multiple episodes for season episode number combination")
elif len(namesSQlResult) == 0:
continue
# if we are here we found ONE episode for this season absolute number
# logger.log(u"I found matching episode: " + namesSQlResult[0]['name'], logger.DEBUG)
out_episodes.append(int(namesSQlResult[0]['episode']))
out_season = int(namesSQlResult[0][
'season']) # note this will always use the last season we got ... this will be a problem on double episodes that break the season barrier
# this is only done for normal shows
if not out_season: # we did not find anything in the loops ? darn there is no episode
logger.log("No episode found for these scene numbers. assuming these are valid indexer numbers",
logger.DEBUG)
out_season = season
out_episodes = episodes
out_absolute_numbers = absolute_numbers
# okay that was easy we found the correct season and episode numbers
return (out_season, out_episodes, out_absolute_numbers)
class ParseResult(object): class ParseResult(object):
def __init__(self, def __init__(self,
original_name, original_name,
@ -454,7 +594,8 @@ class ParseResult(object):
if len(self.ab_episode_numbers): if len(self.ab_episode_numbers):
abNo = self.ab_episode_numbers[i] abNo = self.ab_episode_numbers[i]
(s, e, a) = scene_numbering.get_indexer_numbering(self.show.indexerid, self.show.indexer, self.season_number, (s, e, a) = scene_numbering.get_indexer_numbering(self.show.indexerid, self.show.indexer,
self.season_number,
epNo, abNo) epNo, abNo)
new_episode_numbers.append(e) new_episode_numbers.append(e)
new_season_numbers.append(s) new_season_numbers.append(s)
@ -530,3 +671,11 @@ name_parser_cache = NameParserCache()
class InvalidNameException(Exception): class InvalidNameException(Exception):
"The given name is not valid" "The given name is not valid"
class MultipleSceneShowResults(Exception):
pass
class MultipleSceneEpisodeResults(Exception):
pass

View file

@ -39,6 +39,7 @@ class BTNProvider(generic.TorrentProvider):
generic.TorrentProvider.__init__(self, "BTN") generic.TorrentProvider.__init__(self, "BTN")
self.supportsBacklog = True self.supportsBacklog = True
self.supportsAbsoluteNumbering = True
self.enabled = False self.enabled = False
self.api_key = None self.api_key = None
@ -211,13 +212,14 @@ class BTNProvider(generic.TorrentProvider):
# Search for entire seasons: no need to do special things for air by date shows # Search for entire seasons: no need to do special things for air by date shows
whole_season_params = current_params.copy() whole_season_params = current_params.copy()
partial_season_params = current_params.copy()
# Search for entire seasons: no need to do special things for air by date shows # Search for entire seasons: no need to do special things for air by date shows
whole_season_params['category'] = 'Season' whole_season_params['category'] = 'Season'
if ep_obj.show.air_by_date or ep_obj.show.sports: if ep_obj.show.air_by_date or ep_obj.show.sports:
# Search for the year of the air by date show # Search for the year of the air by date show
whole_season_params['name'] = str(ep_obj.airdate).split('-')[0] whole_season_params['name'] = str(ep_obj.airdate).split('-')[0]
elif ep_obj.show.is_anime:
whole_season_params['name'] = "%d" % ep_obj.scene_absolute_number
else: else:
whole_season_params['name'] = 'Season ' + str(ep_obj.scene_season) whole_season_params['name'] = 'Season ' + str(ep_obj.scene_season)
@ -232,9 +234,9 @@ class BTNProvider(generic.TorrentProvider):
search_params = {'category': 'Episode'} search_params = {'category': 'Episode'}
if self.show.indexer == 1: if self.show.indexer == 1 and not self.show.is_anime:
search_params['tvdb'] = self.show.indexerid search_params['tvdb'] = self.show.indexerid
elif self.show.indexer == 2: elif self.show.indexer == 2 and not self.show.is_anime:
search_params['tvrage'] = self.show.indexerid search_params['tvrage'] = self.show.indexerid
else: else:
search_params['series'] = sanitizeSceneName(self.show.name) search_params['series'] = sanitizeSceneName(self.show.name)
@ -251,6 +253,8 @@ class BTNProvider(generic.TorrentProvider):
# BTN uses dots in dates, we just search for the date since that # BTN uses dots in dates, we just search for the date since that
# combined with the series identifier should result in just one episode # combined with the series identifier should result in just one episode
search_params['name'] = date_str.replace('-', '.') search_params['name'] = date_str.replace('-', '.')
elif self.show.is_anime:
search_params['name'] = "%i" % int(ep_obj.scene_absolute_number)
else: else:
# Do a general name search for the episode, formatted like SXXEYY # Do a general name search for the episode, formatted like SXXEYY
search_params['name'] = "S%02dE%02d" % (ep_obj.scene_season, ep_obj.scene_episode) search_params['name'] = "S%02dE%02d" % (ep_obj.scene_season, ep_obj.scene_episode)

View file

@ -17,6 +17,7 @@
# along with Sick Beard. If not, see <http://www.gnu.org/licenses/>. # along with Sick Beard. If not, see <http://www.gnu.org/licenses/>.
import urllib import urllib
import datetime
import sickbeard import sickbeard
import generic import generic
@ -36,6 +37,7 @@ class Fanzub(generic.NZBProvider):
self.supportsBacklog = False self.supportsBacklog = False
self.supportsAbsoluteNumbering = True self.supportsAbsoluteNumbering = True
self.anime_only = True
self.enabled = False self.enabled = False
@ -101,24 +103,21 @@ class Fanzub(generic.NZBProvider):
results = [] results = []
for i in [2, 3, 4]: # we will look for a version 2, 3 and 4 for i in [2, 3, 4]: # we will look for a version 2, 3 and 4
""" for item in self._doSearch("v" + str(i)):
because of this the proper search failed !!
well more precisly because _doSearch does not accept a dict rather then a string
params = {
"q":"v"+str(i).encode('utf-8')
}
"""
for curResult in self._doSearch("v" + str(i)):
match = re.search('(\w{3}, \d{1,2} \w{3} \d{4} \d\d:\d\d:\d\d) [\+\-]\d{4}', curResult.findtext('pubDate')) (title, url) = self._get_title_and_url(item)
if not match:
if item.has_key('published_parsed') and item['published_parsed']:
result_date = item.published_parsed
if result_date:
result_date = datetime.datetime(*result_date[0:6])
else:
logger.log(u"Unable to figure out the date for entry " + title + ", skipping it")
continue continue
dateString = match.group(1) if not date or result_date > date:
resultDate = parseDate(dateString).replace(tzinfo=None) search_result = classes.Proper(title, url, result_date)
results.append(search_result)
if date == None or resultDate > date:
results.append(classes.Proper(curResult.findtext('title'), curResult.findtext('link'), resultDate))
return results return results
@ -145,7 +144,7 @@ class FanzubCache(tvcache.TVCache):
return self.getRSSFeed(rss_url) return self.getRSSFeed(rss_url)
def _checkAuth(self, data): def _checkItemAuth(self, title, url):
return self.provider._checkAuthFromData(data) return True
provider = Fanzub() provider = Fanzub()

View file

@ -55,11 +55,12 @@ class GenericProvider:
self.supportsBacklog = False self.supportsBacklog = False
self.supportsAbsoluteNumbering = False self.supportsAbsoluteNumbering = False
self.anime_only = False
self.search_mode = None self.search_mode = None
self.search_fallback = False self.search_fallback = False
self.backlog_only = False self.backlog_only = False
self.cache = tvcache.TVCache(self) self.cache = tvcache.TVCache(self)
self.session = requests.session() self.session = requests.session()
@ -254,7 +255,7 @@ class GenericProvider:
u"Incomplete Indexer <-> Scene mapping detected for " + epObj.prettyName() + ", skipping search!") u"Incomplete Indexer <-> Scene mapping detected for " + epObj.prettyName() + ", skipping search!")
continue continue
#cacheResult = self.cache.searchCache([epObj], manualSearch) # cacheResult = self.cache.searchCache([epObj], manualSearch)
#if len(cacheResult): #if len(cacheResult):
# results.update({epObj.episode:cacheResult[epObj]}) # results.update({epObj.episode:cacheResult[epObj]})
# continue # continue
@ -275,7 +276,7 @@ class GenericProvider:
searchItems[epObj] = itemList searchItems[epObj] = itemList
# if we have cached results return them. # if we have cached results return them.
#if len(results): # if len(results):
# return results # return results
for ep_obj in searchItems: for ep_obj in searchItems:
@ -323,7 +324,7 @@ class GenericProvider:
continue continue
if (parse_result.air_by_date and parse_result.air_date != ep_obj.airdate) or ( if (parse_result.air_by_date and parse_result.air_date != ep_obj.airdate) or (
parse_result.sports and parse_result.sports_event_date != ep_obj.airdate): parse_result.sports and parse_result.sports_event_date != ep_obj.airdate):
logger.log("Episode " + title + " didn't air on " + str(ep_obj.airdate) + ", skipping it", logger.log("Episode " + title + " didn't air on " + str(ep_obj.airdate) + ", skipping it",
logger.DEBUG) logger.DEBUG)
continue continue

View file

@ -37,7 +37,7 @@ class NyaaProvider(generic.TorrentProvider):
self.supportsBacklog = True self.supportsBacklog = True
self.supportsAbsoluteNumbering = True self.supportsAbsoluteNumbering = True
self.anime_only = True
self.enabled = False self.enabled = False
self.ratio = None self.ratio = None
@ -60,9 +60,7 @@ class NyaaProvider(generic.TorrentProvider):
return generic.TorrentProvider.findSearchResults(self, show, season, episodes, search_mode, manualSearch) return generic.TorrentProvider.findSearchResults(self, show, season, episodes, search_mode, manualSearch)
def _get_season_search_strings(self, ep_obj): def _get_season_search_strings(self, ep_obj):
names = [] return show_name_helpers.makeSceneShowSearchStrings(self.show)
names.extend(show_name_helpers.makeSceneShowSearchStrings(self.show))
return names
def _get_episode_search_strings(self, ep_obj, add_string=''): def _get_episode_search_strings(self, ep_obj, add_string=''):
return self._get_season_search_strings(ep_obj) return self._get_season_search_strings(ep_obj)

View file

@ -68,7 +68,6 @@ def get_scene_numbering(indexer_id, indexer, season, episode, absolute_number=No
return xem_result return xem_result
return (season, episode, absolute_number) return (season, episode, absolute_number)
def find_scene_numbering(indexer_id, indexer, season, episode, absolute_number=None): def find_scene_numbering(indexer_id, indexer, season, episode, absolute_number=None):
""" """
Same as get_scene_numbering(), but returns None if scene numbering is not set Same as get_scene_numbering(), but returns None if scene numbering is not set
@ -400,7 +399,6 @@ def get_xem_numbering_for_season(indexer_id, indexer, season):
return result return result
def fix_scene_numbering(): def fix_scene_numbering():
ql = [] ql = []
@ -436,3 +434,40 @@ def fix_scene_numbering():
if ql: if ql:
myDB.mass_action(ql) myDB.mass_action(ql)
def get_ep_mapping(epObj, parse_result):
# scores
indexer_numbering = 0
scene_numbering = 0
absolute_numbering = 0
_possible_seasons = sickbeard.scene_exceptions.get_scene_exception_by_name_multiple(parse_result.series_name)
# indexer numbering
if epObj.season == parse_result.season_number:
indexer_numbering += 1
elif epObj.episode in parse_result.episode_numbers:
indexer_numbering += 1
# scene numbering
if epObj.scene_season == parse_result.season_number:
scene_numbering += 1
elif epObj.scene_episode in parse_result.episode_numbers:
scene_numbering += 1
# absolute numbering
if epObj.show.is_anime and parse_result.is_anime:
if epObj.absolute_number in parse_result.ab_episode_numbers:
absolute_numbering +=1
elif epObj.scene_absolute_number in parse_result.ab_episode_numbers:
absolute_numbering += 1
if indexer_numbering == 2:
print "indexer numbering"
elif scene_numbering == 2:
print "scene numbering"
elif absolute_numbering == 1:
print "indexer numbering"
else:
print "could not determin numbering"

View file

@ -42,7 +42,7 @@ from sickbeard import providers
from sickbeard import failed_history from sickbeard import failed_history
from sickbeard.exceptions import ex from sickbeard.exceptions import ex
from sickbeard.providers.generic import GenericProvider, tvcache from sickbeard.providers.generic import GenericProvider, tvcache
from sickbeard.blackandwhitelist import BlackAndWhiteList
def _downloadResult(result): def _downloadResult(result):
""" """
@ -197,11 +197,23 @@ def filter_release_name(name, filter_words):
def pickBestResult(results, show, quality_list=None): def pickBestResult(results, show, quality_list=None):
logger.log(u"Picking the best result out of " + str([x.name for x in results]), logger.DEBUG) logger.log(u"Picking the best result out of " + str([x.name for x in results]), logger.DEBUG)
# build the black And white list
bwl = None
if show:
bwl = BlackAndWhiteList(show.indexerid)
else:
logger.log("Could not create black and white list no show was given", logger.DEBUG)
# find the best result for the current episode # find the best result for the current episode
bestResult = None bestResult = None
for cur_result in results: for cur_result in results:
logger.log("Quality of " + cur_result.name + " is " + Quality.qualityStrings[cur_result.quality]) logger.log("Quality of " + cur_result.name + " is " + Quality.qualityStrings[cur_result.quality])
if bwl:
if not bwl.is_valid(cur_result):
logger.log(cur_result.name+" does not match the blacklist or the whitelist, rejecting it. Result: " + bwl.get_last_result_msg(), logger.MESSAGE)
continue
if quality_list and cur_result.quality not in quality_list: if quality_list and cur_result.quality not in quality_list:
logger.log(cur_result.name + " is a quality we know we don't want, rejecting it", logger.DEBUG) logger.log(cur_result.name + " is a quality we know we don't want, rejecting it", logger.DEBUG)
continue continue
@ -254,12 +266,18 @@ def isFinalResult(result):
show_obj = result.episodes[0].show show_obj = result.episodes[0].show
bwl = BlackAndWhiteList(show_obj.indexerid)
any_qualities, best_qualities = Quality.splitQuality(show_obj.quality) any_qualities, best_qualities = Quality.splitQuality(show_obj.quality)
# if there is a redownload that's higher than this then we definitely need to keep looking # if there is a redownload that's higher than this then we definitely need to keep looking
if best_qualities and result.quality < max(best_qualities): if best_qualities and result.quality < max(best_qualities):
return False return False
# if it does not match the shows black and white list its no good
elif not bwl.is_valid(result):
return False
# if there's no redownload that's higher (above) and this is the highest initial download then we're good # if there's no redownload that's higher (above) and this is the highest initial download then we're good
elif any_qualities and result.quality in any_qualities: elif any_qualities and result.quality in any_qualities:
return True return True
@ -317,7 +335,7 @@ def filterSearchResults(show, season, results):
return foundResults return foundResults
def searchForNeededEpisodes(episodes): def searchForNeededEpisodes(show, episodes):
foundResults = {} foundResults = {}
didSearch = False didSearch = False
@ -328,6 +346,10 @@ def searchForNeededEpisodes(episodes):
for curProviderCount, curProvider in enumerate(providers): for curProviderCount, curProvider in enumerate(providers):
threading.currentThread().name = origThreadName + " :: [" + curProvider.name + "]" threading.currentThread().name = origThreadName + " :: [" + curProvider.name + "]"
if curProvider.anime_only and not show.is_anime:
logger.log(u"" + str(show.name) + " is not an anime skiping ...")
continue
try: try:
logger.log(u"Updating RSS cache ...") logger.log(u"Updating RSS cache ...")
curProvider.cache.updateCache() curProvider.cache.updateCache()
@ -382,9 +404,10 @@ def searchProviders(show, season, episodes, manualSearch=False):
# check if we want to search for season packs instead of just season/episode # check if we want to search for season packs instead of just season/episode
seasonSearch = False seasonSearch = False
seasonEps = show.getAllEpisodes(season) if not manualSearch:
if len(seasonEps) == len(episodes): seasonEps = show.getAllEpisodes(season)
seasonSearch = True if len(seasonEps) == len(episodes):
seasonSearch = True
providers = [x for x in sickbeard.providers.sortedProviderList() if x.isActive()] providers = [x for x in sickbeard.providers.sortedProviderList() if x.isActive()]
@ -399,6 +422,10 @@ def searchProviders(show, season, episodes, manualSearch=False):
foundResults.setdefault(provider.name, {}) foundResults.setdefault(provider.name, {})
searchCount = 0 searchCount = 0
if provider.anime_only and not show.is_anime:
logger.log(u"" + str(show.name) + " is not an anime skiping ...")
continue
search_mode = 'eponly' search_mode = 'eponly'
if seasonSearch and provider.search_mode == 'sponly': if seasonSearch and provider.search_mode == 'sponly':
search_mode = provider.search_mode search_mode = provider.search_mode

View file

@ -97,7 +97,7 @@ class DailySearchQueueItem(generic_queue.QueueItem):
generic_queue.QueueItem.execute(self) generic_queue.QueueItem.execute(self)
logger.log("Beginning daily search for [" + self.show.name + "]") logger.log("Beginning daily search for [" + self.show.name + "]")
foundResults = search.searchForNeededEpisodes(self.segment) foundResults = search.searchForNeededEpisodes(self.show, self.segment)
# reset thread back to original name # reset thread back to original name
threading.currentThread().name = self.thread_name threading.currentThread().name = self.thread_name

View file

@ -351,6 +351,16 @@ class QueueItemAdd(ShowQueueItem):
logger.ERROR) logger.ERROR)
logger.log(traceback.format_exc(), logger.DEBUG) logger.log(traceback.format_exc(), logger.DEBUG)
# before we parse local files lets update exceptions
sickbeard.scene_exceptions.retrieve_exceptions()
# and get scene numbers
logger.log(u"Attempting to load scene numbers", logger.DEBUG)
if self.show.loadEpisodeSceneNumbers():
logger.log(u"loading scene numbers successfull", logger.DEBUG)
else:
logger.log(u"loading scene numbers NOT successfull or no scene numbers available", logger.DEBUG)
try: try:
self.show.loadEpisodesFromDir() self.show.loadEpisodesFromDir()
except Exception, e: except Exception, e:
@ -538,8 +548,13 @@ class QueueItemUpdate(ShowQueueItem):
except exceptions.EpisodeDeletedException: except exceptions.EpisodeDeletedException:
pass pass
sickbeard.showQueueScheduler.action.refreshShow(self.show, True) #@UndefinedVariable logger.log(u"Attempting to load scene numbers", logger.DEBUG)
if self.show.loadEpisodeSceneNumbers():
logger.log(u"loading scene numbers successfull", logger.DEBUG)
else:
logger.log(u"loading scene numbers NOT successfull or no scene numbers available", logger.DEBUG)
sickbeard.showQueueScheduler.action.refreshShow(self.show, True)
class QueueItemForceUpdate(QueueItemUpdate): class QueueItemForceUpdate(QueueItemUpdate):
def __init__(self, show=None): def __init__(self, show=None):

View file

@ -52,6 +52,7 @@ from common import DOWNLOADED, SNATCHED, SNATCHED_PROPER, SNATCHED_BEST, ARCHIVE
from common import NAMING_DUPLICATE, NAMING_EXTEND, NAMING_LIMITED_EXTEND, NAMING_SEPARATED_REPEAT, \ from common import NAMING_DUPLICATE, NAMING_EXTEND, NAMING_LIMITED_EXTEND, NAMING_SEPARATED_REPEAT, \
NAMING_LIMITED_EXTEND_E_PREFIXED NAMING_LIMITED_EXTEND_E_PREFIXED
class TVShow(object): class TVShow(object):
def __init__(self, indexer, indexerid, lang=""): def __init__(self, indexer, indexerid, lang=""):
@ -96,17 +97,19 @@ class TVShow(object):
self.loadFromDB() self.loadFromDB()
def _is_anime(self): def _is_anime(self):
if(self.anime > 0): if (self.anime > 0):
return True return True
else: else:
return False return False
is_anime = property(_is_anime) is_anime = property(_is_anime)
def _is_sports(self): def _is_sports(self):
if(self.sports > 0): if (self.sports > 0):
return True return True
else: else:
return False return False
is_sports = property(_is_sports) is_sports = property(_is_sports)
def _getLocation(self): def _getLocation(self):
@ -197,14 +200,25 @@ class TVShow(object):
if len(sqlResults) == 1: if len(sqlResults) == 1:
episode = int(sqlResults[0]["episode"]) episode = int(sqlResults[0]["episode"])
season = int(sqlResults[0]["season"]) season = int(sqlResults[0]["season"])
logger.log("Found episode by absolute_number:"+str(absolute_number)+" which is "+str(season)+"x"+str(episode), logger.DEBUG) logger.log(
"Found episode by absolute_number:" + str(absolute_number) + " which is " + str(season) + "x" + str(
episode), logger.DEBUG)
elif len(sqlResults) > 1: elif len(sqlResults) > 1:
logger.log("Multiple entries for absolute number: "+str(absolute_number)+" in show: "+self.name+" found ", logger.ERROR) logger.log("Multiple entries for absolute number: " + str(
absolute_number) + " in show: " + self.name + " found ", logger.ERROR)
return None return None
else: else:
logger.log("No entries for absolute number: "+str(absolute_number)+" in show: "+self.name+" found.", logger.DEBUG) logger.log(
"No entries for absolute number: " + str(absolute_number) + " in show: " + self.name + " found.",
logger.DEBUG)
return None return None
def createCurSeasonDict():
if not season in self.episodes:
self.episodes[season] = {}
createCurSeasonDict()
if not episode in self.episodes[season] or self.episodes[season][episode] == None: if not episode in self.episodes[season] or self.episodes[season][episode] == None:
if noCreate: if noCreate:
return None return None
@ -221,7 +235,6 @@ class TVShow(object):
self.episodes[season][episode] = ep self.episodes[season][episode] = ep
epObj = self.episodes[season][episode] epObj = self.episodes[season][episode]
epObj.convertToSceneNumbering()
return epObj return epObj
@ -264,7 +277,7 @@ class TVShow(object):
# in the first year after ended (last airdate), update every 30 days # in the first year after ended (last airdate), update every 30 days
# in the first year after ended (last airdate), update every 30 days # in the first year after ended (last airdate), update every 30 days
if (update_date - last_airdate) < datetime.timedelta(days=450) and ( if (update_date - last_airdate) < datetime.timedelta(days=450) and (
update_date - last_update_indexer) > datetime.timedelta(days=30): update_date - last_update_indexer) > datetime.timedelta(days=30):
return True return True
return False return False
@ -519,6 +532,33 @@ class TVShow(object):
return scannedEps return scannedEps
def loadEpisodeSceneNumbers(self):
epList = self.loadEpisodesFromDB()
sql_l = []
for curSeason in epList:
for curEp in epList[curSeason]:
epObj = self.getEpisode(curSeason, curEp)
with epObj.lock:
(epObj.scene_season, epObj.scene_episode, epObj.scene_absolute_number) = \
sickbeard.scene_numbering.get_scene_numbering(self.indexerid, self.indexer, epObj.season,
epObj.episode, epObj.absolute_number)
logger.log(
str(self.indexerid) + ": adding scene numbering. Indexer: " + str(epObj.season) + "x" + str(
epObj.episode) + "| Scene: " + str(epObj.scene_season) + "x" + str(epObj.scene_episode),
logger.DEBUG)
# mass add to database
if epObj.dirty:
sql_l.append(epObj.get_sql())
if len(sql_l) > 0:
myDB = db.DBConnection()
myDB.mass_action(sql_l)
return True
def getImages(self, fanart=None, poster=None): def getImages(self, fanart=None, poster=None):
fanart_result = poster_result = banner_result = False fanart_result = poster_result = banner_result = False
season_posters_result = season_banners_result = season_all_poster_result = season_all_banner_result = False season_posters_result = season_banners_result = season_all_poster_result = season_all_banner_result = False
@ -765,7 +805,7 @@ class TVShow(object):
if not self.imdbid: if not self.imdbid:
self.imdbid = sqlResults[0]["imdb_id"] self.imdbid = sqlResults[0]["imdb_id"]
#Get IMDb_info from database # Get IMDb_info from database
sqlResults = myDB.select("SELECT * FROM imdb_info WHERE indexer_id = ?", [self.indexerid]) sqlResults = myDB.select("SELECT * FROM imdb_info WHERE indexer_id = ?", [self.indexerid])
if len(sqlResults) == 0: if len(sqlResults) == 0:
@ -851,7 +891,7 @@ class TVShow(object):
else: else:
imdb_info[key] = imdbTv.get(key.replace('_', ' ')) imdb_info[key] = imdbTv.get(key.replace('_', ' '))
#Filter only the value # Filter only the value
if imdb_info['runtimes']: if imdb_info['runtimes']:
imdb_info['runtimes'] = re.search('\d+', imdb_info['runtimes']).group(0) imdb_info['runtimes'] = re.search('\d+', imdb_info['runtimes']).group(0)
else: else:
@ -862,13 +902,13 @@ class TVShow(object):
else: else:
imdb_info['akas'] = '' imdb_info['akas'] = ''
#Join all genres in a string # Join all genres in a string
if imdb_info['genres']: if imdb_info['genres']:
imdb_info['genres'] = '|'.join(imdb_info['genres']) imdb_info['genres'] = '|'.join(imdb_info['genres'])
else: else:
imdb_info['genres'] = '' imdb_info['genres'] = ''
#Get only the production country certificate if any # Get only the production country certificate if any
if imdb_info['certificates'] and imdb_info['countries']: if imdb_info['certificates'] and imdb_info['countries']:
dct = {} dct = {}
try: try:
@ -889,7 +929,7 @@ class TVShow(object):
imdb_info['last_update'] = datetime.date.today().toordinal() imdb_info['last_update'] = datetime.date.today().toordinal()
#Rename dict keys without spaces for DB upsert # Rename dict keys without spaces for DB upsert
self.imdb_info = dict( self.imdb_info = dict(
(k.replace(' ', '_'), k(v) if hasattr(v, 'keys') else v) for k, v in imdb_info.items()) (k.replace(' ', '_'), k(v) if hasattr(v, 'keys') else v) for k, v in imdb_info.items())
logger.log(str(self.indexerid) + u": Obtained info from IMDb ->" + str(self.imdb_info), logger.DEBUG) logger.log(str(self.indexerid) + u": Obtained info from IMDb ->" + str(self.imdb_info), logger.DEBUG)
@ -980,7 +1020,8 @@ class TVShow(object):
# if it used to have a file associated with it and it doesn't anymore then set it to IGNORED # if it used to have a file associated with it and it doesn't anymore then set it to IGNORED
if curEp.location and curEp.status in Quality.DOWNLOADED: if curEp.location and curEp.status in Quality.DOWNLOADED:
logger.log(str(self.indexerid) + u": Location for " + str(season) + "x" + str( logger.log(str(self.indexerid) + u": Location for " + str(season) + "x" + str(
episode) + " doesn't exist, removing it and changing our status to IGNORED", logger.DEBUG) episode) + " doesn't exist, removing it and changing our status to IGNORED",
logger.DEBUG)
curEp.status = IGNORED curEp.status = IGNORED
curEp.subtitles = list() curEp.subtitles = list()
curEp.subtitles_searchcount = 0 curEp.subtitles_searchcount = 0
@ -1008,19 +1049,20 @@ class TVShow(object):
hr = (12 + hr, hr)[None is airs.group(3)] hr = (12 + hr, hr)[None is airs.group(3)]
min = int((airs.group(2), min)[None is airs.group(2)]) min = int((airs.group(2), min)[None is airs.group(2)])
airtime = datetime.time(hr, min) airtime = datetime.time(hr, min)
airdatetime = datetime.datetime.combine(ep_obj.airdate, airtime) airdatetime = datetime.datetime.combine(ep_obj.airdate, airtime)
filemtime = datetime.datetime.fromtimestamp(os.path.getmtime(ep_obj.location)) filemtime = datetime.datetime.fromtimestamp(os.path.getmtime(ep_obj.location))
if filemtime != airdatetime: if filemtime != airdatetime:
import time import time
airdatetime = airdatetime.timetuple() airdatetime = airdatetime.timetuple()
if self.touch(ep_obj.location, time.mktime(airdatetime)): if self.touch(ep_obj.location, time.mktime(airdatetime)):
logger.log(str(self.indexerid) + u": Changed modify date of " + os.path.basename(ep_obj.location) logger.log(str(self.indexerid) + u": Changed modify date of " + os.path.basename(ep_obj.location)
+ " to show air date " + time.strftime("%b %d,%Y (%H:%M)", airdatetime)) + " to show air date " + time.strftime("%b %d,%Y (%H:%M)", airdatetime))
def touch(self, fname, atime = None): def touch(self, fname, atime=None):
if None != atime: if None != atime:
try: try:
@ -1034,7 +1076,7 @@ class TVShow(object):
return False return False
def downloadSubtitles(self, force=False): def downloadSubtitles(self, force=False):
#TODO: Add support for force option # TODO: Add support for force option
if not ek.ek(os.path.isdir, self._location): if not ek.ek(os.path.isdir, self._location):
logger.log(str(self.indexerid) + ": Show dir doesn't exist, can't download subtitles", logger.DEBUG) logger.log(str(self.indexerid) + ": Show dir doesn't exist, can't download subtitles", logger.DEBUG)
return return
@ -1224,8 +1266,8 @@ class TVEpisode(object):
self._season = season self._season = season
self._episode = episode self._episode = episode
self._absolute_number = 0 self._absolute_number = 0
self._scene_season = season self._scene_season = 0
self._scene_episode = episode self._scene_episode = 0
self._scene_absolute_number = 0 self._scene_absolute_number = 0
self._description = "" self._description = ""
self._subtitles = list() self._subtitles = list()
@ -1274,7 +1316,7 @@ class TVEpisode(object):
status = property(lambda self: self._status, dirty_setter("_status")) status = property(lambda self: self._status, dirty_setter("_status"))
indexer = property(lambda self: self._indexer, dirty_setter("_indexer")) indexer = property(lambda self: self._indexer, dirty_setter("_indexer"))
indexerid = property(lambda self: self._indexerid, dirty_setter("_indexerid")) indexerid = property(lambda self: self._indexerid, dirty_setter("_indexerid"))
#location = property(lambda self: self._location, dirty_setter("_location")) # location = property(lambda self: self._location, dirty_setter("_location"))
file_size = property(lambda self: self._file_size, dirty_setter("_file_size")) file_size = property(lambda self: self._file_size, dirty_setter("_file_size"))
release_name = property(lambda self: self._release_name, dirty_setter("_release_name")) release_name = property(lambda self: self._release_name, dirty_setter("_release_name"))
is_proper = property(lambda self: self._is_proper, dirty_setter("_is_proper")) is_proper = property(lambda self: self._is_proper, dirty_setter("_is_proper"))
@ -1282,7 +1324,7 @@ class TVEpisode(object):
def _set_location(self, new_location): def _set_location(self, new_location):
logger.log(u"Setter sets location to " + new_location, logger.DEBUG) logger.log(u"Setter sets location to " + new_location, logger.DEBUG)
#self._location = newLocation # self._location = newLocation
dirty_setter("_location")(self, new_location) dirty_setter("_location")(self, new_location)
if new_location and ek.ek(os.path.isfile, new_location): if new_location and ek.ek(os.path.isfile, new_location):
@ -1297,7 +1339,7 @@ class TVEpisode(object):
self.subtitles = subtitles.subtitlesLanguages(self.location) self.subtitles = subtitles.subtitlesLanguages(self.location)
def downloadSubtitles(self, force=False): def downloadSubtitles(self, force=False):
#TODO: Add support for force option # TODO: Add support for force option
if not ek.ek(os.path.isfile, self.location): if not ek.ek(os.path.isfile, self.location):
logger.log( logger.log(
str(self.show.indexerid) + ": Episode file doesn't exist, can't download subtitles for episode " + str( str(self.show.indexerid) + ": Episode file doesn't exist, can't download subtitles for episode " + str(
@ -1337,7 +1379,7 @@ class TVEpisode(object):
return return
self.refreshSubtitles() self.refreshSubtitles()
self.subtitles_searchcount = self.subtitles_searchcount + 1 if self.subtitles_searchcount else 1 #added the if because sometime it raise an error self.subtitles_searchcount = self.subtitles_searchcount + 1 if self.subtitles_searchcount else 1 # added the if because sometime it raise an error
self.subtitles_lastsearch = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") self.subtitles_lastsearch = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
self.saveToDB() self.saveToDB()
@ -1434,7 +1476,7 @@ class TVEpisode(object):
self.episode) + " not found in the database", logger.DEBUG) self.episode) + " not found in the database", logger.DEBUG)
return False return False
else: else:
#NAMEIT logger.log(u"AAAAA from" + str(self.season)+"x"+str(self.episode) + " -" + self.name + " to " + str(sqlResults[0]["name"])) # NAMEIT logger.log(u"AAAAA from" + str(self.season)+"x"+str(self.episode) + " -" + self.name + " to " + str(sqlResults[0]["name"]))
if sqlResults[0]["name"]: if sqlResults[0]["name"]:
self.name = sqlResults[0]["name"] self.name = sqlResults[0]["name"]
@ -1449,7 +1491,7 @@ class TVEpisode(object):
self.subtitles_searchcount = sqlResults[0]["subtitles_searchcount"] self.subtitles_searchcount = sqlResults[0]["subtitles_searchcount"]
self.subtitles_lastsearch = sqlResults[0]["subtitles_lastsearch"] self.subtitles_lastsearch = sqlResults[0]["subtitles_lastsearch"]
self.airdate = datetime.date.fromordinal(int(sqlResults[0]["airdate"])) self.airdate = datetime.date.fromordinal(int(sqlResults[0]["airdate"]))
#logger.log(u"1 Status changes from " + str(self.status) + " to " + str(sqlResults[0]["status"]), logger.DEBUG) # logger.log(u"1 Status changes from " + str(self.status) + " to " + str(sqlResults[0]["status"]), logger.DEBUG)
self.status = int(sqlResults[0]["status"]) self.status = int(sqlResults[0]["status"])
# don't overwrite my location # don't overwrite my location
@ -1463,12 +1505,31 @@ class TVEpisode(object):
self.indexerid = int(sqlResults[0]["indexerid"]) self.indexerid = int(sqlResults[0]["indexerid"])
self.indexer = int(sqlResults[0]["indexer"]) self.indexer = int(sqlResults[0]["indexer"])
# does one now a better way to test for NULL in the db field ?
if sqlResults[0]["scene_season"]:
self.scene_season = int(sqlResults[0]["scene_season"])
if sqlResults[0]["scene_episode"]:
self.scene_episode = int(sqlResults[0]["scene_episode"])
if sqlResults[0]["scene_absolute_number"]:
self.scene_absolute_number = int(sqlResults[0]["scene_absolute_number"])
if sqlResults[0]["release_name"] is not None: if sqlResults[0]["release_name"] is not None:
self.release_name = sqlResults[0]["release_name"] self.release_name = sqlResults[0]["release_name"]
if sqlResults[0]["is_proper"]: if sqlResults[0]["is_proper"]:
self.is_proper = int(sqlResults[0]["is_proper"]) self.is_proper = int(sqlResults[0]["is_proper"])
if self.scene_season == 0 or self.scene_episode == 0 or self.scene_absolute_number == 0:
(self.scene_season, self.scene_episode, self.scene_absolute_number) = \
sickbeard.scene_numbering.get_scene_numbering(
self.show.indexerid,
self.show.indexer,
self.season,
self.episode,
self.absolute_number)
self.dirty = False self.dirty = False
return True return True
@ -1534,11 +1595,14 @@ class TVEpisode(object):
return False return False
if myEp["absolute_number"] == None or myEp["absolute_number"] == "": if myEp["absolute_number"] == None or myEp["absolute_number"] == "":
logger.log(u"This episode ("+self.show.name+" - "+str(season)+"x"+str(episode)+") has no absolute number on " + sickbeard.indexerApi( logger.log(u"This episode (" + self.show.name + " - " + str(season) + "x" + str(
episode) + ") has no absolute number on " + sickbeard.indexerApi(
self.indexer).name self.indexer).name
, logger.DEBUG) , logger.DEBUG)
else: else:
logger.log(str(self.show.indexerid) + ": The absolute_number for " + str(season) + "x" + str(episode)+" is : "+myEp["absolute_number"], logger.DEBUG) logger.log(
str(self.show.indexerid) + ": The absolute_number for " + str(season) + "x" + str(episode) + " is : " +
myEp["absolute_number"], logger.DEBUG)
self.absolute_number = int(myEp["absolute_number"]) self.absolute_number = int(myEp["absolute_number"])
self.name = getattr(myEp, 'episodename', "") self.name = getattr(myEp, 'episodename', "")
@ -1563,7 +1627,7 @@ class TVEpisode(object):
self.deleteEpisode() self.deleteEpisode()
return False return False
#early conversion to int so that episode doesn't get marked dirty # early conversion to int so that episode doesn't get marked dirty
self.indexerid = getattr(myEp, 'id', None) self.indexerid = getattr(myEp, 'id', None)
if self.indexerid is None: if self.indexerid is None:
logger.log(u"Failed to retrieve ID from " + sickbeard.indexerApi(self.indexer).name, logger.ERROR) logger.log(u"Failed to retrieve ID from " + sickbeard.indexerApi(self.indexer).name, logger.ERROR)
@ -1571,7 +1635,7 @@ class TVEpisode(object):
self.deleteEpisode() self.deleteEpisode()
return False return False
#don't update show status if show dir is missing, unless missing show dirs are created during post-processing # don't update show status if show dir is missing, unless missing show dirs are created during post-processing
if not ek.ek(os.path.isdir, self.show._location) and not sickbeard.CREATE_MISSING_SHOW_DIRS: if not ek.ek(os.path.isdir, self.show._location) and not sickbeard.CREATE_MISSING_SHOW_DIRS:
logger.log( logger.log(
u"The show dir is missing, not bothering to change the episode statuses since it'd probably be invalid") u"The show dir is missing, not bothering to change the episode statuses since it'd probably be invalid")
@ -1653,7 +1717,7 @@ class TVEpisode(object):
showXML = etree.ElementTree(file=nfoFile) showXML = etree.ElementTree(file=nfoFile)
except (SyntaxError, ValueError), e: except (SyntaxError, ValueError), e:
logger.log(u"Error loading the NFO, backing up the NFO and skipping for now: " + ex(e), logger.log(u"Error loading the NFO, backing up the NFO and skipping for now: " + ex(e),
logger.ERROR) #TODO: figure out what's wrong and fix it logger.ERROR) # TODO: figure out what's wrong and fix it
try: try:
ek.ek(os.rename, nfoFile, nfoFile + ".old") ek.ek(os.rename, nfoFile, nfoFile + ".old")
except Exception, e: except Exception, e:
@ -1777,12 +1841,13 @@ class TVEpisode(object):
# use a custom update/insert method to get the data into the DB # use a custom update/insert method to get the data into the DB
return [ return [
"INSERT OR REPLACE INTO tv_episodes (episode_id, indexerid, indexer, name, description, subtitles, subtitles_searchcount, subtitles_lastsearch, airdate, hasnfo, hastbn, status, location, file_size, release_name, is_proper, showid, season, episode, absolute_number) VALUES " "INSERT OR REPLACE INTO tv_episodes (episode_id, indexerid, indexer, name, description, subtitles, subtitles_searchcount, subtitles_lastsearch, airdate, hasnfo, hastbn, status, location, file_size, release_name, is_proper, showid, season, episode, scene_season, scene_episode, absolute_number, scene_absolute_number) VALUES "
"((SELECT episode_id FROM tv_episodes WHERE showid = ? AND season = ? AND episode = ?),?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);", "((SELECT episode_id FROM tv_episodes WHERE showid = ? AND season = ? AND episode = ?),?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);",
[self.show.indexerid, self.season, self.episode, self.indexerid, self.indexer, self.name, self.description, [self.show.indexerid, self.season, self.episode, self.indexerid, self.indexer, self.name, self.description,
",".join([sub for sub in self.subtitles]), self.subtitles_searchcount, self.subtitles_lastsearch, ",".join([sub for sub in self.subtitles]), self.subtitles_searchcount, self.subtitles_lastsearch,
self.airdate.toordinal(), self.hasnfo, self.hastbn, self.status, self.location, self.file_size, self.airdate.toordinal(), self.hasnfo, self.hastbn, self.status, self.location, self.file_size,
self.release_name, self.is_proper, self.show.indexerid, self.season, self.episode, self.absolute_number]] self.release_name, self.is_proper, self.show.indexerid, self.season, self.episode, self.scene_season,
self.scene_episode, self.absolute_number, self.scene_absolute_number]]
def saveToDB(self, forceSave=False): def saveToDB(self, forceSave=False):
""" """
@ -1817,8 +1882,11 @@ class TVEpisode(object):
"file_size": self.file_size, "file_size": self.file_size,
"release_name": self.release_name, "release_name": self.release_name,
"is_proper": self.is_proper, "is_proper": self.is_proper,
"absolute_number": self.absolute_number "scene_season": self.scene_season,
} "scene_episode": self.scene_episode,
"absolute_number": self.absolute_number,
"scene_absolute_number": self.scene_absolute_number
}
controlValueDict = {"showid": self.show.indexerid, controlValueDict = {"showid": self.show.indexerid,
"season": self.season, "season": self.season,
"episode": self.episode} "episode": self.episode}
@ -1840,16 +1908,7 @@ class TVEpisode(object):
Returns: A string representing the episode's name and season/ep numbers Returns: A string representing the episode's name and season/ep numbers
""" """
return self._format_pattern('%SN - %Sx%0E - %EN') return self._format_pattern('Indexer#:[%SN - %Sx%0E - %EN] | Scene#:[%SN - %XSx%0XE - %EN]')
def prettySceneName(self):
"""
Returns the name of this episode in a "pretty" human-readable format. Used for logging
and notifications and such.
Returns: A string representing the episode's name and season/ep numbers
"""
return self._format_pattern('%SN - %XSx%0XE - %EN')
def _ep_name(self): def _ep_name(self):
""" """
@ -1954,7 +2013,7 @@ class TVEpisode(object):
'%0XS': '%02d' % self.scene_season, '%0XS': '%02d' % self.scene_season,
'%XE': str(self.scene_episode), '%XE': str(self.scene_episode),
'%0XE': '%02d' % self.scene_episode, '%0XE': '%02d' % self.scene_episode,
'%AN': '%03d' % self.absolute_number, '%AN': '%(#)03d' % {'#': self.absolute_number},
'%RN': release_name(self.release_name), '%RN': release_name(self.release_name),
'%RG': release_group(self.release_name), '%RG': release_group(self.release_name),
'%AD': str(self.airdate).replace('-', ' '), '%AD': str(self.airdate).replace('-', ' '),
@ -2004,6 +2063,9 @@ class TVEpisode(object):
if self.show.air_by_date or self.show.sports: if self.show.air_by_date or self.show.sports:
result_name = result_name.replace('%RN', '%S.N.%A.D.%E.N-SiCKRAGE') result_name = result_name.replace('%RN', '%S.N.%A.D.%E.N-SiCKRAGE')
result_name = result_name.replace('%rn', '%s.n.%A.D.%e.n-sickrage') result_name = result_name.replace('%rn', '%s.n.%A.D.%e.n-sickrage')
elif self.show.is_anime:
result_name = result_name.replace('%RN', '%S.N.%AN.%E.N-SiCKRAGE')
result_name = result_name.replace('%rn', '%s.n.%an.%e.n-sickrage')
else: else:
result_name = result_name.replace('%RN', '%S.N.S%0SE%0E.%E.N-SiCKRAGE') result_name = result_name.replace('%RN', '%S.N.S%0SE%0E.%E.N-SiCKRAGE')
result_name = result_name.replace('%rn', '%s.n.s%0se%0e.%e.n-sickrage') result_name = result_name.replace('%rn', '%s.n.s%0se%0e.%e.n-sickrage')
@ -2099,8 +2161,8 @@ class TVEpisode(object):
# fill out the template for this piece and then insert this piece into the actual pattern # fill out the template for this piece and then insert this piece into the actual pattern
cur_name_group_result = re.sub('(?i)(?x)' + regex_used, regex_replacement, cur_name_group) cur_name_group_result = re.sub('(?i)(?x)' + regex_used, regex_replacement, cur_name_group)
#cur_name_group_result = cur_name_group.replace(ep_format, ep_string) # cur_name_group_result = cur_name_group.replace(ep_format, ep_string)
#logger.log(u"found "+ep_format+" as the ep pattern using "+regex_used+" and replaced it with "+regex_replacement+" to result in "+cur_name_group_result+" from "+cur_name_group, logger.DEBUG) # logger.log(u"found "+ep_format+" as the ep pattern using "+regex_used+" and replaced it with "+regex_replacement+" to result in "+cur_name_group_result+" from "+cur_name_group, logger.DEBUG)
result_name = result_name.replace(cur_name_group, cur_name_group_result) result_name = result_name.replace(cur_name_group, cur_name_group_result)
result_name = self._format_string(result_name, replace_map) result_name = self._format_string(result_name, replace_map)
@ -2202,7 +2264,8 @@ class TVEpisode(object):
self.location) self.location)
if self.show.subtitles and sickbeard.SUBTITLES_DIR != '': if self.show.subtitles and sickbeard.SUBTITLES_DIR != '':
related_subs = postProcessor.PostProcessor(self.location).list_associated_files(sickbeard.SUBTITLES_DIR, subtitles_only=True) related_subs = postProcessor.PostProcessor(self.location).list_associated_files(sickbeard.SUBTITLES_DIR,
subtitles_only=True)
absolute_proper_subs_path = ek.ek(os.path.join, sickbeard.SUBTITLES_DIR, self.formatted_filename()) absolute_proper_subs_path = ek.ek(os.path.join, sickbeard.SUBTITLES_DIR, self.formatted_filename())
logger.log(u"Files associated to " + self.location + ": " + str(related_files), logger.DEBUG) logger.log(u"Files associated to " + self.location + ": " + str(related_files), logger.DEBUG)
@ -2218,7 +2281,8 @@ class TVEpisode(object):
logger.log(str(self.indexerid) + u": Unable to rename file " + cur_related_file, logger.ERROR) logger.log(str(self.indexerid) + u": Unable to rename file " + cur_related_file, logger.ERROR)
for cur_related_sub in related_subs: for cur_related_sub in related_subs:
cur_result = helpers.rename_ep_file(cur_related_sub, absolute_proper_subs_path,absolute_current_path_no_ext_length) cur_result = helpers.rename_ep_file(cur_related_sub, absolute_proper_subs_path,
absolute_current_path_no_ext_length)
if not cur_result: if not cur_result:
logger.log(str(self.indexerid) + u": Unable to rename file " + cur_related_sub, logger.ERROR) logger.log(str(self.indexerid) + u": Unable to rename file " + cur_related_sub, logger.ERROR)
@ -2240,15 +2304,17 @@ class TVEpisode(object):
relEp.saveToDB() relEp.saveToDB()
def convertToSceneNumbering(self): def convertToSceneNumbering(self):
(self.scene_season, self.scene_episode, self.scene_absolute_number) = sickbeard.scene_numbering.get_scene_numbering(self.show.indexerid, (self.scene_season, self.scene_episode,
self.show.indexer, self.scene_absolute_number) = sickbeard.scene_numbering.get_scene_numbering(self.show.indexerid,
self.season, self.show.indexer,
self.episode, self.season,
self.absolute_number) self.episode,
self.absolute_number)
def convertToIndexerNumbering(self): def convertToIndexerNumbering(self):
(self.season, self.episode, self.absolute_number) = sickbeard.scene_numbering.get_indexer_numbering(self.show.indexerid, (self.season, self.episode, self.absolute_number) = sickbeard.scene_numbering.get_indexer_numbering(
self.show.indexer, self.show.indexerid,
self.scene_season, self.show.indexer,
self.scene_episode, self.scene_season,
self.scene_absolute_number) self.scene_episode,
self.scene_absolute_number)

View file

@ -60,6 +60,8 @@ from sickbeard.scene_exceptions import get_scene_exceptions
from sickbeard.scene_numbering import get_scene_numbering, set_scene_numbering, get_scene_numbering_for_show, \ from sickbeard.scene_numbering import get_scene_numbering, set_scene_numbering, get_scene_numbering_for_show, \
get_xem_numbering_for_show get_xem_numbering_for_show
from sickbeard.blackandwhitelist import BlackAndWhiteList
from lib.dateutil import tz from lib.dateutil import tz
from lib.unrar2 import RarFile, RarInfo from lib.unrar2 import RarFile, RarInfo
@ -3042,6 +3044,8 @@ class Home:
else: else:
t.sortedShowLists = [["Shows",sorted(sickbeard.showList, lambda x, y: cmp(titler(x.name), titler(y.name)))]] t.sortedShowLists = [["Shows",sorted(sickbeard.showList, lambda x, y: cmp(titler(x.name), titler(y.name)))]]
t.bwl = BlackAndWhiteList(showObj.indexerid)
t.epCounts = epCounts t.epCounts = epCounts
t.epCats = epCats t.epCats = epCats
@ -3077,7 +3081,7 @@ class Home:
def editShow(self, show=None, location=None, anyQualities=[], bestQualities=[], exceptions_list=[], def editShow(self, show=None, location=None, anyQualities=[], bestQualities=[], exceptions_list=[],
flatten_folders=None, paused=None, directCall=False, air_by_date=None, sports=None, dvdorder=None, flatten_folders=None, paused=None, directCall=False, air_by_date=None, sports=None, dvdorder=None,
indexerLang=None, subtitles=None, archive_firstmatch=None, rls_ignore_words=None, indexerLang=None, subtitles=None, archive_firstmatch=None, rls_ignore_words=None,
rls_require_words=None, anime=None): rls_require_words=None, anime=None, blackWords=None, whiteWords=None, blacklist=None, whitelist=None):
if show is None: if show is None:
errString = "Invalid show ID: " + str(show) errString = "Invalid show ID: " + str(show)
@ -3101,7 +3105,24 @@ class Home:
t = PageTemplate(file="editShow.tmpl") t = PageTemplate(file="editShow.tmpl")
t.submenu = HomeMenu() t.submenu = HomeMenu()
bwl = BlackAndWhiteList(showObj.indexerid)
t.whiteWords = ""
if "global" in bwl.whiteDict:
t.whiteWords = ", ".join(bwl.whiteDict["global"])
t.blackWords = ""
if "global" in bwl.blackDict:
t.blackWords = ", ".join(bwl.blackDict["global"])
if showObj.is_anime: if showObj.is_anime:
t.whitelist = []
if bwl.whiteDict.has_key("release_group"):
t.whitelist = bwl.whiteDict["release_group"]
t.blacklist = []
if bwl.blackDict.has_key("release_group"):
t.blacklist = bwl.blackDict["release_group"]
t.groups = [] t.groups = []
if helpers.set_up_anidb_connection(): if helpers.set_up_anidb_connection():
anime = adba.Anime(sickbeard.ADBA_CONNECTION, name=showObj.name) anime = adba.Anime(sickbeard.ADBA_CONNECTION, name=showObj.name)
@ -3151,6 +3172,55 @@ class Home:
else: else:
do_update_exceptions = True do_update_exceptions = True
bwl = BlackAndWhiteList(showObj.indexerid)
if whitelist:
whitelist = whitelist.split(",")
shortWhiteList = []
if helpers.set_up_anidb_connection():
for groupName in whitelist:
group = sickbeard.ADBA_CONNECTION.group(gname=groupName)
for line in group.datalines:
if line["shortname"]:
shortWhiteList.append(line["shortname"])
else:
if not groupName in shortWhiteList:
shortWhiteList.append(groupName)
else:
shortWhiteList = whitelist
bwl.set_white_keywords_for("release_group", shortWhiteList)
else:
bwl.set_white_keywords_for("release_group", [])
if blacklist:
blacklist = blacklist.split(",")
shortBlacklist = []
if helpers.set_up_anidb_connection():
for groupName in blacklist:
group = sickbeard.ADBA_CONNECTION.group(gname=groupName)
for line in group.datalines:
if line["shortname"]:
shortBlacklist.append(line["shortname"])
else:
if not groupName in shortBlacklist:
shortBlacklist.append(groupName)
else:
shortBlacklist = blacklist
bwl.set_black_keywords_for("release_group", shortBlacklist)
else:
bwl.set_black_keywords_for("release_group", [])
if whiteWords:
whiteWords = [x.strip() for x in whiteWords.split(",")]
bwl.set_white_keywords_for("global", whiteWords)
else:
bwl.set_white_keywords_for("global", [])
if blackWords:
blackWords = [x.strip() for x in blackWords.split(",")]
bwl.set_black_keywords_for("global", blackWords)
else:
bwl.set_black_keywords_for("global", [])
errors = [] errors = []
with showObj.lock: with showObj.lock:
newQuality = Quality.combineQualities(map(int, anyQualities), map(int, bestQualities)) newQuality = Quality.combineQualities(map(int, anyQualities), map(int, bestQualities))