Private GIT

Skip to content
Snippets Groups Projects
Select Git revision
  • dea999e2e93d11f57f3b0e778d2955395ed5c5ef
  • 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

newznab.py

Blame
  • user avatar
    536c5d7e
    History
    newznab.py 11.90 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 urllib
    import email.utils
    import datetime
    import re
    import os
    
    from xml.dom.minidom import parseString
    
    import sickbeard
    import generic
    
    from sickbeard import classes, show_name_helpers
    from sickbeard import helpers
    from sickbeard import scene_exceptions
    from sickbeard import encodingKludge as ek
    
    from sickbeard import exceptions
    from sickbeard import logger
    from sickbeard import tvcache
    from sickbeard.exceptions import ex
    from sickbeard.name_parser.parser import NameParser, InvalidNameException
    
    
    
    class NewznabProvider(generic.NZBProvider):
    
        def __init__(self, name, url, key=''):
    
            generic.NZBProvider.__init__(self, name)
    
            self.cache = NewznabCache(self)
    
            self.url = url
            self.key = key
    
            # if a provider doesn't need an api key then this can be false
            self.needs_auth = True
    
            self.enabled = True
            self.supportsBacklog = True
    
            self.default = False
    
        def configStr(self):
            return self.name + '|' + self.url + '|' + self.key + '|' + str(int(self.enabled))
    
        def imageName(self):
            if ek.ek(os.path.isfile, ek.ek(os.path.join, sickbeard.PROG_DIR, 'data', 'images', 'providers', self.getID() + '.png')):
                return self.getID() + '.png'
            return 'newznab.png'
    
        def isEnabled(self):
            return self.enabled
    
        def _get_season_search_strings(self, show, season=None):
    
            if not show:
                return [{}]
    
            to_return = []
    
            # add new query strings for exceptions
            name_exceptions = scene_exceptions.get_scene_exceptions(show.tvdbid) + [show.name]
            for cur_exception in name_exceptions:
    
                cur_params = {}
    
                # search directly by tvrage id
                if show.tvrid:
                    cur_params['rid'] = show.tvrid
                # if we can't then fall back on a very basic name search
                else:
                    if show.audio_lang=="fr":
                        cur_params['q'] = helpers.sanitizeSceneName(cur_exception)+ " french"
                    else:
                        cur_params['q'] = helpers.sanitizeSceneName(cur_exception)
    
                if season != None:
                    # air-by-date means &season=2010&q=2010.03, no other way to do it atm
                    if show.air_by_date:
                        cur_params['season'] = season.split('-')[0]
                        if 'q' in cur_params:
                            cur_params['q'] += '.' + season.replace('-', '.')
                        else:
                            cur_params['q'] = season.replace('-', '.')
                    else:
                        cur_params['season'] = season
    
                # hack to only add a single result if it's a rageid search
                if not ('rid' in cur_params and to_return):
                    to_return.append(cur_params)
    
            return to_return
    
        def _get_episode_search_strings(self, ep_obj, french=None):
            showNames = show_name_helpers.allPossibleShowNames(ep_obj.show)
            for show_name in showNames:
                ep_obj.show.sname=show_name
                params = {}
    
                if not ep_obj:
                    return [params]
            
            # search directly by tvrage id
                if ep_obj.show.tvrid:
                    params['rid'] = ep_obj.show.tvrid
                    if ep_obj.show.audio_lang=="fr" or french:
                        params['q'] = "french"
                    else:
                        params['q'] = helpers.sanitizeSceneName(ep_obj.show.sname)
            # if we can't then fall back on a very basic name search
                else:
                    if ep_obj.show.audio_lang=="fr" or french:
                        params['q'] = helpers.sanitizeSceneName(ep_obj.show.sname) + " french"
                    else:
                        params['q'] = helpers.sanitizeSceneName(ep_obj.show.sname)
    
                if ep_obj.show.air_by_date:
                    date_str = str(ep_obj.airdate)
    
                    params['season'] = date_str.partition('-')[0]
                    params['ep'] = date_str.partition('-')[2].replace('-', '/')
                    
                else:
                    params['season'] = ep_obj.scene_season
                    params['ep'] = ep_obj.scene_episode
    
                    to_return = [params]
    
            # only do exceptions if we are searching by name
                if 'q' in params:
    
                # add new query strings for exceptions
                    name_exceptions = scene_exceptions.get_scene_exceptions(ep_obj.show.tvdbid)
                    for cur_exception in name_exceptions:
    
                    # don't add duplicates
                        if cur_exception == ep_obj.show.sname:
                            continue
    
                        cur_return = params.copy()
                        cur_return['q'] = helpers.sanitizeSceneName(cur_exception)
                        to_return.append(cur_return)
    
            return to_return
    
        def _get_language(self, title=None, item=None):
            if not title:
                return 'en'
            else:
                try:
                    myParser = NameParser()
                    parse_result = myParser.parse(title)
                except InvalidNameException:
                    logger.log(u"Unable to parse the filename "+title+" into a valid episode", logger.WARNING)
                    return 'en'
    
            return parse_result.audio_langs    
    
        def _doGeneralSearch(self, search_string):
            return self._doSearch({'q': search_string.replace('!','')})
    
        def _checkAuthFromData(self, data):
    
            try:
                parsedXML = parseString(data)
            except Exception:
                return False
    
            if parsedXML.documentElement.tagName == 'error':
                code = parsedXML.documentElement.getAttribute('code')
                if code == '100':
                    raise exceptions.AuthException("Your API key for " + self.name + " is incorrect, check your config.")
                elif code == '101':
                    raise exceptions.AuthException("Your account on " + self.name + " has been suspended, contact the administrator.")
                elif code == '102':
                    raise exceptions.AuthException("Your account isn't allowed to use the API on " + self.name + ", contact the administrator")
                else:
                    logger.log(u"Unknown error given from " + self.name + ": "+parsedXML.documentElement.getAttribute('description'), logger.ERROR)
                    return False
    
            return True
    
        def _doSearch(self, search_params, show=None, max_age=0, season=None, french=None):
            
            cat = '5030,5040'
            if (show and show.audio_lang != u"en") or french:
                cat = '5020'
    
            params = {"t": "tvsearch",
                      "maxage": sickbeard.USENET_RETENTION,
                      "limit": 100,
                      "cat": cat}
    
            # if max_age is set, use it, don't allow it to be missing
            if max_age or not params['maxage']:
                params['maxage'] = max_age
    
            # hack this in for now
            if self.getID() == 'nzbs_org':
                params['cat'] += ',5070,5090'
    
            if search_params:
                params.update(search_params)
    
            if self.key:
                params['apikey'] = self.key
    
            searchURL = self.url + 'api?' + urllib.urlencode(params)
    
            logger.log(u"Search url: " + searchURL, logger.DEBUG)
    
            data = self.getURL(searchURL)
    
            if not data:
                return []
    
            # hack this in until it's fixed server side
            if not data.startswith('<?xml'):
                data = '<?xml version="1.0" encoding="ISO-8859-1" ?>' + data
    
            try:
                parsedXML = parseString(data)
                items = parsedXML.getElementsByTagName('item')
            except Exception, e:
                logger.log(u"Error trying to load " + self.name + " RSS feed: " + ex(e), logger.ERROR)
                logger.log(u"RSS data: " + data, logger.DEBUG)
                return []
    
            if not self._checkAuthFromData(data):
                return []
    
            if parsedXML.documentElement.tagName != 'rss':
                logger.log(u"Resulting XML from " + self.name + " isn't RSS, not parsing it", logger.ERROR)
                return []
    
            results = []
    
            for curItem in items:
                (title, url) = self._get_title_and_url(curItem)
    
                if not title or not url:
                    logger.log(u"The XML returned from the " + self.name + " RSS feed is incomplete, this result is unusable: " + data, logger.ERROR)
                    continue
    
                results.append(curItem)
    
            return results
    
        def findPropers(self, date=None):
    
            search_terms = ['.proper.', '.repack.']
            results = []
    
            cache_results = self.cache.listPropers(date)
            results = [classes.Proper(x['name'], x['url'], datetime.datetime.fromtimestamp(x['time'])) for x in cache_results]
    
            for term in search_terms:
                for curResult in self._doSearch({'q': term}, max_age=4):
    
                    (title, url) = self._get_title_and_url(curResult)
    
                    description_node = curResult.getElementsByTagName('pubDate')[0]
                    descriptionStr = helpers.get_xml_text(description_node)
    
                    try:
                        # we could probably do dateStr = descriptionStr but we want date in this format
                        dateStr = re.search('(\w{3}, \d{1,2} \w{3} \d{4} \d\d:\d\d:\d\d) [\+\-]\d{4}', descriptionStr).group(1)
                    except:
                        dateStr = None
    
                    if not dateStr:
                        logger.log(u"Unable to figure out the date for entry " + title + ", skipping it")
                        continue
                    else:
    
                        resultDate = email.utils.parsedate(dateStr)
                        if resultDate:
                            resultDate = datetime.datetime(*resultDate[0:6])
    
                    if date == None or resultDate > date:
                        search_result = classes.Proper(title, url, resultDate)
                        results.append(search_result)
    
            return results
    
    
    class NewznabCache(tvcache.TVCache):
    
        def __init__(self, provider):
    
            tvcache.TVCache.__init__(self, provider)
    
            # only poll newznab providers every 15 minutes max
            self.minTime = 15
    
        def _getRSSData(self):
            
            languages = helpers.getAllLanguages()
    
            languages = filter(lambda x: not x == u"en", languages)
            cat = '5030,5040'
            if len(languages) > 0:
                cat = '5020'
    
            params = {"t": "tvsearch",
                      "cat": cat}
    
            # hack this in for now
            if self.provider.getID() == 'nzbs_org':
                params['cat'] += ',5070,5090'
    
            if self.provider.key:
                params['apikey'] = self.provider.key
    
            url = self.provider.url + 'api?' + urllib.urlencode(params)
    
            logger.log(self.provider.name + " cache update URL: " + url, logger.DEBUG)
    
            data = self.provider.getURL(url)
    
            # hack this in until it's fixed server side
            if data and not data.startswith('<?xml'):
                data = '<?xml version="1.0" encoding="ISO-8859-1" ?>' + data
    
            return data
    
        def _checkAuth(self, data):
    
            return self.provider._checkAuthFromData(data)