# Author: Nyaran <nyayukko@gmail.com>, based on Antoine Bertin <diaoulael@gmail.com> work # URL: http://code.google.com/p/sickbeard/ # # This file is part of SickRage. # # SickRage 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. # # SickRage 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 SickRage. If not, see <http://www.gnu.org/licenses/>. import time import datetime import sickbeard from sickbeard.common import * from sickbeard import notifiers from sickbeard import logger from sickbeard import encodingKludge as ek from sickbeard import db from sickbeard import history import subliminal import babelfish subliminal.cache_region.configure('dogpile.cache.memory') provider_urls = {'addic7ed': 'http://www.addic7ed.com', 'opensubtitles': 'http://www.opensubtitles.org', 'podnapisi': 'http://www.podnapisi.net', 'thesubdb': 'http://www.thesubdb.com', 'tvsubtitles': 'http://www.tvsubtitles.net' } SINGLE = 'und' def sortedServiceList(): newList = [] lmgtfy = 'http://lmgtfy.com/?q=' curIndex = 0 for curService in sickbeard.SUBTITLES_SERVICES_LIST: if curService in subliminal.provider_manager.available_providers: newList.append({'name': curService, 'url': provider_urls[curService] if curService in provider_urls else (lmgtfy % curService), 'image': curService + '.png', 'enabled': sickbeard.SUBTITLES_SERVICES_ENABLED[curIndex] == 1 }) curIndex += 1 for curService in subliminal.provider_manager.available_providers: if curService not in [x['name'] for x in newList]: newList.append({'name': curService, 'url': provider_urls[curService] if curService in provider_urls else (lmgtfy % curService), 'image': curService + '.png', 'enabled': False, }) return newList def getEnabledServiceList(): return [x['name'] for x in sortedServiceList() if x['enabled']] #Hack around this for now. def fromietf(language): return babelfish.Language.fromietf(language if language not in 'pb' else 'pt-BR') def isValidLanguage(language): try: langObj = fromietf(language) except: return False return True def getLanguageName(language): return fromietf(language ).name # TODO: Filter here for non-languages in sickbeard.SUBTITLES_LANGUAGES def wantedLanguages(sqlLike = False): wantedLanguages = sorted(sickbeard.SUBTITLES_LANGUAGES) if sqlLike: return '%' + ','.join(wantedLanguages) + '%' return wantedLanguages def subtitlesLanguages(video_path): """Return a list detected subtitles for the given video file""" resultList = [] languages = subliminal.video.scan_subtitle_languages(video_path) for language in languages: if hasattr(language, 'alpha3') and language.alpha3: resultList.append(language.alpha3) elif hasattr(language, 'alpha2') and language.alpha2: resultList.append(language.alpha2) defaultLang = wantedLanguages() if len(resultList) is 1 and len(defaultLang) is 1: return defaultLang return sorted(resultList) # TODO: Return only languages our providers allow def subtitleLanguageFilter(): return [language for language in babelfish.LANGUAGE_MATRIX if hasattr(language, 'alpha2') and language.alpha2] class SubtitlesFinder(): """ The SubtitlesFinder will be executed every hour but will not necessarly search and download subtitles. Only if the defined rule is true """ def run(self, force=False): if not sickbeard.USE_SUBTITLES: return if len(sickbeard.subtitles.getEnabledServiceList()) < 1: logger.log(u'Not enough services selected. At least 1 service is required to search subtitles in the background', logger.ERROR) return logger.log(u'Checking for subtitles', logger.INFO) # get episodes on which we want subtitles # criteria is: # - show subtitles = 1 # - episode subtitles != config wanted languages or SINGLE (depends on config multi) # - search count < 2 and diff(airdate, now) > 1 week : now -> 1d # - search count < 7 and diff(airdate, now) <= 1 week : now -> 4h -> 8h -> 16h -> 1d -> 1d -> 1d today = datetime.date.today().toordinal() # you have 5 minutes to understand that one. Good luck myDB = db.DBConnection() sqlResults = myDB.select('SELECT s.show_name, e.showid, e.season, e.episode, e.status, e.subtitles, ' + 'e.subtitles_searchcount AS searchcount, e.subtitles_lastsearch AS lastsearch, e.location, (? - e.airdate) AS airdate_daydiff ' + 'FROM tv_episodes AS e INNER JOIN tv_shows AS s ON (e.showid = s.indexer_id) ' + 'WHERE s.subtitles = 1 AND e.subtitles NOT LIKE (?) ' + 'AND (e.subtitles_searchcount <= 2 OR (e.subtitles_searchcount <= 7 AND airdate_daydiff <= 7)) ' + 'AND e.location != ""', [today, wantedLanguages(True)]) if len(sqlResults) == 0: logger.log('No subtitles to download', logger.INFO) return rules = self._getRules() now = datetime.datetime.now() for epToSub in sqlResults: if not ek.ek(os.path.isfile, epToSub['location']): logger.log('Episode file does not exist, cannot download subtitles for episode %dx%d of show %s' % (epToSub['season'], epToSub['episode'], epToSub['show_name']), logger.DEBUG) continue # Old shows rule throwaway = datetime.datetime.strptime('20110101', '%Y%m%d') if ((epToSub['airdate_daydiff'] > 7 and epToSub['searchcount'] < 2 and now - datetime.datetime.strptime(epToSub['lastsearch'], '%Y-%m-%d %H:%M:%S') > datetime.timedelta(hours=rules['old'][epToSub['searchcount']])) or # Recent shows rule (epToSub['airdate_daydiff'] <= 7 and epToSub['searchcount'] < 7 and now - datetime.datetime.strptime(epToSub['lastsearch'], '%Y-%m-%d %H:%M:%S') > datetime.timedelta(hours=rules['new'][epToSub['searchcount']]))): logger.log('Downloading subtitles for episode %dx%d of show %s' % (epToSub['season'], epToSub['episode'], epToSub['show_name']), logger.DEBUG) showObj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(epToSub['showid'])) if not showObj: logger.log(u'Show not found', logger.DEBUG) return epObj = showObj.getEpisode(int(epToSub["season"]), int(epToSub["episode"])) if isinstance(epObj, str): logger.log(u'Episode not found', logger.DEBUG) return previous_subtitles = epObj.subtitles try: epObj.downloadSubtitles() except Exception as e: logger.log(u'Unable to find subtitles', logger.DEBUG) logger.log(str(e), logger.DEBUG) return newSubtitles = frozenset(epObj.subtitles).difference(previous_subtitles) if newSubtitles: logger.log(u'Downloaded subtitles for S%02dE%02d in %s' % (epToSub["season"], epToSub["episode"], ', '.join(newSubtitles))) def _getRules(self): """ Define the hours to wait between 2 subtitles search depending on: - the episode: new or old - the number of searches done so far (searchcount), represented by the index of the list """ return {'old': [0, 24], 'new': [0, 4, 8, 4, 16, 24, 24]}