# Author: Nic Wolfe # URL: http://code.google.com/p/sickbeard/ # # This file is part of SickGear. # # SickGear 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. # # SickGear 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 SickGear. If not, see . from collections import defaultdict import datetime import re import threading import time import traceback import sickbeard from . import db, helpers, logger, name_cache from .anime import create_anidb_obj from .classes import OrderedDefaultdict from .indexers.indexer_config import TVINFO_TVDB from _23 import filter_iter, map_iter from six import iteritems, PY2, text_type # noinspection PyUnreachableCode if False: # noinspection PyUnresolvedReferences from typing import AnyStr, List, Tuple exception_dict = {} anidb_exception_dict = {} xem_exception_dict = {} xem_ids_list = defaultdict(list) exceptionsCache = {} exceptionsSeasonCache = {} exceptionLock = threading.Lock() def should_refresh(name): """ :param name: name :type name: AnyStr :return: :rtype: bool """ max_refresh_age_secs = 86400 # 1 day my_db = db.DBConnection() rows = my_db.select('SELECT last_refreshed FROM scene_exceptions_refresh WHERE list = ?', [name]) if rows: last_refresh = int(rows[0]['last_refreshed']) return int(time.mktime(datetime.datetime.today().timetuple())) > last_refresh + max_refresh_age_secs return True def set_last_refresh(name): """ :param name: name :type name: AnyStr """ my_db = db.DBConnection() my_db.upsert('scene_exceptions_refresh', {'last_refreshed': int(time.mktime(datetime.datetime.today().timetuple()))}, {'list': name}) def get_scene_exceptions(tvid, prodid, season=-1): """ Given a indexer_id, return a list of all the scene exceptions. :param tvid: tvid :type tvid: int :param prodid: prodid :type prodid: int or long :param season: season number :type season: int :return: :rtype: List """ global exceptionsCache exceptions_list = [] if (tvid, prodid) not in exceptionsCache or season not in exceptionsCache[(tvid, prodid)]: my_db = db.DBConnection() exceptions = my_db.select('SELECT show_name' ' FROM scene_exceptions' ' WHERE indexer = ? AND indexer_id = ?' ' AND season = ?', [tvid, prodid, season]) if exceptions: exceptions_list = list(set([cur_exception['show_name'] for cur_exception in exceptions])) if (tvid, prodid) not in exceptionsCache: exceptionsCache[(tvid, prodid)] = {} exceptionsCache[(tvid, prodid)][season] = exceptions_list else: exceptions_list = exceptionsCache[(tvid, prodid)][season] if 1 == season: # if we where looking for season 1 we can add generic names exceptions_list += get_scene_exceptions(tvid, prodid, season=-1) return exceptions_list def get_all_scene_exceptions(tvid_prodid): """ :param tvid_prodid: :type tvid_prodid: AnyStr :return: :rtype: OrderedDefaultdict """ exceptions_dict = OrderedDefaultdict(list) from sickbeard.tv import TVidProdid my_db = db.DBConnection() exceptions = my_db.select('SELECT show_name,season' ' FROM scene_exceptions' ' WHERE indexer = ? AND indexer_id = ?' ' ORDER BY season', TVidProdid(tvid_prodid).list) if exceptions: for cur_exception in exceptions: exceptions_dict[cur_exception['season']].append(cur_exception['show_name']) return exceptions_dict def get_scene_seasons(tvid, prodid): """ return a list of season numbers that have scene exceptions :param tvid: tvid :type tvid: int :param prodid: prodid :type prodid: int or long :return: :rtype: List """ global exceptionsSeasonCache exception_season_list = [] if (tvid, prodid) not in exceptionsSeasonCache: my_db = db.DBConnection() sql_result = my_db.select('SELECT DISTINCT(season) AS season' ' FROM scene_exceptions' ' WHERE indexer = ? AND indexer_id = ?', [tvid, prodid]) if sql_result: exception_season_list = list(set([int(x['season']) for x in sql_result])) if (tvid, prodid) not in exceptionsSeasonCache: exceptionsSeasonCache[(tvid, prodid)] = {} exceptionsSeasonCache[(tvid, prodid)] = exception_season_list else: exception_season_list = exceptionsSeasonCache[(tvid, prodid)] return exception_season_list def get_scene_exception_by_name(show_name): """ :param show_name: show name :type show_name: AnyStr :return: :rtype: Tuple[None, None, None] or Tuple[int, int or long, int] """ return get_scene_exception_by_name_multiple(show_name)[0] def get_scene_exception_by_name_multiple(show_name): """ :param show_name: show name :type show_name: AnyStr :return: (tvid, prodid, season) of the exception, None if no exception is present. :rtype: Tuple[None, None, None] or Tuple[int, int or long, int] """ try: exception_result = name_cache.nameCache[helpers.full_sanitize_scene_name(show_name)] return [exception_result] except (BaseException, Exception): return [[None, None, None]] def retrieve_exceptions(): """ Looks up the exceptions on github, parses them into a dict, and inserts them into the scene_exceptions table in cache.db. Also clears the scene name cache. """ global exception_dict, anidb_exception_dict, xem_exception_dict # exceptions are stored on github pages for tvid in sickbeard.TVInfoAPI().sources: if should_refresh(sickbeard.TVInfoAPI(tvid).name): logger.log(u'Checking for scene exception updates for %s' % sickbeard.TVInfoAPI(tvid).name) url = sickbeard.TVInfoAPI(tvid).config['scene_url'] url_data = helpers.get_url(url) if None is url_data: # When None is urlData, trouble connecting to github logger.log(u'Check scene exceptions update failed. Unable to get URL: %s' % url, logger.ERROR) continue else: set_last_refresh(sickbeard.TVInfoAPI(tvid).name) # each exception is on one line with the format indexer_id: 'show name 1', 'show name 2', etc for cur_line in url_data.splitlines(): cur_line = cur_line prodid, sep, aliases = cur_line.partition(':') if not aliases: continue prodid = int(prodid) # regex out the list of shows, taking \' into account # alias_list = [re.sub(r'\\(.)', r'\1', x) for x in re.findall(r"'(.*?)(?