Private GIT

Skip to content
Snippets Groups Projects
Select Git revision
  • 8d9d2881bcec478162268b61cdc6a93c445ccb56
  • master default protected
  • development
  • MonTest
  • gh-pages
  • ThePirateBay
  • Pistachitos
  • custom_naming
  • dataTables
  • libdirs
  • nzbs_org_transition
  • api
  • timezones
  • adel-0002
  • adel-0001
  • build-497
  • build-496
  • build-495
  • build-494
  • build-493
  • build-492
  • build-491
  • build-490
  • build-489
  • build-488
  • build-487
  • build-486
  • build-485
  • build-484
  • build-483
  • build-482
  • build-481
  • build-480
33 results

tvcache.py

Blame
  • user avatar
    536c5d7e
    History
    tvcache.py 16.68 KiB
    # Author: Nic Wolfe <nic@wolfeden.ca>
    # 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/>.
    
    import time
    import datetime
    import sqlite3
    
    import sickbeard
    
    from sickbeard import db
    from sickbeard import logger
    from sickbeard.common import Quality
    
    from sickbeard import helpers, exceptions, show_name_helpers, scene_exceptions
    from sickbeard import name_cache
    from sickbeard.exceptions import ex
    
    #import xml.etree.cElementTree as etree
    import xml.dom.minidom
    
    from lib.tvdb_api import tvdb_api, tvdb_exceptions
    
    from name_parser.parser import NameParser, InvalidNameException
    
    
    class CacheDBConnection(db.DBConnection):
    
        def __init__(self, providerName):
            db.DBConnection.__init__(self, "cache.db")
    
            # Create the table if it's not already there
            try:
                sql = "CREATE TABLE "+providerName+" (name TEXT, season NUMERIC, episodes TEXT, tvrid NUMERIC, tvdbid NUMERIC, url TEXT, time NUMERIC, quality TEXT);"
                self.connection.execute(sql)
                self.connection.commit()
            except sqlite3.OperationalError, e:
                if str(e) != "table "+providerName+" already exists":
                    raise
    
            # Create the table if it's not already there
            try:
                sql = "CREATE TABLE lastUpdate (provider TEXT, time NUMERIC);"
                self.connection.execute(sql)
                self.connection.commit()
            except sqlite3.OperationalError, e:
                if str(e) != "table lastUpdate already exists":
                    raise
    
    class TVCache():
    
        def __init__(self, provider):
    
            self.provider = provider
            self.providerID = self.provider.getID()
            self.minTime = 10
    
        def _getDB(self):
    
            return CacheDBConnection(self.providerID)
    
        def _clearCache(self):
    
            myDB = self._getDB()
    
            myDB.action("DELETE FROM "+self.providerID+" WHERE 1")
    
        def _getRSSData(self):
    
            data = None
    
            return data
    
        def _checkAuth(self, data):
            return True
    
        def _checkItemAuth(self, title, url):
            return True
    
        def updateCache(self):
    
            if not self.shouldUpdate():
                return
    
            data = self._getRSSData()
    
            # as long as the http request worked we count this as an update
            if data:
                self.setLastUpdate()
            else:
                return []
    
            # now that we've loaded the current RSS feed lets delete the old cache
            logger.log(u"Clearing "+self.provider.name+" cache and updating with new information")
            self._clearCache()
    
            if not self._checkAuth(data):
                raise exceptions.AuthException("Your authentication info for "+self.provider.name+" is incorrect, check your config")
    
            try:
                parsedXML = xml.dom.minidom.parseString(data)
                items = parsedXML.getElementsByTagName('item')
            except Exception, e:
                logger.log(u"Error trying to load "+self.provider.name+" RSS feed: "+ex(e), logger.ERROR)
                logger.log(u"Feed contents: "+repr(data), logger.DEBUG)
                return []
    
            if parsedXML.documentElement.tagName != 'rss':
                logger.log(u"Resulting XML from "+self.provider.name+" isn't RSS, not parsing it", logger.ERROR)
                return []
    
            for item in items:
    
                self._parseItem(item)
    
        def _translateLinkURL(self, url):
            return url.replace('&amp;','&')
    
        def _parseItem(self, item):
    
            title = helpers.get_xml_text(item.getElementsByTagName('title')[0])
            url = helpers.get_xml_text(item.getElementsByTagName('link')[0])
    
            self._checkItemAuth(title, url)
    
            if not title or not url:
                logger.log(u"The XML returned from the "+self.provider.name+" feed is incomplete, this result is unusable", logger.ERROR)
                return
    
            url = self._translateLinkURL(url)
    
            logger.log(u"Adding item from RSS to cache: "+title, logger.DEBUG)
    
            self._addCacheEntry(title, url)
    
        def _getLastUpdate(self):
            myDB = self._getDB()
            sqlResults = myDB.select("SELECT time FROM lastUpdate WHERE provider = ?", [self.providerID])
    
            if sqlResults:
                lastTime = int(sqlResults[0]["time"])
            else:
                lastTime = 0
    
            return datetime.datetime.fromtimestamp(lastTime)
    
        def setLastUpdate(self, toDate=None):
    
            if not toDate:
                toDate = datetime.datetime.today()
    
            myDB = self._getDB()
            myDB.upsert("lastUpdate",
                        {'time': int(time.mktime(toDate.timetuple()))},
                        {'provider': self.providerID})
    
        lastUpdate = property(_getLastUpdate)
    
        def shouldUpdate(self):
            # if we've updated recently then skip the update
            if datetime.datetime.today() - self.lastUpdate < datetime.timedelta(minutes=self.minTime):
                logger.log(u"Last update was too soon, using old cache: today()-"+str(self.lastUpdate)+"<"+str(datetime.timedelta(minutes=self.minTime)), logger.DEBUG)
                return False
    
            return True
    
        def _addCacheEntry(self, name, url, season=None, episodes=None, tvdb_id=0, tvrage_id=0, quality=None, extraNames=[]):
    
            myDB = self._getDB()
    
            parse_result = None
    
            # if we don't have complete info then parse the filename to get it
            for curName in [name] + extraNames:
                try:
                    myParser = NameParser()
                    parse_result = myParser.parse(curName)
                except InvalidNameException:
                    logger.log(u"Unable to parse the filename "+curName+" into a valid episode", logger.DEBUG)
                    continue
    
            if not parse_result:
                logger.log(u"Giving up because I'm unable to parse this name: "+name, logger.DEBUG)
                return False
    
            if not parse_result.series_name:
                logger.log(u"No series name retrieved from "+name+", unable to cache it", logger.DEBUG)
                return False
    
            tvdb_lang = None
    
            # if we need tvdb_id or tvrage_id then search the DB for them
            if not tvdb_id or not tvrage_id:
    
                # if we have only the tvdb_id, use the database
                if tvdb_id:
                    showObj = helpers.findCertainShow(sickbeard.showList, tvdb_id)
                    if showObj:
                        tvrage_id = showObj.tvrid
                        tvdb_lang = showObj.lang
                    else:
                        logger.log(u"We were given a TVDB id "+str(tvdb_id)+" but it doesn't match a show we have in our list, so leaving tvrage_id empty", logger.DEBUG)
                        tvrage_id = 0
    
                # if we have only a tvrage_id then use the database
                elif tvrage_id:
                    showObj = helpers.findCertainTVRageShow(sickbeard.showList, tvrage_id)
                    if showObj:
                        tvdb_id = showObj.tvdbid
                        tvdb_lang = showObj.lang
                    else:
                        logger.log(u"We were given a TVRage id "+str(tvrage_id)+" but it doesn't match a show we have in our list, so leaving tvdb_id empty", logger.DEBUG)
                        tvdb_id = 0
    
                # if they're both empty then fill out as much info as possible by searching the show name
                else:
    
                    # check the name cache and see if we already know what show this is
                    logger.log(u"Checking the cache to see if we already know the tvdb id of "+parse_result.series_name, logger.DEBUG)
                    tvdb_id = name_cache.retrieveNameFromCache(parse_result.series_name)
                    
                    # remember if the cache lookup worked or not so we know whether we should bother updating it later
                    if tvdb_id == None:
                        logger.log(u"No cache results returned, continuing on with the search", logger.DEBUG)
                        from_cache = False
                    else:
                        logger.log(u"Cache lookup found "+repr(tvdb_id)+", using that", logger.DEBUG)
                        from_cache = True
                    
                    # if the cache failed, try looking up the show name in the database
                    if tvdb_id == None:
                        logger.log(u"Trying to look the show up in the show database", logger.DEBUG)
                        showResult = helpers.searchDBForShow(parse_result.series_name)
                        if showResult:
                            logger.log(parse_result.series_name+" was found to be show "+showResult[1]+" ("+str(showResult[0])+") in our DB.", logger.DEBUG)
                            tvdb_id = showResult[0]
                    def regexSearch(season=-1):
                        if show_name_helpers.isGoodResult(name, curShow, False, season=season):
                            logger.log(u"Successfully matched " + name + " to " + curShow.name + " with regex", logger.DEBUG)
                            tvdb_id = curShow.tvdbid
                            tvdb_lang = curShow.lang
                            return True
                        return False
                    # if the DB lookup fails then do a comprehensive regex search
                    if tvdb_id == None:
                        logger.log(u"Couldn't figure out a show name straight from the DB, trying a regex search instead", logger.DEBUG)
                        for curShow in sickbeard.showList:
                            if regexSearch():
                                break
                    # if the DB lookup fails then do a comprehensive regex search on saason exceptions
                    if tvdb_id == None:
                        logger.log(u"Couldn't figure out a show name straight from the DB, trying a regex search on season exceptions", logger.DEBUG)
                        for curShow in sickbeard.showList:
                            scene_seasons = scene_exceptions.getSceneSeasons(curShow.tvdbid)
                            for cur_scene_season in scene_seasons:
                                if regexSearch(cur_scene_season):
                                    break
                    # if tvdb_id was anything but None (0 or a number) then 
                    if not from_cache:
                        name_cache.addNameToCache(parse_result.series_name, tvdb_id)
    
                    # if we came out with tvdb_id = None it means we couldn't figure it out at all, just use 0 for that
                    if tvdb_id == None:
                        tvdb_id = 0
    
                    # if we found the show then retrieve the show object
                    if tvdb_id:
                        showObj = helpers.findCertainShow(sickbeard.showList, tvdb_id)
                        if showObj:
                            tvrage_id = showObj.tvrid
                            tvdb_lang = showObj.lang
    
            # if we weren't provided with season/episode information then get it from the name that we parsed
            if not season:
                season = parse_result.season_number if parse_result.season_number != None else 1
            if not episodes:
                episodes = parse_result.episode_numbers
    
            # if we have an air-by-date show then get the real season/episode numbers
            if parse_result.air_by_date and tvdb_id:
                try:
                    # There's gotta be a better way of doing this but we don't wanna
                    # change the language value elsewhere
                    ltvdb_api_parms = sickbeard.TVDB_API_PARMS.copy()
    
                    if not (tvdb_lang == "" or tvdb_lang == "en" or tvdb_lang == None):
                        ltvdb_api_parms['language'] = tvdb_lang
    
                    t = tvdb_api.Tvdb(**ltvdb_api_parms)
                    epObj = t[tvdb_id].airedOn(parse_result.air_date)[0]
                    season = int(epObj["seasonnumber"])
                    episodes = [int(epObj["episodenumber"])]
                except tvdb_exceptions.tvdb_episodenotfound:
                    logger.log(u"Unable to find episode with date "+str(parse_result.air_date)+" for show "+parse_result.series_name+", skipping", logger.WARNING)
                    return False
                except tvdb_exceptions.tvdb_error, e:
                    logger.log(u"Unable to contact TVDB: "+ex(e), logger.WARNING)
                    return False
    
            episodeText = "|"+"|".join(map(str, episodes))+"|"
    
            # get the current timestamp
            curTimestamp = int(time.mktime(datetime.datetime.today().timetuple()))
    
            if not quality:
                quality = Quality.nameQuality(name)
    
            myDB.action("INSERT INTO "+self.providerID+" (name, season, episodes, tvrid, tvdbid, url, time, quality) VALUES (?,?,?,?,?,?,?,?)",
                        [name, season, episodeText, tvrage_id, tvdb_id, url, curTimestamp, quality])
    
    
        def searchCache(self, episode, manualSearch=False):
            neededEps = self.findNeededEpisodes(episode, manualSearch)
            return neededEps[episode]
    
        def listPropers(self, date=None, delimiter="."):
    
            myDB = self._getDB()
    
            sql = "SELECT * FROM "+self.providerID+" WHERE name LIKE '%.PROPER.%' OR name LIKE '%.REPACK.%'"
    
            if date != None:
                sql += " AND time >= "+str(int(time.mktime(date.timetuple())))
    
            #return filter(lambda x: x['tvdbid'] != 0, myDB.select(sql))
            return myDB.select(sql)
    
        def findNeededEpisodes(self, episode = None, manualSearch=False):
            neededEps = {}
    
            if episode:
                neededEps[episode] = []
    
            myDB = self._getDB()
    
            if not episode:
                sqlResults = myDB.select("SELECT * FROM "+self.providerID)
            else:
                sqlResults = myDB.select("SELECT * FROM "+self.providerID+" WHERE tvdbid = ? AND season = ? AND episodes LIKE ?", [episode.show.tvdbid, episode.scene_season, "%|"+str(episode.scene_episode)+"|%"])
    
            # for each cache entry
            for curResult in sqlResults:
                
                # get the show object, or if it's not one of our shows then ignore it
                showObj = helpers.findCertainShow(sickbeard.showList, int(curResult["tvdbid"]))
                if not showObj:
                    continue
                
                try:
                    fp = NameParser()
                    parse_result = fp.parse(curResult["name"])
                except InvalidNameException:
                    logger.log(u"Unable to parse the filename "+curResult["name"]+" into a valid episode", logger.WARNING)
                    
                if not parse_result.audio_langs == showObj.audio_lang:
                    continue
    
                # skip non-tv crap (but allow them for Newzbin cause we assume it's filtered well)
                if self.providerID != 'newzbin' and not show_name_helpers.filterBadReleases(curResult["name"],showObj.audio_lang):
                    continue
    
                # get season and ep data (ignoring multi-eps for now)
                curSeason = int(curResult["season"])
                if curSeason == -1:
                    continue
                curEp = curResult["episodes"].split("|")[1]
                if not curEp:
                    continue
                curEp = int(curEp)
                curQuality = int(curResult["quality"])
    
                # if the show says we want that episode then add it to the list
                if not showObj.wantEpisode(curSeason, curEp, curQuality, manualSearch):
                    logger.log(u"Skipping "+curResult["name"]+" because we don't want an episode that's "+Quality.qualityStrings[curQuality], logger.DEBUG)
    
                else:
    
                    if episode:
                        epObj = episode
                    else:
                        epObj = showObj.getEpisode(curSeason, curEp)
    
                    # build a result object
                    title = curResult["name"]
                    url = curResult["url"]
    
                    logger.log(u"Found result " + title + " at " + url)
    
                    result = self.provider.getResult([epObj])
                    result.url = url
                    result.name = title
                    result.quality = curQuality
                    result.audio_lang = str(showObj.audio_lang)
                    # add it to the list
                    if epObj not in neededEps:
                        neededEps[epObj] = [result]
                    else:
                        neededEps[epObj].append(result)
    
            return neededEps