Beispiel #1
0
    def loadShowsFromDB():
        """
        Populates the showList with shows from the database
        """
        logging.debug("Loading initial show list")

        myDB = db.DBConnection()
        sqlResults = myDB.select("SELECT * FROM tv_shows")

        sickbeard.showList = []
        for sqlShow in sqlResults:
            try:
                curShow = TVShow(int(sqlShow[b"indexer"]), int(sqlShow[b"indexer_id"]))

                # Build internal name cache for show
                name_cache.buildNameCache(curShow)

                # get next episode info
                curShow.nextEpisode()

                # add show to internal show list
                sickbeard.showList.append(curShow)
            except Exception as e:
                logging.error(
                        "There was an error creating the show in " + sqlShow[b"location"] + ": " + str(e).decode(
                                'utf-8'))
                logging.debug(traceback.format_exc())
 def test_sceneExceptionByNameAnime(self):
     sickbeard.showList = None
     sickbeard.showList = [Show(1, 79604), Show(1, 295243)]
     sickbeard.showList[0].anime = 1
     sickbeard.showList[1].anime = 1
     scene_exceptions.retrieve_exceptions()
     name_cache.buildNameCache()
     self.assertEqual(scene_exceptions.get_scene_exception_by_name(u'ブラック・ラグーン'), [79604, -1])
     self.assertEqual(scene_exceptions.get_scene_exception_by_name(u'Burakku Ragūn'), [79604, -1])
     self.assertEqual(scene_exceptions.get_scene_exception_by_name('Rokka no Yuusha'), [295243, -1])
    def test_extra_info_no_name(self):
        for i in range(2):
            if 1 == i:
                if None is parser.regex:
                    # only retest if regex lib is installed, now test re lib
                    continue
                parser.regex = None
            for case in extra_info_no_name_tests:
                tvs = TVShow(False, case[0], 2, 1)
                for e in case[1]:
                    tvs.episodes.setdefault(e[1], {}).update({e[2]: TVEpisode(e[0])})

                sickbeard.showList = [tvs]
                name_cache.nameCache = {}
                name_cache.buildNameCache()

                np = parser.NameParser()
                r = np.parse(case[2], cache_result=False)
                n_ep = r.extra_info_no_name()
                self.assertEqual(n_ep, case[3])
Beispiel #4
0
    def run(self, force=False):
        self.amActive = True

        # clear internal name cache
        name_cache.clearCache()

        # get and update scene exceptions lists
        scene_exceptions.retrieve_exceptions()

        # build internal name cache for searches and parsing
        name_cache.buildNameCache()

        # refresh network timezones
        network_timezones.update_network_dict()

        # sure, why not?
        if sickbeard.USE_FAILED_DOWNLOADS:
            failed_history.trimHistory()

        self.amActive = False
Beispiel #5
0
            self._finishEarly()
            raise

        # add it to the show list
        sickbeard.showList.append(self.show)

        try:
            self.show.loadEpisodesFromIndexer()
        except Exception, e:
            logger.log(
                u"Error with " + sickbeard.indexerApi(self.show.indexer).name +
                ", not creating episode list: " + ex(e), logger.ERROR)
            logger.log(traceback.format_exc(), logger.DEBUG)

        # update internal name cache
        name_cache.buildNameCache()

        try:
            self.show.loadEpisodesFromDir()
        except Exception, e:
            logger.log(u"Error searching dir for episodes: " + ex(e),
                       logger.ERROR)
            logger.log(traceback.format_exc(), logger.DEBUG)

        # if they set default ep status to WANTED then run the backlog to search for episodes
        if self.show.default_ep_status == WANTED:
            logger.log(
                u"Launching backlog for this show since its episodes are WANTED"
            )
            sickbeard.backlogSearchScheduler.action.searchBacklog([self.show])
Beispiel #6
0
    def start(self):
        # do some preliminary stuff
        sickbeard.MY_FULLNAME = ek(os.path.normpath,
                                   ek(os.path.abspath, __file__))
        sickbeard.MY_NAME = ek(os.path.basename, sickbeard.MY_FULLNAME)
        sickbeard.PROG_DIR = ek(os.path.dirname, sickbeard.MY_FULLNAME)
        sickbeard.DATA_DIR = sickbeard.PROG_DIR
        sickbeard.MY_ARGS = sys.argv[1:]

        try:
            locale.setlocale(locale.LC_ALL, "")
            sickbeard.SYS_ENCODING = locale.getpreferredencoding()
        except (locale.Error, IOError):
            sickbeard.SYS_ENCODING = 'UTF-8'

        # pylint: disable=no-member
        if not sickbeard.SYS_ENCODING or sickbeard.SYS_ENCODING.lower() in ('ansi_x3.4-1968', 'us-ascii', 'ascii', 'charmap') or \
            (sys.platform.startswith('win') and sys.getwindowsversion()[0] >= 6 and str(getattr(sys.stdout, 'device', sys.stdout).encoding).lower() in ('cp65001', 'charmap')):
            sickbeard.SYS_ENCODING = 'UTF-8'

        # TODO: Continue working on making this unnecessary, this hack creates all sorts of hellish problems
        if not hasattr(sys, "setdefaultencoding"):
            reload(sys)

        try:
            # pylint: disable=no-member
            # On non-unicode builds this will raise an AttributeError, if encoding type is not valid it throws a LookupError
            sys.setdefaultencoding(sickbeard.SYS_ENCODING)
        except Exception:
            sys.exit(
                "Sorry, you MUST add the SickRage folder to the PYTHONPATH environment variable\n"
                + "or find another way to force Python to use " +
                sickbeard.SYS_ENCODING + " for string encoding.")

        # Need console logging for SickBeard.py and SickBeard-console.exe
        self.consoleLogging = (not hasattr(
            sys, "frozen")) or (sickbeard.MY_NAME.lower().find('-console') > 0)

        # Rename the main thread
        threading.currentThread().name = u"MAIN"

        try:
            opts, _ = getopt.getopt(sys.argv[1:], "hqdp::", [
                'help', 'quiet', 'nolaunch', 'daemon', 'pidfile=', 'port=',
                'datadir=', 'config=', 'noresize'
            ])
        except getopt.GetoptError:
            sys.exit(self.help_message())

        for o, a in opts:
            # Prints help message
            if o in ('-h', '--help'):
                sys.exit(self.help_message())

            # For now we'll just silence the logging
            if o in ('-q', '--quiet'):
                self.consoleLogging = False

            # Suppress launching web browser
            # Needed for OSes without default browser assigned
            # Prevent duplicate browser window when restarting in the app
            if o in ('--nolaunch', ):
                self.noLaunch = True

            # Override default/configured port
            if o in ('-p', '--port'):
                try:
                    self.forcedPort = int(a)
                except ValueError:
                    sys.exit("Port: " + str(a) + " is not a number. Exiting.")

            # Run as a double forked daemon
            if o in ('-d', '--daemon'):
                self.runAsDaemon = True
                # When running as daemon disable consoleLogging and don't start browser
                self.consoleLogging = False
                self.noLaunch = True

                if sys.platform == 'win32' or sys.platform == 'darwin':
                    self.runAsDaemon = False

            # Write a pidfile if requested
            if o in ('--pidfile', ):
                self.CREATEPID = True
                self.PIDFILE = str(a)

                # If the pidfile already exists, sickbeard may still be running, so exit
                if ek(os.path.exists, self.PIDFILE):
                    sys.exit("PID file: " + self.PIDFILE +
                             " already exists. Exiting.")

            # Specify folder to load the config file from
            if o in ('--config', ):
                sickbeard.CONFIG_FILE = ek(os.path.abspath, a)

            # Specify folder to use as the data dir
            if o in ('--datadir', ):
                sickbeard.DATA_DIR = ek(os.path.abspath, a)

            # Prevent resizing of the banner/posters even if PIL is installed
            if o in ('--noresize', ):
                sickbeard.NO_RESIZE = True

        # The pidfile is only useful in daemon mode, make sure we can write the file properly
        if self.CREATEPID:
            if self.runAsDaemon:
                pid_dir = ek(os.path.dirname, self.PIDFILE)
                if not ek(os.access, pid_dir, os.F_OK):
                    sys.exit("PID dir: " + pid_dir +
                             " doesn't exist. Exiting.")
                if not ek(os.access, pid_dir, os.W_OK):
                    sys.exit("PID dir: " + pid_dir +
                             " must be writable (write permissions). Exiting.")

            else:
                if self.consoleLogging:
                    sys.stdout.write(
                        u"Not running in daemon mode. PID file creation disabled.\n"
                    )

                self.CREATEPID = False

        # If they don't specify a config file then put it in the data dir
        if not sickbeard.CONFIG_FILE:
            sickbeard.CONFIG_FILE = ek(os.path.join, sickbeard.DATA_DIR,
                                       "config.ini")

        # Make sure that we can create the data dir
        if not ek(os.access, sickbeard.DATA_DIR, os.F_OK):
            try:
                ek(os.makedirs, sickbeard.DATA_DIR, 0744)
            except os.error:
                raise SystemExit("Unable to create datadir '" +
                                 sickbeard.DATA_DIR + "'")

        # Make sure we can write to the data dir
        if not ek(os.access, sickbeard.DATA_DIR, os.W_OK):
            raise SystemExit("Datadir must be writeable '" +
                             sickbeard.DATA_DIR + "'")

        # Make sure we can write to the config file
        if not ek(os.access, sickbeard.CONFIG_FILE, os.W_OK):
            if ek(os.path.isfile, sickbeard.CONFIG_FILE):
                raise SystemExit("Config file '" + sickbeard.CONFIG_FILE +
                                 "' must be writeable.")
            elif not ek(os.access, ek(os.path.dirname, sickbeard.CONFIG_FILE),
                        os.W_OK):
                raise SystemExit("Config file root dir '" +
                                 ek(os.path.dirname, sickbeard.CONFIG_FILE) +
                                 "' must be writeable.")

        ek(os.chdir, sickbeard.DATA_DIR)

        # Check if we need to perform a restore first
        restoreDir = ek(os.path.join, sickbeard.DATA_DIR, 'restore')
        if ek(os.path.exists, restoreDir):
            success = self.restoreDB(restoreDir, sickbeard.DATA_DIR)
            if self.consoleLogging:
                sys.stdout.write(
                    u"Restore: restoring DB and config.ini %s!\n" %
                    ("FAILED", "SUCCESSFUL")[success])

        # Load the config and publish it to the sickbeard package
        if self.consoleLogging and not ek(os.path.isfile,
                                          sickbeard.CONFIG_FILE):
            sys.stdout.write(u"Unable to find '" + sickbeard.CONFIG_FILE +
                             "' , all settings will be default!" + "\n")

        sickbeard.CFG = ConfigObj(sickbeard.CONFIG_FILE)

        # Initialize the config and our threads
        sickbeard.initialize(consoleLogging=self.consoleLogging)

        if self.runAsDaemon:
            self.daemonize()

        # Get PID
        sickbeard.PID = os.getpid()

        # Build from the DB to start with
        self.loadShowsFromDB()

        logger.log(u"Starting up SickRage [%s] from '%s'" %
                   (sickbeard.BRANCH, sickbeard.CONFIG_FILE))

        self.clear_cache()

        if self.forcedPort:
            logger.log(u"Forcing web server to port %s" % self.forcedPort)
            self.startPort = self.forcedPort
        else:
            self.startPort = sickbeard.WEB_PORT

        if sickbeard.WEB_LOG:
            self.log_dir = sickbeard.LOG_DIR
        else:
            self.log_dir = None

        # sickbeard.WEB_HOST is available as a configuration value in various
        # places but is not configurable. It is supported here for historic reasons.
        if sickbeard.WEB_HOST and sickbeard.WEB_HOST != '0.0.0.0':
            self.webhost = sickbeard.WEB_HOST
        else:
            if sickbeard.WEB_IPV6:
                self.webhost = ''
            else:
                self.webhost = '0.0.0.0'

        # web server options
        self.web_options = {
            'port':
            int(self.startPort),
            'host':
            self.webhost,
            'data_root':
            ek(os.path.join, sickbeard.PROG_DIR, 'gui', sickbeard.GUI_NAME),
            'web_root':
            sickbeard.WEB_ROOT,
            'log_dir':
            self.log_dir,
            'username':
            sickbeard.WEB_USERNAME,
            'password':
            sickbeard.WEB_PASSWORD,
            'enable_https':
            sickbeard.ENABLE_HTTPS,
            'handle_reverse_proxy':
            sickbeard.HANDLE_REVERSE_PROXY,
            'https_cert':
            ek(os.path.join, sickbeard.PROG_DIR, sickbeard.HTTPS_CERT),
            'https_key':
            ek(os.path.join, sickbeard.PROG_DIR, sickbeard.HTTPS_KEY),
        }

        # start web server
        self.webserver = SRWebServer(self.web_options)
        self.webserver.start()

        # Fire up all our threads
        sickbeard.start()

        # Build internal name cache
        name_cache.buildNameCache()

        # Prepopulate network timezones, it isn't thread safe
        network_timezones.update_network_dict()

        # sure, why not?
        if sickbeard.USE_FAILED_DOWNLOADS:
            failed_history.trimHistory()

        # # Check for metadata indexer updates for shows (Disabled until we use api)
        # sickbeard.showUpdateScheduler.forceRun()

        # Launch browser
        if sickbeard.LAUNCH_BROWSER and not (self.noLaunch
                                             or self.runAsDaemon):
            sickbeard.launchBrowser(
                'https' if sickbeard.ENABLE_HTTPS else 'http', self.startPort,
                sickbeard.WEB_ROOT)

        # main loop
        while True:
            time.sleep(1)
Beispiel #7
0
    def start(self):  # pylint: disable=too-many-branches,too-many-statements
        """
        Start SickRage
        """
        # do some preliminary stuff
        sickbeard.MY_FULLNAME = ek(os.path.normpath, ek(os.path.abspath, __file__))
        sickbeard.MY_NAME = ek(os.path.basename, sickbeard.MY_FULLNAME)
        sickbeard.PROG_DIR = ek(os.path.dirname, sickbeard.MY_FULLNAME)
        sickbeard.DATA_DIR = sickbeard.PROG_DIR
        sickbeard.MY_ARGS = sys.argv[1:]

        try:
            locale.setlocale(locale.LC_ALL, '')
            sickbeard.SYS_ENCODING = locale.getpreferredencoding()
        except (locale.Error, IOError):
            sickbeard.SYS_ENCODING = 'UTF-8'

        # pylint: disable=no-member
        if not sickbeard.SYS_ENCODING or sickbeard.SYS_ENCODING.lower() in ('ansi_x3.4-1968', 'us-ascii', 'ascii', 'charmap') or \
                (sys.platform.startswith('win') and sys.getwindowsversion()[0] >= 6 and str(getattr(sys.stdout, 'device', sys.stdout).encoding).lower() in ('cp65001', 'charmap')):
            sickbeard.SYS_ENCODING = 'UTF-8'

        # TODO: Continue working on making this unnecessary, this hack creates all sorts of hellish problems
        if not hasattr(sys, 'setdefaultencoding'):
            reload(sys)

        try:
            # On non-unicode builds this will raise an AttributeError, if encoding type is not valid it throws a LookupError
            sys.setdefaultencoding(sickbeard.SYS_ENCODING)  # pylint: disable=no-member
        except (AttributeError, LookupError):
            sys.exit('Sorry, you MUST add the SickRage folder to the PYTHONPATH environment variable\n'
                     'or find another way to force Python to use %s for string encoding.' % sickbeard.SYS_ENCODING)

        # Need console logging for SickBeard.py and SickBeard-console.exe
        self.console_logging = (not hasattr(sys, 'frozen')) or (sickbeard.MY_NAME.lower().find('-console') > 0)

        # Rename the main thread
        threading.currentThread().name = 'MAIN'

        try:
            opts, _ = getopt.getopt(
                sys.argv[1:], 'hqdp::',
                ['help', 'quiet', 'nolaunch', 'daemon', 'pidfile=', 'port=', 'datadir=', 'config=', 'noresize']
            )
        except getopt.GetoptError:
            sys.exit(self.help_message())

        for option, value in opts:
            # Prints help message
            if option in ('-h', '--help'):
                sys.exit(self.help_message())

            # For now we'll just silence the logging
            if option in ('-q', '--quiet'):
                self.console_logging = False

            # Suppress launching web browser
            # Needed for OSes without default browser assigned
            # Prevent duplicate browser window when restarting in the app
            if option in ('--nolaunch',):
                self.no_launch = True

            # Override default/configured port
            if option in ('-p', '--port'):
                try:
                    self.forced_port = int(value)
                except ValueError:
                    sys.exit('Port: {0} is not a number. Exiting.'.format(value))

            # Run as a double forked daemon
            if option in ('-d', '--daemon'):
                self.run_as_daemon = True
                # When running as daemon disable console_logging and don't start browser
                self.console_logging = False
                self.no_launch = True

                if sys.platform == 'win32' or sys.platform == 'darwin':
                    self.run_as_daemon = False

            # Write a pid file if requested
            if option in ('--pidfile',):
                self.create_pid = True
                self.pid_file = str(value)

                # If the pid file already exists, SickRage may still be running, so exit
                if ek(os.path.exists, self.pid_file):
                    sys.exit('PID file: {0} already exists. Exiting.'.format(self.pid_file))

            # Specify folder to load the config file from
            if option in ('--config',):
                sickbeard.CONFIG_FILE = ek(os.path.abspath, value)

            # Specify folder to use as the data directory
            if option in ('--datadir',):
                sickbeard.DATA_DIR = ek(os.path.abspath, value)

            # Prevent resizing of the banner/posters even if PIL is installed
            if option in ('--noresize',):
                sickbeard.NO_RESIZE = True

        # The pid file is only useful in daemon mode, make sure we can write the file properly
        if self.create_pid:
            if self.run_as_daemon:
                pid_dir = ek(os.path.dirname, self.pid_file)
                if not ek(os.access, pid_dir, os.F_OK):
                    sys.exit('PID dir: {0} doesn\'t exist. Exiting.'.format(pid_dir))
                if not ek(os.access, pid_dir, os.W_OK):
                    sys.exit('PID dir: {0} must be writable (write permissions). Exiting.'.format(pid_dir))

            else:
                if self.console_logging:
                    sys.stdout.write('Not running in daemon mode. PID file creation disabled.\n')

                self.create_pid = False

        # If they don't specify a config file then put it in the data dir
        if not sickbeard.CONFIG_FILE:
            sickbeard.CONFIG_FILE = ek(os.path.join, sickbeard.DATA_DIR, 'config.ini')

        # Make sure that we can create the data dir
        if not ek(os.access, sickbeard.DATA_DIR, os.F_OK):
            try:
                ek(os.makedirs, sickbeard.DATA_DIR, 0o744)
            except os.error:
                raise SystemExit('Unable to create data directory: {0}'.format(sickbeard.DATA_DIR))

        # Make sure we can write to the data dir
        if not ek(os.access, sickbeard.DATA_DIR, os.W_OK):
            raise SystemExit('Data directory must be writeable: {0}'.format(sickbeard.DATA_DIR))

        # Make sure we can write to the config file
        if not ek(os.access, sickbeard.CONFIG_FILE, os.W_OK):
            if ek(os.path.isfile, sickbeard.CONFIG_FILE):
                raise SystemExit('Config file must be writeable: {0}'.format(sickbeard.CONFIG_FILE))
            elif not ek(os.access, ek(os.path.dirname, sickbeard.CONFIG_FILE), os.W_OK):
                raise SystemExit('Config file root dir must be writeable: {0}'.format(ek(os.path.dirname, sickbeard.CONFIG_FILE)))

        ek(os.chdir, sickbeard.DATA_DIR)

        # Check if we need to perform a restore first
        restore_dir = ek(os.path.join, sickbeard.DATA_DIR, 'restore')
        if ek(os.path.exists, restore_dir):
            success = self.restore_db(restore_dir, sickbeard.DATA_DIR)
            if self.console_logging:
                sys.stdout.write('Restore: restoring DB and config.ini {0}!\n'.format(('FAILED', 'SUCCESSFUL')[success]))

        # Load the config and publish it to the sickbeard package
        if self.console_logging and not ek(os.path.isfile, sickbeard.CONFIG_FILE):
            sys.stdout.write('Unable to find {0}, all settings will be default!\n'.format(sickbeard.CONFIG_FILE))

        sickbeard.CFG = ConfigObj(sickbeard.CONFIG_FILE)

        # Initialize the config and our threads
        sickbeard.initialize(consoleLogging=self.console_logging)

        if self.run_as_daemon:
            self.daemonize()

        # Get PID
        sickbeard.PID = os.getpid()

        # Build from the DB to start with
        self.load_shows_from_db()

        logger.log('Starting SickRage [{branch}] using \'{config}\''.format
                   (branch=sickbeard.BRANCH, config=sickbeard.CONFIG_FILE))

        self.clear_cache()

        if self.forced_port:
            logger.log('Forcing web server to port {port}'.format(port=self.forced_port))
            self.start_port = self.forced_port
        else:
            self.start_port = sickbeard.WEB_PORT

        if sickbeard.WEB_LOG:
            self.log_dir = sickbeard.LOG_DIR
        else:
            self.log_dir = None

        # sickbeard.WEB_HOST is available as a configuration value in various
        # places but is not configurable. It is supported here for historic reasons.
        if sickbeard.WEB_HOST and sickbeard.WEB_HOST != '0.0.0.0':
            self.web_host = sickbeard.WEB_HOST
        else:
            self.web_host = '' if sickbeard.WEB_IPV6 else '0.0.0.0'

        # web server options
        self.web_options = {
            'port': int(self.start_port),
            'host': self.web_host,
            'data_root': ek(os.path.join, sickbeard.PROG_DIR, 'gui', sickbeard.GUI_NAME),
            'web_root': sickbeard.WEB_ROOT,
            'log_dir': self.log_dir,
            'username': sickbeard.WEB_USERNAME,
            'password': sickbeard.WEB_PASSWORD,
            'enable_https': sickbeard.ENABLE_HTTPS,
            'handle_reverse_proxy': sickbeard.HANDLE_REVERSE_PROXY,
            'https_cert': ek(os.path.join, sickbeard.PROG_DIR, sickbeard.HTTPS_CERT),
            'https_key': ek(os.path.join, sickbeard.PROG_DIR, sickbeard.HTTPS_KEY),
        }

        # start web server
        self.web_server = SRWebServer(self.web_options)
        self.web_server.start()

        # Fire up all our threads
        sickbeard.start()

        # Build internal name cache
        name_cache.buildNameCache()

        # Pre-populate network timezones, it isn't thread safe
        network_timezones.update_network_dict()

        # sure, why not?
        if sickbeard.USE_FAILED_DOWNLOADS:
            failed_history.trimHistory()

        # Check for metadata indexer updates for shows (sets the next aired ep!)
        # sickbeard.showUpdateScheduler.forceRun()

        # Launch browser
        if sickbeard.LAUNCH_BROWSER and not (self.no_launch or self.run_as_daemon):
            sickbeard.launchBrowser('https' if sickbeard.ENABLE_HTTPS else 'http', self.start_port, sickbeard.WEB_ROOT)

        # main loop
        while True:
            time.sleep(1)
Beispiel #8
0
    def run(self):

        ShowQueueItem.run(self)

        logger.log(u"Starting to add show {0}".format("by ShowDir: {0}".format(self.showDir) if self.showDir else "by Indexer Id: {0}".format(self.indexer_id)))
        # make sure the Indexer IDs are valid
        try:

            lINDEXER_API_PARMS = sickbeard.indexerApi(self.indexer).api_params.copy()
            if self.lang:
                lINDEXER_API_PARMS['language'] = self.lang

            logger.log(u"" + str(sickbeard.indexerApi(self.indexer).name) + ": " + repr(lINDEXER_API_PARMS))

            t = sickbeard.indexerApi(self.indexer).indexer(**lINDEXER_API_PARMS)
            s = t[self.indexer_id]

            # Let's try to create the show Dir if it's not provided. This way we force the show dir to build build using the
            # Indexers provided series name
            if not self.showDir and self.root_dir:
                show_name = get_showname_from_indexer(self.indexer, self.indexer_id, self.lang)
                if show_name:
                    self.showDir = ek(os.path.join, self.root_dir, sanitize_filename(show_name))
                    dir_exists = makeDir(self.showDir)
                    if not dir_exists:
                        logger.log(u"Unable to create the folder {0}, can't add the show".format(self.showDir))
                        return

                    chmodAsParent(self.showDir)
                else:
                    logger.log(u"Unable to get a show {0}, can't add the show".format(self.showDir))
                    return

            # this usually only happens if they have an NFO in their show dir which gave us a Indexer ID that has no proper english version of the show
            if getattr(s, 'seriesname', None) is None:
                logger.log(u"Show in {} has no name on {}, probably searched with the wrong language.".format
                           (self.showDir, sickbeard.indexerApi(self.indexer).name), logger.ERROR)

                ui.notifications.error("Unable to add show",
                                       "Show in " + self.showDir + " has no name on " + str(sickbeard.indexerApi(
                                           self.indexer).name) + ", probably the wrong language. Delete .nfo and add manually in the correct language.")
                self._finishEarly()
                return
            # if the show has no episodes/seasons
            if not s:
                logger.log(u"Show " + str(s['seriesname']) + " is on " + str(
                    sickbeard.indexerApi(self.indexer).name) + " but contains no season/episode data.")
                ui.notifications.error("Unable to add show",
                                       "Show " + str(s['seriesname']) + " is on " + str(sickbeard.indexerApi(
                                           self.indexer).name) + " but contains no season/episode data.")
                self._finishEarly()
                return
        except Exception as e:
            logger.log(u"%s Error while loading information from indexer %s. Error: %r" % (self.indexer_id, sickbeard.indexerApi(self.indexer).name, ex(e)), logger.ERROR)
            # logger.log(u"Show name with ID %s doesn't exist on %s anymore. If you are using trakt, it will be removed from your TRAKT watchlist. If you are adding manually, try removing the nfo and adding again" %
            #            (self.indexer_id, sickbeard.indexerApi(self.indexer).name), logger.WARNING)

            ui.notifications.error(
                "Unable to add show",
                "Unable to look up the show in %s on %s using ID %s, not using the NFO. Delete .nfo and try adding manually again." %
                (self.showDir, sickbeard.indexerApi(self.indexer).name, self.indexer_id)
            )

            if sickbeard.USE_TRAKT:

                trakt_id = sickbeard.indexerApi(self.indexer).config['trakt_id']
                trakt_api = TraktAPI(sickbeard.SSL_VERIFY, sickbeard.TRAKT_TIMEOUT)

                title = self.showDir.split("/")[-1]
                data = {
                    'shows': [
                        {
                            'title': title,
                            'ids': {}
                        }
                    ]
                }
                if trakt_id == 'tvdb_id':
                    data['shows'][0]['ids']['tvdb'] = self.indexer_id
                else:
                    data['shows'][0]['ids']['tvrage'] = self.indexer_id

                trakt_api.traktRequest("sync/watchlist/remove", data, method='POST')

            self._finishEarly()
            return

        try:
            newShow = TVShow(self.indexer, self.indexer_id, self.lang)
            newShow.load_from_indexer()

            self.show = newShow

            # set up initial values
            self.show.location = self.showDir
            self.show.subtitles = self.subtitles if self.subtitles is not None else sickbeard.SUBTITLES_DEFAULT
            self.show.quality = self.quality if self.quality else sickbeard.QUALITY_DEFAULT
            self.show.flatten_folders = self.flatten_folders if self.flatten_folders is not None else sickbeard.FLATTEN_FOLDERS_DEFAULT
            self.show.anime = self.anime if self.anime is not None else sickbeard.ANIME_DEFAULT
            self.show.scene = self.scene if self.scene is not None else sickbeard.SCENE_DEFAULT
            self.show.paused = self.paused if self.paused is not None else False

            # set up default new/missing episode status
            logger.log(u"Setting all episodes to the specified default status: " + str(self.show.default_ep_status))
            self.show.default_ep_status = self.default_status

            if self.show.anime:
                self.show.release_groups = BlackAndWhiteList(self.show.indexerid)
                if self.blacklist:
                    self.show.release_groups.set_black_keywords(self.blacklist)
                if self.whitelist:
                    self.show.release_groups.set_white_keywords(self.whitelist)

            # # be smartish about this
            # if self.show.genre and "talk show" in self.show.genre.lower():
            #     self.show.air_by_date = 1
            # if self.show.genre and "documentary" in self.show.genre.lower():
            #     self.show.air_by_date = 0
            # if self.show.classification and "sports" in self.show.classification.lower():
            #     self.show.sports = 1

        except sickbeard.indexer_exception as e:
            logger.log(
                u"Unable to add show due to an error with " + sickbeard.indexerApi(self.indexer).name + ": " + ex(e),
                logger.ERROR)
            if self.show:
                ui.notifications.error(
                    "Unable to add " + str(self.show.name) + " due to an error with " + sickbeard.indexerApi(
                        self.indexer).name + "")
            else:
                ui.notifications.error(
                    "Unable to add show due to an error with " + sickbeard.indexerApi(self.indexer).name + "")
            self._finishEarly()
            return

        except MultipleShowObjectsException:
            logger.log(u"The show in " + self.showDir + " is already in your show list, skipping", logger.WARNING)
            ui.notifications.error('Show skipped', "The show in " + self.showDir + " is already in your show list")
            self._finishEarly()
            return

        except Exception as e:
            logger.log(u"Error trying to add show: " + ex(e), logger.ERROR)
            logger.log(traceback.format_exc(), logger.DEBUG)
            self._finishEarly()
            raise

        logger.log(u"Retrieving show info from IMDb", logger.DEBUG)
        try:
            self.show.load_imdb_info()
        except imdb_exceptions.IMDbError as e:
            logger.log(u"Something wrong on IMDb api: " + ex(e), logger.WARNING)
        except Exception as e:
            logger.log(u"Error loading IMDb info: " + ex(e), logger.ERROR)

        try:
            self.show.save_to_db()
        except Exception as e:
            logger.log(u"Error saving the show to the database: " + ex(e), logger.ERROR)
            logger.log(traceback.format_exc(), logger.DEBUG)
            self._finishEarly()
            raise

        # add it to the show list
        sickbeard.showList.append(self.show)

        try:
            self.show.load_episodes_from_indexer()
        except Exception as e:
            logger.log(
                u"Error with " + sickbeard.indexerApi(self.show.indexer).name + ", not creating episode list: " + ex(e),
                logger.ERROR)
            logger.log(traceback.format_exc(), logger.DEBUG)

        # update internal name cache
        name_cache.buildNameCache(self.show)

        try:
            self.show.load_episodes_from_dir()
        except Exception as e:
            logger.log(u"Error searching dir for episodes: " + ex(e), logger.ERROR)
            logger.log(traceback.format_exc(), logger.DEBUG)

        # if they set default ep status to WANTED then run the backlog to search for episodes
        # FIXME: This needs to be a backlog queue item!!!
        if self.show.default_ep_status == WANTED:
            logger.log(u"Launching backlog for this show since its episodes are WANTED")
            sickbeard.backlogSearchScheduler.action.searchBacklog([self.show])

        self.show.write_metadata()
        self.show.update_metadata()
        self.show.populate_cache()

        self.show.flush_episodes()

        if sickbeard.USE_TRAKT:
            # if there are specific episodes that need to be added by trakt
            sickbeard.traktCheckerScheduler.action.manage_new_show(self.show)

            # add show to trakt.tv library
            if sickbeard.TRAKT_SYNC:
                sickbeard.traktCheckerScheduler.action.add_show_trakt_library(self.show)

            if sickbeard.TRAKT_SYNC_WATCHLIST:
                logger.log(u"update watchlist")
                notifiers.trakt_notifier.update_watchlist(show_obj=self.show)

        # Load XEM data to DB for show
        sickbeard.scene_numbering.xem_refresh(self.show.indexerid, self.show.indexer, force=True)

        # check if show has XEM mapping so we can determin if searches should go by scene numbering or indexer numbering.
        if not self.scene and sickbeard.scene_numbering.get_xem_numbering_for_show(self.show.indexerid,
                                                                                   self.show.indexer):
            self.show.scene = 1

        # After initial add, set to default_status_after.
        self.show.default_ep_status = self.default_status_after

        self.finish()
Beispiel #9
0
    def run(self):

        ShowQueueItem.run(self)

        logger.log(u'Starting to add show ' + self.showDir)
        # make sure the Indexer IDs are valid
        try:

            lINDEXER_API_PARMS = sickbeard.indexerApi(self.indexer).api_params.copy()
            if self.lang:
                lINDEXER_API_PARMS['language'] = self.lang

            logger.log(u'' + str(sickbeard.indexerApi(self.indexer).name) + ': ' + repr(lINDEXER_API_PARMS))

            t = sickbeard.indexerApi(self.indexer).indexer(**lINDEXER_API_PARMS)
            s = t[self.indexer_id]

            # this usually only happens if they have an NFO in their show dir which gave us a Indexer ID that has no proper english version of the show
            if getattr(s, 'seriesname', None) is None:
                logger.log(u'Show in ' + self.showDir + ' has no name on ' + str(
                    sickbeard.indexerApi(self.indexer).name) + ', probably the wrong language used to search with.',
                           logger.ERROR)
                ui.notifications.error('Unable to add show',
                                       'Show in ' + self.showDir + ' has no name on ' + str(sickbeard.indexerApi(
                                           self.indexer).name) + ', probably the wrong language. Delete .nfo and add manually in the correct language.')
                self._finishEarly()
                return
            # if the show has no episodes/seasons
            if not sickbeard.ALLOW_INCOMPLETE_SHOWDATA and not s:
                msg = u'Show %s is on %s but contains no season/episode data. Only the show folder was created.'\
                      % (s['seriesname'], sickbeard.indexerApi(self.indexer).name)
                logger.log(msg, logger.ERROR)
                ui.notifications.error('Unable to add show', msg)
                self._finishEarly()
                return
        except Exception as e:
            logger.log(u'Unable to find show ID:' + str(self.indexer_id) + ' on Indexer: ' + str(
                sickbeard.indexerApi(self.indexer).name), logger.ERROR)
            ui.notifications.error('Unable to add show',
                                   'Unable to look up the show in ' + self.showDir + ' on ' + str(sickbeard.indexerApi(
                                       self.indexer).name) + ' using ID ' + str(
                                       self.indexer_id) + ', not using the NFO. Delete .nfo and try adding manually again.')
            self._finishEarly()
            return

        try:
            newShow = TVShow(self.indexer, self.indexer_id, self.lang)
            newShow.loadFromIndexer()

            self.show = newShow

            # set up initial values
            self.show.location = self.showDir
            self.show.subtitles = self.subtitles if None is not self.subtitles else sickbeard.SUBTITLES_DEFAULT
            self.show.quality = self.quality if self.quality else sickbeard.QUALITY_DEFAULT
            self.show.flatten_folders = self.flatten_folders if None is not self.flatten_folders else sickbeard.FLATTEN_FOLDERS_DEFAULT
            self.show.anime = self.anime if None is not self.anime else sickbeard.ANIME_DEFAULT
            self.show.scene = self.scene if None is not self.scene else sickbeard.SCENE_DEFAULT
            self.show.paused = self.paused if None is not self.paused else False
            self.show.tag = self.tag if None is not self.tag else 'Show List'

            if self.show.anime:
                self.show.release_groups = BlackAndWhiteList(self.show.indexerid)
                if self.blacklist:
                    self.show.release_groups.set_black_keywords(self.blacklist)
                if self.whitelist:
                    self.show.release_groups.set_white_keywords(self.whitelist)

            # be smartish about this
            if self.show.genre and 'talk show' in self.show.genre.lower():
                self.show.air_by_date = 1
            if self.show.genre and 'documentary' in self.show.genre.lower():
                self.show.air_by_date = 0
            if self.show.classification and 'sports' in self.show.classification.lower():
                self.show.sports = 1

        except sickbeard.indexer_exception as e:
            logger.log(
                u'Unable to add show due to an error with ' + sickbeard.indexerApi(self.indexer).name + ': ' + ex(e),
                logger.ERROR)
            if self.show:
                ui.notifications.error(
                    'Unable to add ' + str(self.show.name) + ' due to an error with ' + sickbeard.indexerApi(
                        self.indexer).name + '')
            else:
                ui.notifications.error(
                    'Unable to add show due to an error with ' + sickbeard.indexerApi(self.indexer).name + '')
            self._finishEarly()
            return

        except exceptions.MultipleShowObjectsException:
            logger.log(u'The show in ' + self.showDir + ' is already in your show list, skipping', logger.ERROR)
            ui.notifications.error('Show skipped', 'The show in ' + self.showDir + ' is already in your show list')
            self._finishEarly()
            return

        except Exception as e:
            logger.log(u'Error trying to add show: ' + ex(e), logger.ERROR)
            logger.log(traceback.format_exc(), logger.DEBUG)
            self._finishEarly()
            raise

        self.show.load_imdb_info()

        try:
            self.show.saveToDB()
        except Exception as e:
            logger.log(u'Error saving the show to the database: ' + ex(e), logger.ERROR)
            logger.log(traceback.format_exc(), logger.DEBUG)
            self._finishEarly()
            raise

        # add it to the show list
        sickbeard.showList.append(self.show)

        try:
            self.show.loadEpisodesFromIndexer()
        except Exception as e:
            logger.log(
                u'Error with ' + sickbeard.indexerApi(self.show.indexer).name + ', not creating episode list: ' + ex(e),
                logger.ERROR)
            logger.log(traceback.format_exc(), logger.DEBUG)

        try:
            self.show.loadEpisodesFromDir()
        except Exception as e:
            logger.log(u'Error searching directory for episodes: ' + ex(e), logger.ERROR)
            logger.log(traceback.format_exc(), logger.DEBUG)

        # if they gave a custom status then change all the eps to it
        my_db = db.DBConnection()
        if self.default_status != SKIPPED:
            logger.log(u'Setting all episodes to the specified default status: ' + str(self.default_status))
            my_db.action('UPDATE tv_episodes SET status = ? WHERE status = ? AND showid = ? AND season != 0',
                        [self.default_status, SKIPPED, self.show.indexerid])

        # if they gave a number to start or number to end as wanted, then change those eps to it
        def get_wanted(db_obj, wanted_max, latest):
            actual = 0
            if wanted_max:
                select_id = 'FROM [tv_episodes] t5 JOIN (SELECT t3.indexerid, t3.status, t3.season*1000000+t3.episode AS t3_se, t2.start_season FROM [tv_episodes] t3'\
                            + ' JOIN (SELECT t1.showid, M%s(t1.season) AS start_season' % ('IN', 'AX')[latest]\
                            + ', MAX(t1.airdate) AS airdate, t1.episode, t1.season*1000000+t1.episode AS se FROM [tv_episodes] t1'\
                            + ' WHERE %s=t1.showid' % self.show.indexerid\
                            + ' AND 0<t1.season AND t1.status NOT IN (%s)) AS t2' % UNAIRED\
                            + ' ON t2.showid=t3.showid AND 0<t3.season AND t2.se>=t3_se ORDER BY t3_se %sSC' % ('A', 'DE')[latest]\
                            + ' %s) as t4' % (' LIMIT %s' % wanted_max, '')[-1 == wanted_max]\
                            + ' ON t4.indexerid=t5.indexerid'\
                            + '%s' % ('', ' AND t4.start_season=t5.season')[-1 == wanted_max]\
                            + ' AND t4.status NOT IN (%s)' % ','.join([str(x) for x in sickbeard.common.Quality.DOWNLOADED + [WANTED]])
                select = 'SELECT t5.indexerid as indexerid, t5.season as season, t5.episode as episode, t5.status as status ' + select_id
                update = 'UPDATE [tv_episodes] SET status=%s WHERE indexerid IN (SELECT t5.indexerid %s)' % (WANTED, select_id)

                wanted_updates = db_obj.select(select)
                db_obj.action(update)
                result = db_obj.select('SELECT changes() as last FROM [tv_episodes]')
                for cur_result in result:
                    actual = cur_result['last']
                    break

                action_log = 'didn\'t find any episodes that need to be set wanted'
                if actual:
                    action_log = ('updated %s %s episodes > %s'
                                  % ((((('%s of %s' % (actual, wanted_max)), ('%s of max %s limited' % (actual, wanted_max)))[10 == wanted_max]), ('max %s available' % actual))[-1 == wanted_max],
                                     ('first season', 'latest')[latest],
                                     ','.join([('S%02dE%02d=%d' % (a['season'], a['episode'], a['status'])) for a in wanted_updates])))
                logger.log('Get wanted ' + action_log)
            return actual

        items_wanted = get_wanted(my_db, self.default_wanted_begin, latest=False)
        items_wanted += get_wanted(my_db, self.default_wanted_latest, latest=True)

        msg = ' the specified show into ' + self.showDir
        # if started with WANTED eps then run the backlog
        if WANTED == self.default_status or items_wanted:
            logger.log(u'Launching backlog for this show since episodes are WANTED')
            sickbeard.backlogSearchScheduler.action.search_backlog([self.show])  #@UndefinedVariable
            ui.notifications.message('Show added/search', 'Adding and searching for episodes of' + msg)
        else:
            ui.notifications.message('Show added', 'Adding' + msg)

        self.show.writeMetadata()
        self.show.updateMetadata()
        self.show.populateCache()

        self.show.flushEpisodes()

        # if sickbeard.USE_TRAKT:
        #     # if there are specific episodes that need to be added by trakt
        #     sickbeard.traktCheckerScheduler.action.manageNewShow(self.show)
        #
        #     # add show to trakt.tv library
        #     if sickbeard.TRAKT_SYNC:
        #         sickbeard.traktCheckerScheduler.action.addShowToTraktLibrary(self.show)

        # Load XEM data to DB for show
        sickbeard.scene_numbering.xem_refresh(self.show.indexerid, self.show.indexer, force=True)
        # check if show has XEM mapping and if user disabled scene numbering during add show, output availability to log
        if not self.scene and self.show.indexerid in sickbeard.scene_exceptions.xem_ids_list[self.show.indexer]:
            logger.log(u'Alternative scene episode numbers were disabled during add show. Edit show to enable them for searching.')

        # update internal name cache
        name_cache.buildNameCache(self.show)

        self.finish()
Beispiel #10
0
    def run(self):

        ShowQueueItem.run(self)

        logger.log(u"Starting to add show {0}".format(
            "by ShowDir: {0}".format(self.showDir) if self.
            showDir else "by Indexer Id: {0}".format(self.indexer_id)))
        # make sure the Indexer IDs are valid
        try:

            lINDEXER_API_PARMS = sickbeard.indexerApi(
                self.indexer).api_params.copy()
            if self.lang:
                lINDEXER_API_PARMS['language'] = self.lang

            logger.log(u"" + str(sickbeard.indexerApi(self.indexer).name) +
                       ": " + repr(lINDEXER_API_PARMS))

            t = sickbeard.indexerApi(
                self.indexer).indexer(**lINDEXER_API_PARMS)
            s = t[self.indexer_id]

            # Let's try to create the show Dir if it's not provided. This way we force the show dir to build build using the
            # Indexers provided series name
            if not self.showDir and self.root_dir:
                show_name = get_showname_from_indexer(self.indexer,
                                                      self.indexer_id,
                                                      self.lang)
                if show_name:
                    self.showDir = ek(os.path.join, self.root_dir,
                                      sanitize_filename(show_name))
                    dir_exists = makeDir(self.showDir)
                    if not dir_exists:
                        logger.log(
                            u"Unable to create the folder {0}, can't add the show"
                            .format(self.showDir))
                        return

                    chmodAsParent(self.showDir)
                else:
                    logger.log(
                        u"Unable to get a show {0}, can't add the show".format(
                            self.showDir))
                    return

            # this usually only happens if they have an NFO in their show dir which gave us a Indexer ID that has no proper english version of the show
            if getattr(s, 'seriesname', None) is None:
                logger.log(
                    u"Show in {} has no name on {}, probably searched with the wrong language."
                    .format(self.showDir,
                            sickbeard.indexerApi(self.indexer).name),
                    logger.ERROR)

                ui.notifications.error(
                    "Unable to add show",
                    "Show in " + self.showDir + " has no name on " +
                    str(sickbeard.indexerApi(self.indexer).name) +
                    ", probably the wrong language. Delete .nfo and add manually in the correct language."
                )
                self._finishEarly()
                return
            # if the show has no episodes/seasons
            if not s:
                logger.log(u"Show " + str(s['seriesname']) + " is on " +
                           str(sickbeard.indexerApi(self.indexer).name) +
                           " but contains no season/episode data.")
                ui.notifications.error(
                    "Unable to add show", "Show " + str(s['seriesname']) +
                    " is on " + str(sickbeard.indexerApi(self.indexer).name) +
                    " but contains no season/episode data.")
                self._finishEarly()
                return
        except Exception as e:
            logger.log(
                u"%s Error while loading information from indexer %s. Error: %r"
                % (self.indexer_id, sickbeard.indexerApi(
                    self.indexer).name, ex(e)), logger.ERROR)
            # logger.log(u"Show name with ID %s doesn't exist on %s anymore. If you are using trakt, it will be removed from your TRAKT watchlist. If you are adding manually, try removing the nfo and adding again" %
            #            (self.indexer_id, sickbeard.indexerApi(self.indexer).name), logger.WARNING)

            ui.notifications.error(
                "Unable to add show",
                "Unable to look up the show in %s on %s using ID %s, not using the NFO. Delete .nfo and try adding manually again."
                % (self.showDir, sickbeard.indexerApi(
                    self.indexer).name, self.indexer_id))

            if sickbeard.USE_TRAKT:

                trakt_id = sickbeard.indexerApi(
                    self.indexer).config['trakt_id']
                trakt_api = TraktAPI(sickbeard.SSL_VERIFY,
                                     sickbeard.TRAKT_TIMEOUT)

                title = self.showDir.split("/")[-1]
                data = {'shows': [{'title': title, 'ids': {}}]}
                if trakt_id == 'tvdb_id':
                    data['shows'][0]['ids']['tvdb'] = self.indexer_id
                else:
                    data['shows'][0]['ids']['tvrage'] = self.indexer_id

                trakt_api.traktRequest("sync/watchlist/remove",
                                       data,
                                       method='POST')

            self._finishEarly()
            return

        try:
            newShow = TVShow(self.indexer, self.indexer_id, self.lang)
            newShow.loadFromIndexer()

            self.show = newShow

            # set up initial values
            self.show.location = self.showDir
            self.show.subtitles = self.subtitles if self.subtitles is not None else sickbeard.SUBTITLES_DEFAULT
            self.show.quality = self.quality if self.quality else sickbeard.QUALITY_DEFAULT
            self.show.flatten_folders = self.flatten_folders if self.flatten_folders is not None else sickbeard.FLATTEN_FOLDERS_DEFAULT
            self.show.anime = self.anime if self.anime is not None else sickbeard.ANIME_DEFAULT
            self.show.scene = self.scene if self.scene is not None else sickbeard.SCENE_DEFAULT
            self.show.paused = self.paused if self.paused is not None else False

            # set up default new/missing episode status
            logger.log(
                u"Setting all episodes to the specified default status: " +
                str(self.show.default_ep_status))
            self.show.default_ep_status = self.default_status

            if self.show.anime:
                self.show.release_groups = BlackAndWhiteList(
                    self.show.indexerid)
                if self.blacklist:
                    self.show.release_groups.set_black_keywords(self.blacklist)
                if self.whitelist:
                    self.show.release_groups.set_white_keywords(self.whitelist)

            # # be smartish about this
            # if self.show.genre and "talk show" in self.show.genre.lower():
            #     self.show.air_by_date = 1
            # if self.show.genre and "documentary" in self.show.genre.lower():
            #     self.show.air_by_date = 0
            # if self.show.classification and "sports" in self.show.classification.lower():
            #     self.show.sports = 1

        except sickbeard.indexer_exception as e:
            logger.log(
                u"Unable to add show due to an error with " +
                sickbeard.indexerApi(self.indexer).name + ": " + ex(e),
                logger.ERROR)
            if self.show:
                ui.notifications.error(
                    "Unable to add " + str(self.show.name) +
                    " due to an error with " +
                    sickbeard.indexerApi(self.indexer).name + "")
            else:
                ui.notifications.error(
                    "Unable to add show due to an error with " +
                    sickbeard.indexerApi(self.indexer).name + "")
            self._finishEarly()
            return

        except MultipleShowObjectsException:
            logger.log(
                u"The show in " + self.showDir +
                " is already in your show list, skipping", logger.WARNING)
            ui.notifications.error(
                'Show skipped', "The show in " + self.showDir +
                " is already in your show list")
            self._finishEarly()
            return

        except Exception as e:
            logger.log(u"Error trying to add show: " + ex(e), logger.ERROR)
            logger.log(traceback.format_exc(), logger.DEBUG)
            self._finishEarly()
            raise

        logger.log(u"Retrieving show info from IMDb", logger.DEBUG)
        try:
            self.show.loadIMDbInfo()
        except imdb_exceptions.IMDbError as e:
            logger.log(u" Something wrong on IMDb api: " + ex(e),
                       logger.WARNING)
        except Exception as e:
            logger.log(u"Error loading IMDb info: " + ex(e), logger.ERROR)

        try:
            self.show.saveToDB()
        except Exception as e:
            logger.log(u"Error saving the show to the database: " + ex(e),
                       logger.ERROR)
            logger.log(traceback.format_exc(), logger.DEBUG)
            self._finishEarly()
            raise

        # add it to the show list
        sickbeard.showList.append(self.show)

        try:
            self.show.loadEpisodesFromIndexer()
        except Exception as e:
            logger.log(
                u"Error with " + sickbeard.indexerApi(self.show.indexer).name +
                ", not creating episode list: " + ex(e), logger.ERROR)
            logger.log(traceback.format_exc(), logger.DEBUG)

        # update internal name cache
        name_cache.buildNameCache(self.show)

        try:
            self.show.loadEpisodesFromDir()
        except Exception as e:
            logger.log(u"Error searching dir for episodes: " + ex(e),
                       logger.ERROR)
            logger.log(traceback.format_exc(), logger.DEBUG)

        # if they set default ep status to WANTED then run the backlog to search for episodes
        # FIXME: This needs to be a backlog queue item!!!
        if self.show.default_ep_status == WANTED:
            logger.log(
                u"Launching backlog for this show since its episodes are WANTED"
            )
            sickbeard.backlogSearchScheduler.action.searchBacklog([self.show])

        self.show.writeMetadata()
        self.show.updateMetadata()
        self.show.populateCache()

        self.show.flushEpisodes()

        if sickbeard.USE_TRAKT:
            # if there are specific episodes that need to be added by trakt
            sickbeard.traktCheckerScheduler.action.manage_new_show(self.show)

            # add show to trakt.tv library
            if sickbeard.TRAKT_SYNC:
                sickbeard.traktCheckerScheduler.action.add_show_watchlist(
                    self.show)

            if sickbeard.TRAKT_SYNC_WATCHLIST:
                logger.log(u"update watchlist")
                notifiers.trakt_notifier.update_watchlist(show_obj=self.show)

        # Load XEM data to DB for show
        sickbeard.scene_numbering.xem_refresh(self.show.indexerid,
                                              self.show.indexer,
                                              force=True)

        # check if show has XEM mapping so we can determin if searches should go by scene numbering or indexer numbering.
        if not self.scene and sickbeard.scene_numbering.get_xem_numbering_for_show(
                self.show.indexerid, self.show.indexer):
            self.show.scene = 1

        # After initial add, set to default_status_after.
        self.show.default_ep_status = self.default_status_after

        self.finish()
Beispiel #11
0
    def setUp(self):
        super(SceneExceptionTestCase, self).setUp()

        sickbeard.showList = [Show(1, 79604), Show(1, 251085)]
        scene_exceptions.retrieve_exceptions()
        name_cache.buildNameCache()
Beispiel #12
0
def searchProviders(show, episodes, manualSearch=False, downCurQuality=False):
    """
    Walk providers for information on shows

    :param show: Show we are looking for
    :param episodes: Episodes we hope to find
    :param manualSearch: Boolean, is this a manual search?
    :param downCurQuality: Boolean, should we redownload currently avaialble quality file
    :return: results for search
    """
    foundResults = {}
    finalResults = []

    didSearch = False
    threads = []

    # build name cache for show
    name_cache.buildNameCache(show)

    origThreadName = threading.currentThread().name

    providers = [
        x for x in sickbeard.providers.sortedProviderList(
            sickbeard.RANDOMIZE_PROVIDERS) if x.isActive() and x.enable_backlog
    ]
    for curProvider in providers:
        threads += [
            threading.Thread(target=curProvider.cache.updateCache,
                             name=origThreadName + " :: [" + curProvider.name +
                             "]")
        ]

    # start the thread we just created
    for t in threads:
        t.start()

    # wait for all threads to finish
    for t in threads:
        t.join()

    for providerNum, curProvider in enumerate(providers):
        if curProvider.anime_only and not show.is_anime:
            logging.debug("" + str(show.name) + " is not an anime, skiping")
            continue

        threading.currentThread(
        ).name = origThreadName + " :: [" + curProvider.name + "]"

        foundResults[curProvider.name] = {}

        searchCount = 0
        search_mode = curProvider.search_mode

        # Always search for episode when manually searching when in sponly
        if search_mode == 'sponly' and manualSearch == True:
            search_mode = 'eponly'

        while True:
            searchCount += 1

            if search_mode == 'eponly':
                logging.info("Performing episode search for " + show.name)
            else:
                logging.info("Performing season pack search for " + show.name)

            try:
                searchResults = curProvider.findSearchResults(
                    show, episodes, search_mode, manualSearch, downCurQuality)
            except AuthException as e:
                logging.error("Authentication error: {}".format(ex(e)))
                break
            except Exception as e:
                logging.error("Error while searching " + curProvider.name +
                              ", skipping: {}".format(ex(e)))
                logging.debug(traceback.format_exc())
                break

            didSearch = True

            if len(searchResults):
                # make a list of all the results for this provider
                for curEp in searchResults:
                    if curEp in foundResults:
                        foundResults[
                            curProvider.name][curEp] += searchResults[curEp]
                    else:
                        foundResults[
                            curProvider.name][curEp] = searchResults[curEp]

                break
            elif not curProvider.search_fallback or searchCount == 2:
                break

            if search_mode == 'sponly':
                logging.debug("Fallback episode search initiated")
                search_mode = 'eponly'
            else:
                logging.debug("Fallback season pack search initiate")
                search_mode = 'sponly'

        # skip to next provider if we have no results to process
        if not len(foundResults[curProvider.name]):
            continue

        # pick the best season NZB
        bestSeasonResult = None
        if SEASON_RESULT in foundResults[curProvider.name]:
            bestSeasonResult = pickBestResult(
                foundResults[curProvider.name][SEASON_RESULT], show)

        highest_quality_overall = 0
        for cur_episode in foundResults[curProvider.name]:
            for cur_result in foundResults[curProvider.name][cur_episode]:
                if cur_result.quality != Quality.UNKNOWN and cur_result.quality > highest_quality_overall:
                    highest_quality_overall = cur_result.quality
        logging.debug("The highest quality of any match is " +
                      Quality.qualityStrings[highest_quality_overall])

        # see if every episode is wanted
        if bestSeasonResult:
            searchedSeasons = [str(x.season) for x in episodes]

            # get the quality of the season nzb
            seasonQual = bestSeasonResult.quality
            logging.debug("The quality of the season " +
                          bestSeasonResult.provider.providerType + " is " +
                          Quality.qualityStrings[seasonQual])

            myDB = db.DBConnection()
            allEps = [
                int(x[b"episode"]) for x in myDB.select(
                    "SELECT episode FROM tv_episodes WHERE showid = ? AND ( season IN ( "
                    + ','.join(searchedSeasons) + " ) )", [show.indexerid])
            ]

            logging.info(
                "Executed query: [SELECT episode FROM tv_episodes WHERE showid = %s AND season in  %s]"
                % (show.indexerid, ','.join(searchedSeasons)))
            logging.debug("Episode list: " + str(allEps))

            allWanted = True
            anyWanted = False
            for curEpNum in allEps:
                for season in set([x.season for x in episodes]):
                    if not show.wantEpisode(season, curEpNum, seasonQual,
                                            downCurQuality):
                        allWanted = False
                    else:
                        anyWanted = True

            # if we need every ep in the season and there's nothing better then just download this and be done with it (unless single episodes are preferred)
            if allWanted and bestSeasonResult.quality == highest_quality_overall:
                logging.info(
                    "Every ep in this season is needed, downloading the whole "
                    + bestSeasonResult.provider.providerType + " " +
                    bestSeasonResult.name)
                epObjs = []
                for curEpNum in allEps:
                    for season in set([x.season for x in episodes]):
                        epObjs.append(show.getEpisode(season, curEpNum))
                bestSeasonResult.episodes = epObjs

                return [bestSeasonResult]

            elif not anyWanted:
                logging.debug(
                    "No eps from this season are wanted at this quality, ignoring the result of "
                    + bestSeasonResult.name)

            else:

                if bestSeasonResult.provider.providerType == GenericProvider.NZB:
                    logging.debug(
                        "Breaking apart the NZB and adding the individual ones to our results"
                    )

                    # if not, break it apart and add them as the lowest priority results
                    individualResults = nzbSplitter.splitResult(
                        bestSeasonResult)
                    for curResult in individualResults:
                        if len(curResult.episodes) == 1:
                            epNum = curResult.episodes[0].episode
                        elif len(curResult.episodes) > 1:
                            epNum = MULTI_EP_RESULT

                        if epNum in foundResults[curProvider.name]:
                            foundResults[curProvider.name][epNum].append(
                                curResult)
                        else:
                            foundResults[curProvider.name][epNum] = [curResult]

                # If this is a torrent all we can do is leech the entire torrent, user will have to select which eps not do download in his torrent client
                else:

                    # Season result from Torrent Provider must be a full-season torrent, creating multi-ep result for it.
                    logging.info(
                        "Adding multi-ep result for full-season torrent. Set the episodes you don't want to 'don't download' in your torrent client if desired!"
                    )
                    epObjs = []
                    for curEpNum in allEps:
                        for season in set([x.season for x in episodes]):
                            epObjs.append(show.getEpisode(season, curEpNum))
                    bestSeasonResult.episodes = epObjs

                    if MULTI_EP_RESULT in foundResults[curProvider.name]:
                        foundResults[curProvider.name][MULTI_EP_RESULT].append(
                            bestSeasonResult)
                    else:
                        foundResults[curProvider.name][MULTI_EP_RESULT] = [
                            bestSeasonResult
                        ]

        # go through multi-ep results and see if we really want them or not, get rid of the rest
        multiResults = {}
        if MULTI_EP_RESULT in foundResults[curProvider.name]:
            for _multiResult in foundResults[
                    curProvider.name][MULTI_EP_RESULT]:

                logging.debug(
                    "Seeing if we want to bother with multi-episode result " +
                    _multiResult.name)

                # Filter result by ignore/required/whitelist/blacklist/quality, etc
                multiResult = pickBestResult(_multiResult, show)
                if not multiResult:
                    continue

                # see how many of the eps that this result covers aren't covered by single results
                neededEps = []
                notNeededEps = []
                for epObj in multiResult.episodes:
                    # if we have results for the episode
                    if epObj.episode in foundResults[curProvider.name] and len(
                            foundResults[curProvider.name][epObj.episode]) > 0:
                        notNeededEps.append(epObj.episode)
                    else:
                        neededEps.append(epObj.episode)

                logging.debug("Single-ep check result is neededEps: " +
                              str(neededEps) + ", notNeededEps: " +
                              str(notNeededEps))

                if not neededEps:
                    logging.debug(
                        "All of these episodes were covered by single episode results, ignoring this multi-episode result"
                    )
                    continue

                # check if these eps are already covered by another multi-result
                multiNeededEps = []
                multiNotNeededEps = []
                for epObj in multiResult.episodes:
                    if epObj.episode in multiResults:
                        multiNotNeededEps.append(epObj.episode)
                    else:
                        multiNeededEps.append(epObj.episode)

                logging.debug("Multi-ep check result is multiNeededEps: " +
                              str(multiNeededEps) + ", multiNotNeededEps: " +
                              str(multiNotNeededEps))

                if not multiNeededEps:
                    logging.debug(
                        "All of these episodes were covered by another multi-episode nzbs, ignoring this multi-ep result"
                    )
                    continue

                # don't bother with the single result if we're going to get it with a multi result
                for epObj in multiResult.episodes:
                    multiResults[epObj.episode] = multiResult
                    if epObj.episode in foundResults[curProvider.name]:
                        logging.debug(
                            "A needed multi-episode result overlaps with a single-episode result for ep #"
                            + str(epObj.episode) +
                            ", removing the single-episode results from the list"
                        )
                        del foundResults[curProvider.name][epObj.episode]

        # of all the single ep results narrow it down to the best one for each episode
        finalResults += set(multiResults.values())
        for curEp in foundResults[curProvider.name]:
            if curEp in (MULTI_EP_RESULT, SEASON_RESULT):
                continue

            if not len(foundResults[curProvider.name][curEp]) > 0:
                continue

            # if all results were rejected move on to the next episode
            bestResult = pickBestResult(foundResults[curProvider.name][curEp],
                                        show)
            if not bestResult:
                continue

            # add result if its not a duplicate and
            found = False
            for i, result in enumerate(finalResults):
                for bestResultEp in bestResult.episodes:
                    if bestResultEp in result.episodes:
                        if result.quality < bestResult.quality:
                            finalResults.pop(i)
                        else:
                            found = True
            if not found:
                finalResults += [bestResult]

        # check that we got all the episodes we wanted first before doing a match and snatch
        wantedEpCount = 0
        for wantedEp in episodes:
            for result in finalResults:
                if wantedEp in result.episodes and isFinalResult(result):
                    wantedEpCount += 1

        # make sure we search every provider for results unless we found everything we wanted
        if wantedEpCount == len(episodes):
            break

    if not didSearch:
        logging.warning(
            "No NZB/Torrent providers found or enabled in the sickrage config for backlog searches. Please check your settings."
        )

    return finalResults
Beispiel #13
0
def searchProviders(show, episodes, manualSearch=False, downCurQuality=False):
    """
    Walk providers for information on shows

    :param show: Show we are looking for
    :param episodes: Episodes we hope to find
    :param manualSearch: Boolean, is this a manual search?
    :param downCurQuality: Boolean, should we redownload currently avaialble quality file
    :return: results for search
    """
    foundResults = {}
    finalResults = []

    didSearch = False
    threads = []

    # build name cache for show
    name_cache.buildNameCache(show)

    origThreadName = threading.currentThread().name

    providers = [x for x in sickbeard.providers.sortedProviderList(sickbeard.RANDOMIZE_PROVIDERS) if
                 x.isActive() and x.enable_backlog]
    for curProvider in providers:
        threads += [threading.Thread(target=curProvider.cache.updateCache,
                                     name=origThreadName + " :: [" + curProvider.name + "]")]

    # start the thread we just created
    for t in threads:
        t.start()

    # wait for all threads to finish
    for t in threads:
        t.join()

    for providerNum, curProvider in enumerate(providers):
        if curProvider.anime_only and not show.is_anime:
            logging.debug("" + str(show.name) + " is not an anime, skiping")
            continue

        threading.currentThread().name = origThreadName + " :: [" + curProvider.name + "]"

        foundResults[curProvider.name] = {}

        searchCount = 0
        search_mode = curProvider.search_mode

        # Always search for episode when manually searching when in sponly
        if search_mode == 'sponly' and manualSearch == True:
            search_mode = 'eponly'

        while True:
            searchCount += 1

            if search_mode == 'eponly':
                logging.info("Performing episode search for " + show.name)
            else:
                logging.info("Performing season pack search for " + show.name)

            try:
                searchResults = curProvider.findSearchResults(show, episodes, search_mode, manualSearch, downCurQuality)
            except AuthException as e:
                logging.error("Authentication error: {}".format(ex(e)))
                break
            except Exception as e:
                logging.error("Error while searching " + curProvider.name + ", skipping: {}".format(ex(e)))
                logging.debug(traceback.format_exc())
                break

            didSearch = True

            if len(searchResults):
                # make a list of all the results for this provider
                for curEp in searchResults:
                    if curEp in foundResults:
                        foundResults[curProvider.name][curEp] += searchResults[curEp]
                    else:
                        foundResults[curProvider.name][curEp] = searchResults[curEp]

                break
            elif not curProvider.search_fallback or searchCount == 2:
                break

            if search_mode == 'sponly':
                logging.debug("Fallback episode search initiated")
                search_mode = 'eponly'
            else:
                logging.debug("Fallback season pack search initiate")
                search_mode = 'sponly'

        # skip to next provider if we have no results to process
        if not len(foundResults[curProvider.name]):
            continue

        # pick the best season NZB
        bestSeasonResult = None
        if SEASON_RESULT in foundResults[curProvider.name]:
            bestSeasonResult = pickBestResult(foundResults[curProvider.name][SEASON_RESULT], show)

        highest_quality_overall = 0
        for cur_episode in foundResults[curProvider.name]:
            for cur_result in foundResults[curProvider.name][cur_episode]:
                if cur_result.quality != Quality.UNKNOWN and cur_result.quality > highest_quality_overall:
                    highest_quality_overall = cur_result.quality
        logging.debug("The highest quality of any match is " + Quality.qualityStrings[highest_quality_overall])

        # see if every episode is wanted
        if bestSeasonResult:
            searchedSeasons = [str(x.season) for x in episodes]

            # get the quality of the season nzb
            seasonQual = bestSeasonResult.quality
            logging.debug(
                    "The quality of the season " + bestSeasonResult.provider.providerType + " is " +
                    Quality.qualityStrings[
                        seasonQual])

            myDB = db.DBConnection()
            allEps = [int(x[b"episode"])
                      for x in myDB.select(
                    "SELECT episode FROM tv_episodes WHERE showid = ? AND ( season IN ( " + ','.join(
                        searchedSeasons) + " ) )",
                    [show.indexerid])]

            logging.info("Executed query: [SELECT episode FROM tv_episodes WHERE showid = %s AND season in  %s]" % (
            show.indexerid, ','.join(searchedSeasons)))
            logging.debug("Episode list: " + str(allEps))

            allWanted = True
            anyWanted = False
            for curEpNum in allEps:
                for season in set([x.season for x in episodes]):
                    if not show.wantEpisode(season, curEpNum, seasonQual, downCurQuality):
                        allWanted = False
                    else:
                        anyWanted = True

            # if we need every ep in the season and there's nothing better then just download this and be done with it (unless single episodes are preferred)
            if allWanted and bestSeasonResult.quality == highest_quality_overall:
                logging.info(
                        "Every ep in this season is needed, downloading the whole " + bestSeasonResult.provider.providerType + " " + bestSeasonResult.name)
                epObjs = []
                for curEpNum in allEps:
                    for season in set([x.season for x in episodes]):
                        epObjs.append(show.getEpisode(season, curEpNum))
                bestSeasonResult.episodes = epObjs

                return [bestSeasonResult]

            elif not anyWanted:
                logging.debug(
                        "No eps from this season are wanted at this quality, ignoring the result of " + bestSeasonResult.name)

            else:

                if bestSeasonResult.provider.providerType == GenericProvider.NZB:
                    logging.debug("Breaking apart the NZB and adding the individual ones to our results")

                    # if not, break it apart and add them as the lowest priority results
                    individualResults = nzbSplitter.splitResult(bestSeasonResult)
                    for curResult in individualResults:
                        if len(curResult.episodes) == 1:
                            epNum = curResult.episodes[0].episode
                        elif len(curResult.episodes) > 1:
                            epNum = MULTI_EP_RESULT

                        if epNum in foundResults[curProvider.name]:
                            foundResults[curProvider.name][epNum].append(curResult)
                        else:
                            foundResults[curProvider.name][epNum] = [curResult]

                # If this is a torrent all we can do is leech the entire torrent, user will have to select which eps not do download in his torrent client
                else:

                    # Season result from Torrent Provider must be a full-season torrent, creating multi-ep result for it.
                    logging.info(
                            "Adding multi-ep result for full-season torrent. Set the episodes you don't want to 'don't download' in your torrent client if desired!")
                    epObjs = []
                    for curEpNum in allEps:
                        for season in set([x.season for x in episodes]):
                            epObjs.append(show.getEpisode(season, curEpNum))
                    bestSeasonResult.episodes = epObjs

                    if MULTI_EP_RESULT in foundResults[curProvider.name]:
                        foundResults[curProvider.name][MULTI_EP_RESULT].append(bestSeasonResult)
                    else:
                        foundResults[curProvider.name][MULTI_EP_RESULT] = [bestSeasonResult]

        # go through multi-ep results and see if we really want them or not, get rid of the rest
        multiResults = {}
        if MULTI_EP_RESULT in foundResults[curProvider.name]:
            for _multiResult in foundResults[curProvider.name][MULTI_EP_RESULT]:

                logging.debug("Seeing if we want to bother with multi-episode result " + _multiResult.name)

                # Filter result by ignore/required/whitelist/blacklist/quality, etc
                multiResult = pickBestResult(_multiResult, show)
                if not multiResult:
                    continue

                # see how many of the eps that this result covers aren't covered by single results
                neededEps = []
                notNeededEps = []
                for epObj in multiResult.episodes:
                    # if we have results for the episode
                    if epObj.episode in foundResults[curProvider.name] and len(
                            foundResults[curProvider.name][epObj.episode]) > 0:
                        notNeededEps.append(epObj.episode)
                    else:
                        neededEps.append(epObj.episode)

                logging.debug(
                        "Single-ep check result is neededEps: " + str(neededEps) + ", notNeededEps: " + str(
                            notNeededEps))

                if not neededEps:
                    logging.debug(
                        "All of these episodes were covered by single episode results, ignoring this multi-episode result")
                    continue

                # check if these eps are already covered by another multi-result
                multiNeededEps = []
                multiNotNeededEps = []
                for epObj in multiResult.episodes:
                    if epObj.episode in multiResults:
                        multiNotNeededEps.append(epObj.episode)
                    else:
                        multiNeededEps.append(epObj.episode)

                logging.debug(
                        "Multi-ep check result is multiNeededEps: " + str(
                            multiNeededEps) + ", multiNotNeededEps: " + str(
                                multiNotNeededEps))

                if not multiNeededEps:
                    logging.debug(
                            "All of these episodes were covered by another multi-episode nzbs, ignoring this multi-ep result")
                    continue

                # don't bother with the single result if we're going to get it with a multi result
                for epObj in multiResult.episodes:
                    multiResults[epObj.episode] = multiResult
                    if epObj.episode in foundResults[curProvider.name]:
                        logging.debug(
                                "A needed multi-episode result overlaps with a single-episode result for ep #" + str(
                                        epObj.episode) + ", removing the single-episode results from the list")
                        del foundResults[curProvider.name][epObj.episode]

        # of all the single ep results narrow it down to the best one for each episode
        finalResults += set(multiResults.values())
        for curEp in foundResults[curProvider.name]:
            if curEp in (MULTI_EP_RESULT, SEASON_RESULT):
                continue

            if not len(foundResults[curProvider.name][curEp]) > 0:
                continue

            # if all results were rejected move on to the next episode
            bestResult = pickBestResult(foundResults[curProvider.name][curEp], show)
            if not bestResult:
                continue

            # add result if its not a duplicate and
            found = False
            for i, result in enumerate(finalResults):
                for bestResultEp in bestResult.episodes:
                    if bestResultEp in result.episodes:
                        if result.quality < bestResult.quality:
                            finalResults.pop(i)
                        else:
                            found = True
            if not found:
                finalResults += [bestResult]

        # check that we got all the episodes we wanted first before doing a match and snatch
        wantedEpCount = 0
        for wantedEp in episodes:
            for result in finalResults:
                if wantedEp in result.episodes and isFinalResult(result):
                    wantedEpCount += 1

        # make sure we search every provider for results unless we found everything we wanted
        if wantedEpCount == len(episodes):
            break

    if not didSearch:
        logging.warning(
            "No NZB/Torrent providers found or enabled in the sickrage config for backlog searches. Please check your settings.")

    return finalResults
Beispiel #14
0
    def start(self):
        # do some preliminary stuff
        sickbeard.MY_FULLNAME = os.path.normpath(os.path.abspath(__file__))
        sickbeard.MY_NAME = os.path.basename(sickbeard.MY_FULLNAME)
        sickbeard.PROG_DIR = os.path.dirname(sickbeard.MY_FULLNAME)
        sickbeard.DATA_DIR = sickbeard.PROG_DIR
        sickbeard.MY_ARGS = sys.argv[1:]
        sickbeard.SYS_ENCODING = None

        try:
            locale.setlocale(locale.LC_ALL, '')
        except (locale.Error, IOError):
            pass
        try:
            sickbeard.SYS_ENCODING = locale.getpreferredencoding()
        except (locale.Error, IOError):
            pass

        # For OSes that are poorly configured I'll just randomly force UTF-8
        if not sickbeard.SYS_ENCODING or sickbeard.SYS_ENCODING in ('ANSI_X3.4-1968', 'US-ASCII', 'ASCII'):
            sickbeard.SYS_ENCODING = 'UTF-8'

        if not hasattr(sys, 'setdefaultencoding'):
            moves.reload_module(sys)

        try:
            # pylint: disable=E1101
            # On non-unicode builds this will raise an AttributeError, if encoding type is not valid it throws a LookupError
            sys.setdefaultencoding(sickbeard.SYS_ENCODING)
        except:
            print('Sorry, you MUST add the SickGear folder to the PYTHONPATH environment variable')
            print('or find another way to force Python to use %s for string encoding.' % sickbeard.SYS_ENCODING)
            sys.exit(1)

        # Need console logging for SickBeard.py and SickBeard-console.exe
        self.consoleLogging = (not hasattr(sys, 'frozen')) or (sickbeard.MY_NAME.lower().find('-console') > 0)

        # Rename the main thread
        threading.currentThread().name = 'MAIN'

        try:
            opts, args = getopt.getopt(sys.argv[1:], 'hfqdp::',
                                       ['help', 'forceupdate', 'quiet', 'nolaunch', 'daemon', 'pidfile=', 'port=',
                                        'datadir=', 'config=', 'noresize'])  # @UnusedVariable
        except getopt.GetoptError:
            sys.exit(self.help_message())

        for o, a in opts:
            # Prints help message
            if o in ('-h', '--help'):
                sys.exit(self.help_message())

            # For now we'll just silence the logging
            if o in ('-q', '--quiet'):
                self.consoleLogging = False

            # Should we update (from indexer) all shows in the DB right away?
            if o in ('-f', '--forceupdate'):
                self.forceUpdate = True

            # Suppress launching web browser
            # Needed for OSes without default browser assigned
            # Prevent duplicate browser window when restarting in the app
            if o in ('--nolaunch',):
                self.noLaunch = True

            # Override default/configured port
            if o in ('-p', '--port'):
                try:
                    self.forcedPort = int(a)
                except ValueError:
                    sys.exit('Port: %s is not a number. Exiting.' % a)

            # Run as a double forked daemon
            if o in ('-d', '--daemon'):
                self.runAsDaemon = True
                # When running as daemon disable consoleLogging and don't start browser
                self.consoleLogging = False
                self.noLaunch = True

                if sys.platform == 'win32':
                    self.runAsDaemon = False

            # Write a pidfile if requested
            if o in ('--pidfile',):
                self.CREATEPID = True
                self.PIDFILE = str(a)

                # If the pidfile already exists, sickbeard may still be running, so exit
                if os.path.exists(self.PIDFILE):
                    sys.exit('PID file: %s already exists. Exiting.' % self.PIDFILE)

            # Specify folder to load the config file from
            if o in ('--config',):
                sickbeard.CONFIG_FILE = os.path.abspath(a)

            # Specify folder to use as the data dir
            if o in ('--datadir',):
                sickbeard.DATA_DIR = os.path.abspath(a)

            # Prevent resizing of the banner/posters even if PIL is installed
            if o in ('--noresize',):
                sickbeard.NO_RESIZE = True

        # The pidfile is only useful in daemon mode, make sure we can write the file properly
        if self.CREATEPID:
            if self.runAsDaemon:
                pid_dir = os.path.dirname(self.PIDFILE)
                if not os.access(pid_dir, os.F_OK):
                    sys.exit(u"PID dir: %s doesn't exist. Exiting." % pid_dir)
                if not os.access(pid_dir, os.W_OK):
                    sys.exit(u'PID dir: %s must be writable (write permissions). Exiting.' % pid_dir)

            else:
                if self.consoleLogging:
                    print(u'Not running in daemon mode. PID file creation disabled')

                self.CREATEPID = False

        # If they don't specify a config file then put it in the data dir
        if not sickbeard.CONFIG_FILE:
            sickbeard.CONFIG_FILE = os.path.join(sickbeard.DATA_DIR, 'config.ini')

        # Make sure that we can create the data dir
        if not os.access(sickbeard.DATA_DIR, os.F_OK):
            try:
                os.makedirs(sickbeard.DATA_DIR, 0o744)
            except os.error:
                sys.exit(u'Unable to create data directory: %s Exiting.' % sickbeard.DATA_DIR)

        # Make sure we can write to the data dir
        if not os.access(sickbeard.DATA_DIR, os.W_OK):
            sys.exit(u'Data directory: %s must be writable (write permissions). Exiting.' % sickbeard.DATA_DIR)

        # Make sure we can write to the config file
        if not os.access(sickbeard.CONFIG_FILE, os.W_OK):
            if os.path.isfile(sickbeard.CONFIG_FILE):
                sys.exit(u'Config file: %s must be writeable (write permissions). Exiting.' % sickbeard.CONFIG_FILE)
            elif not os.access(os.path.dirname(sickbeard.CONFIG_FILE), os.W_OK):
                sys.exit(u'Config file directory: %s must be writeable (write permissions). Exiting'
                         % os.path.dirname(sickbeard.CONFIG_FILE))
        os.chdir(sickbeard.DATA_DIR)

        if self.consoleLogging:
            print(u'Starting up SickGear from %s' % sickbeard.CONFIG_FILE)

        # Load the config and publish it to the sickbeard package
        if not os.path.isfile(sickbeard.CONFIG_FILE):
            print(u'Unable to find "%s", all settings will be default!' % sickbeard.CONFIG_FILE)

        sickbeard.CFG = ConfigObj(sickbeard.CONFIG_FILE)

        CUR_DB_VERSION = db.DBConnection().checkDBVersion()

        if CUR_DB_VERSION > 0:
            if CUR_DB_VERSION < MIN_DB_VERSION:
                print(u'Your database version (%s) is too old to migrate from with this version of SickGear' \
                      % CUR_DB_VERSION)
                sys.exit(u'Upgrade using a previous version of SG first, or start with no database file to begin fresh')
            if CUR_DB_VERSION > MAX_DB_VERSION:
                print(u'Your database version (%s) has been incremented past what this version of SickGear supports' \
                      % CUR_DB_VERSION)
                sys.exit(
                    u'If you have used other forks of SG, your database may be unusable due to their modifications')

        # Initialize the config and our threads
        sickbeard.initialize(consoleLogging=self.consoleLogging)

        if self.runAsDaemon:
            self.daemonize()

        # Get PID
        sickbeard.PID = os.getpid()

        if self.forcedPort:
            logger.log(u'Forcing web server to port %s' % self.forcedPort)
            self.startPort = self.forcedPort
        else:
            self.startPort = sickbeard.WEB_PORT

        if sickbeard.WEB_LOG:
            self.log_dir = sickbeard.LOG_DIR
        else:
            self.log_dir = None

        # sickbeard.WEB_HOST is available as a configuration value in various
        # places but is not configurable. It is supported here for historic reasons.
        if sickbeard.WEB_HOST and sickbeard.WEB_HOST != '0.0.0.0':
            self.webhost = sickbeard.WEB_HOST
        else:
            if sickbeard.WEB_IPV6:
                self.webhost = '::'
            else:
                self.webhost = '0.0.0.0'

        # web server options
        self.web_options = {
            'port': int(self.startPort),
            'host': self.webhost,
            'data_root': os.path.join(sickbeard.PROG_DIR, 'gui', sickbeard.GUI_NAME),
            'web_root': sickbeard.WEB_ROOT,
            'log_dir': self.log_dir,
            'username': sickbeard.WEB_USERNAME,
            'password': sickbeard.WEB_PASSWORD,
            'enable_https': sickbeard.ENABLE_HTTPS,
            'handle_reverse_proxy': sickbeard.HANDLE_REVERSE_PROXY,
            'https_cert': os.path.join(sickbeard.PROG_DIR, sickbeard.HTTPS_CERT),
            'https_key': os.path.join(sickbeard.PROG_DIR, sickbeard.HTTPS_KEY),
        }

        # start web server
        try:
            # used to check if existing SG instances have been started
            sickbeard.helpers.wait_for_free_port(self.web_options['host'], self.web_options['port'])

            self.webserver = WebServer(self.web_options)
            self.webserver.start()
        except Exception:
            logger.log(u'Unable to start web server, is something else running on port %d?' % self.startPort,
                       logger.ERROR)
            if sickbeard.LAUNCH_BROWSER and not self.runAsDaemon:
                logger.log(u'Launching browser and exiting', logger.ERROR)
                sickbeard.launchBrowser(self.startPort)
            os._exit(1)

        # Check if we need to perform a restore first
        restoreDir = os.path.join(sickbeard.DATA_DIR, 'restore')
        if os.path.exists(restoreDir):
            if self.restore(restoreDir, sickbeard.DATA_DIR):
                logger.log(u'Restore successful...')
            else:
                logger.log_error_and_exit(u'Restore FAILED!')

        # Build from the DB to start with
        self.loadShowsFromDB()

        # Fire up all our threads
        sickbeard.start()

        # Build internal name cache
        name_cache.buildNameCache()

        # refresh network timezones
        network_timezones.update_network_dict()

        # load all ids from xem
        startup_background_tasks = threading.Thread(name='FETCH-XEMDATA', target=sickbeard.scene_exceptions.get_xem_ids)
        startup_background_tasks.start()

        # sure, why not?
        if sickbeard.USE_FAILED_DOWNLOADS:
            failed_history.trimHistory()

        # Start an update if we're supposed to
        if self.forceUpdate or sickbeard.UPDATE_SHOWS_ON_START:
            sickbeard.showUpdateScheduler.action.run(force=True)  # @UndefinedVariable

        # Launch browser
        if sickbeard.LAUNCH_BROWSER and not (self.noLaunch or self.runAsDaemon):
            sickbeard.launchBrowser(self.startPort)

        # main loop
        while True:
            time.sleep(1)
Beispiel #15
0
    def start(self):
        # do some preliminary stuff
        sickbeard.MY_FULLNAME = os.path.normpath(os.path.abspath(__file__))
        sickbeard.MY_NAME = os.path.basename(sickbeard.MY_FULLNAME)
        sickbeard.PROG_DIR = os.path.dirname(sickbeard.MY_FULLNAME)
        sickbeard.DATA_DIR = sickbeard.PROG_DIR
        sickbeard.MY_ARGS = sys.argv[1:]
        sickbeard.SYS_ENCODING = None

        try:
            locale.setlocale(locale.LC_ALL, '')
        except (locale.Error, IOError):
            pass
        try:
            sickbeard.SYS_ENCODING = locale.getpreferredencoding()
        except (locale.Error, IOError):
            pass

        # For OSes that are poorly configured I'll just randomly force UTF-8
        if not sickbeard.SYS_ENCODING or sickbeard.SYS_ENCODING in ('ANSI_X3.4-1968', 'US-ASCII', 'ASCII'):
            sickbeard.SYS_ENCODING = 'UTF-8'

        if not hasattr(sys, 'setdefaultencoding'):
            moves.reload_module(sys)

        try:
            # pylint: disable=E1101
            # On non-unicode builds this raises an AttributeError, if encoding type is not valid it throws a LookupError
            sys.setdefaultencoding(sickbeard.SYS_ENCODING)
        except (StandardError, Exception):
            print('Sorry, you MUST add the SickGear folder to the PYTHONPATH environment variable')
            print('or find another way to force Python to use %s for string encoding.' % sickbeard.SYS_ENCODING)
            sys.exit(1)

        # Need console logging for SickBeard.py and SickBeard-console.exe
        self.console_logging = (not hasattr(sys, 'frozen')) or (sickbeard.MY_NAME.lower().find('-console') > 0)

        # Rename the main thread
        threading.currentThread().name = 'MAIN'

        try:
            opts, args = getopt.getopt(sys.argv[1:], 'hfqdsp::',
                                       ['help', 'forceupdate', 'quiet', 'nolaunch', 'daemon', 'systemd', 'pidfile=',
                                        'port=', 'datadir=', 'config=', 'noresize'])  # @UnusedVariable
        except getopt.GetoptError:
            sys.exit(self.help_message())

        for o, a in opts:
            # Prints help message
            if o in ('-h', '--help'):
                sys.exit(self.help_message())

            # For now we'll just silence the logging
            if o in ('-q', '--quiet'):
                self.console_logging = False

            # Should we update (from indexer) all shows in the DB right away?
            if o in ('-f', '--forceupdate'):
                self.force_update = True

            # Suppress launching web browser
            # Needed for OSes without default browser assigned
            # Prevent duplicate browser window when restarting in the app
            if o in ('--nolaunch',):
                self.no_launch = True

            # Override default/configured port
            if o in ('-p', '--port'):
                try:
                    self.forced_port = int(a)
                except ValueError:
                    sys.exit('Port: %s is not a number. Exiting.' % a)

            # Run as a double forked daemon
            if o in ('-d', '--daemon'):
                self.run_as_daemon = True
                # When running as daemon disable console_logging and don't start browser
                self.console_logging = False
                self.no_launch = True

                if 'win32' == sys.platform:
                    self.run_as_daemon = False

            # Run as a systemd service
            if o in ('-s', '--systemd') and 'win32' != sys.platform:
                self.run_as_systemd = True
                self.run_as_daemon = False
                self.console_logging = False
                self.no_launch = True

            # Write a pidfile if requested
            if o in ('--pidfile',):
                self.create_pid = True
                self.pid_file = str(a)

                # If the pidfile already exists, sickbeard may still be running, so exit
                if os.path.exists(self.pid_file):
                    sys.exit('PID file: %s already exists. Exiting.' % self.pid_file)

            # Specify folder to load the config file from
            if o in ('--config',):
                sickbeard.CONFIG_FILE = os.path.abspath(a)

            # Specify folder to use as the data dir
            if o in ('--datadir',):
                sickbeard.DATA_DIR = os.path.abspath(a)

            # Prevent resizing of the banner/posters even if PIL is installed
            if o in ('--noresize',):
                sickbeard.NO_RESIZE = True

        # The pidfile is only useful in daemon mode, make sure we can write the file properly
        if self.create_pid:
            if self.run_as_daemon:
                pid_dir = os.path.dirname(self.pid_file)
                if not os.access(pid_dir, os.F_OK):
                    sys.exit(u"PID dir: %s doesn't exist. Exiting." % pid_dir)
                if not os.access(pid_dir, os.W_OK):
                    sys.exit(u'PID dir: %s must be writable (write permissions). Exiting.' % pid_dir)

            else:
                if self.console_logging:
                    print(u'Not running in daemon mode. PID file creation disabled')

                self.create_pid = False

        # If they don't specify a config file then put it in the data dir
        if not sickbeard.CONFIG_FILE:
            sickbeard.CONFIG_FILE = os.path.join(sickbeard.DATA_DIR, 'config.ini')

        # Make sure that we can create the data dir
        if not os.access(sickbeard.DATA_DIR, os.F_OK):
            try:
                os.makedirs(sickbeard.DATA_DIR, 0o744)
            except os.error:
                sys.exit(u'Unable to create data directory: %s Exiting.' % sickbeard.DATA_DIR)

        # Make sure we can write to the data dir
        if not os.access(sickbeard.DATA_DIR, os.W_OK):
            sys.exit(u'Data directory: %s must be writable (write permissions). Exiting.' % sickbeard.DATA_DIR)

        # Make sure we can write to the config file
        if not os.access(sickbeard.CONFIG_FILE, os.W_OK):
            if os.path.isfile(sickbeard.CONFIG_FILE):
                sys.exit(u'Config file: %s must be writeable (write permissions). Exiting.' % sickbeard.CONFIG_FILE)
            elif not os.access(os.path.dirname(sickbeard.CONFIG_FILE), os.W_OK):
                sys.exit(u'Config file directory: %s must be writeable (write permissions). Exiting'
                         % os.path.dirname(sickbeard.CONFIG_FILE))
        os.chdir(sickbeard.DATA_DIR)

        if self.console_logging:
            print(u'Starting up SickGear from %s' % sickbeard.CONFIG_FILE)

        # Load the config and publish it to the sickbeard package
        if not os.path.isfile(sickbeard.CONFIG_FILE):
            print(u'Unable to find "%s", all settings will be default!' % sickbeard.CONFIG_FILE)

        sickbeard.CFG = ConfigObj(sickbeard.CONFIG_FILE)
        try:
            stack_size = int(sickbeard.CFG['General']['stack_size'])
        except (StandardError, Exception):
            stack_size = None

        if stack_size:
            try:
                threading.stack_size(stack_size)
            except (StandardError, Exception) as er:
                print('Stack Size %s not set: %s' % (stack_size, er.message))

        # check all db versions
        for d, min_v, max_v, base_v, mo in [
            ('failed.db', sickbeard.failed_db.MIN_DB_VERSION, sickbeard.failed_db.MAX_DB_VERSION,
             sickbeard.failed_db.TEST_BASE_VERSION, 'FailedDb'),
            ('cache.db', sickbeard.cache_db.MIN_DB_VERSION, sickbeard.cache_db.MAX_DB_VERSION,
             sickbeard.cache_db.TEST_BASE_VERSION, 'CacheDb'),
            ('sickbeard.db', sickbeard.mainDB.MIN_DB_VERSION, sickbeard.mainDB.MAX_DB_VERSION,
             sickbeard.mainDB.TEST_BASE_VERSION, 'MainDb')
        ]:
            cur_db_version = db.DBConnection(d).checkDBVersion()

            # handling of standalone TEST db versions
            if cur_db_version >= 100000 and cur_db_version != max_v:
                print('Your [%s] database version (%s) is a test db version and doesn\'t match SickGear required '
                      'version (%s), downgrading to production db' % (d, cur_db_version, max_v))
                self.execute_rollback(mo, max_v)
                cur_db_version = db.DBConnection(d).checkDBVersion()
                if cur_db_version >= 100000:
                    print(u'Rollback to production failed.')
                    sys.exit(u'If you have used other forks, your database may be unusable due to their changes')
                if 100000 <= max_v and None is not base_v:
                    max_v = base_v  # set max_v to the needed base production db for test_db
                print(u'Rollback to production of [%s] successful.' % d)

            # handling of production db versions
            if 0 < cur_db_version < 100000:
                if cur_db_version < min_v:
                    print(u'Your [%s] database version (%s) is too old to migrate from with this version of SickGear'
                          % (d, cur_db_version))
                    sys.exit(u'Upgrade using a previous version of SG first,'
                             + u' or start with no database file to begin fresh')
                if cur_db_version > max_v:
                    print(u'Your [%s] database version (%s) has been incremented past'
                          u' what this version of SickGear supports. Trying to rollback now. Please wait...' %
                          (d, cur_db_version))
                    self.execute_rollback(mo, max_v)
                    if db.DBConnection(d).checkDBVersion() > max_v:
                        print(u'Rollback failed.')
                        sys.exit(u'If you have used other forks, your database may be unusable due to their changes')
                    print(u'Rollback of [%s] successful.' % d)

        # free memory
        global rollback_loaded
        rollback_loaded = None

        # Initialize the config and our threads
        sickbeard.initialize(console_logging=self.console_logging)

        if self.run_as_daemon:
            self.daemonize()

        # Get PID
        sickbeard.PID = os.getpid()

        if self.forced_port:
            logger.log(u'Forcing web server to port %s' % self.forced_port)
            self.start_port = self.forced_port
        else:
            self.start_port = sickbeard.WEB_PORT

        if sickbeard.WEB_LOG:
            self.log_dir = sickbeard.LOG_DIR
        else:
            self.log_dir = None

        # sickbeard.WEB_HOST is available as a configuration value in various
        # places but is not configurable. It is supported here for historic reasons.
        if sickbeard.WEB_HOST and sickbeard.WEB_HOST != '0.0.0.0':
            self.webhost = sickbeard.WEB_HOST
        else:
            self.webhost = (('0.0.0.0', '::')[sickbeard.WEB_IPV6], '')[sickbeard.WEB_IPV64]

        # web server options
        self.web_options = dict(
            host=self.webhost,
            port=int(self.start_port),
            web_root=sickbeard.WEB_ROOT,
            data_root=os.path.join(sickbeard.PROG_DIR, 'gui', sickbeard.GUI_NAME),
            log_dir=self.log_dir,
            username=sickbeard.WEB_USERNAME,
            password=sickbeard.WEB_PASSWORD,
            handle_reverse_proxy=sickbeard.HANDLE_REVERSE_PROXY,
            enable_https=False,
            https_cert=None,
            https_key=None,
        )
        if sickbeard.ENABLE_HTTPS:
            self.web_options.update(dict(
                enable_https=sickbeard.ENABLE_HTTPS,
                https_cert=os.path.join(sickbeard.PROG_DIR, sickbeard.HTTPS_CERT),
                https_key=os.path.join(sickbeard.PROG_DIR, sickbeard.HTTPS_KEY)
            ))

        # start web server
        try:
            # used to check if existing SG instances have been started
            sickbeard.helpers.wait_for_free_port(
                sickbeard.WEB_IPV6 and '::1' or self.web_options['host'], self.web_options['port'])

            self.webserver = WebServer(self.web_options)
            self.webserver.start()
        except (StandardError, Exception):
            logger.log(u'Unable to start web server, is something else running on port %d?' % self.start_port,
                       logger.ERROR)
            if self.run_as_systemd:
                self.exit(0)
            if sickbeard.LAUNCH_BROWSER and not self.no_launch:
                logger.log(u'Launching browser and exiting', logger.ERROR)
                sickbeard.launch_browser(self.start_port)
            self.exit(1)

        # Check if we need to perform a restore first
        restore_dir = os.path.join(sickbeard.DATA_DIR, 'restore')
        if os.path.exists(restore_dir):
            if self.restore(restore_dir, sickbeard.DATA_DIR):
                logger.log(u'Restore successful...')
            else:
                logger.log_error_and_exit(u'Restore FAILED!')

        # Build from the DB to start with
        self.load_shows_from_db()

        # Fire up all our threads
        sickbeard.start()

        # Build internal name cache
        name_cache.buildNameCache()

        # refresh network timezones
        network_timezones.update_network_dict()

        # load all ids from xem
        startup_background_tasks = threading.Thread(name='FETCH-XEMDATA', target=sickbeard.scene_exceptions.get_xem_ids)
        startup_background_tasks.start()

        # check history snatched_proper update
        if not db.DBConnection().has_flag('history_snatch_proper'):
            # noinspection PyUnresolvedReferences
            history_snatched_proper_task = threading.Thread(name='UPGRADE-HISTORY-ACTION',
                                                            target=sickbeard.history.history_snatched_proper_fix)
            history_snatched_proper_task.start()

        if sickbeard.USE_FAILED_DOWNLOADS:
            failed_history.remove_old_history()

        # Start an update if we're supposed to
        if self.force_update or sickbeard.UPDATE_SHOWS_ON_START:
            sickbeard.showUpdateScheduler.action.run(force=True)  # @UndefinedVariable

        # Launch browser
        if sickbeard.LAUNCH_BROWSER and not self.no_launch:
            sickbeard.launch_browser(self.start_port)

        # main loop
        while True:
            time.sleep(1)
Beispiel #16
0
    def start(self):
        # map the following codecs to utf-8
        codecs.register(lambda name: codecs.lookup('utf-8')
                        if name == 'cp65001' else None)
        codecs.register(lambda name: codecs.lookup('utf-8')
                        if name == 'cp1252' else None)

        # get locale encoding
        try:
            locale.setlocale(locale.LC_ALL, "")
            sickbeard.SYS_ENCODING = locale.getpreferredencoding()
        except (locale.Error, IOError):
            sickbeard.SYS_ENCODING = None

        # enforce UTF-8
        if not sickbeard.SYS_ENCODING or codecs.lookup(
                sickbeard.SYS_ENCODING).name == 'ascii':
            sickbeard.SYS_ENCODING = 'UTF-8'

        # do some preliminary stuff
        sickbeard.MY_FULLNAME = ek(os.path.normpath,
                                   ek(os.path.abspath, __file__))
        sickbeard.MY_NAME = ek(os.path.basename, sickbeard.MY_FULLNAME)
        sickbeard.PROG_DIR = ek(os.path.dirname, sickbeard.MY_FULLNAME)
        sickbeard.DATA_DIR = sickbeard.PROG_DIR
        sickbeard.MY_ARGS = sys.argv[1:]

        # Need console logging for SickBeard.py and SickBeard-console.exe
        self.consoleLogging = (not hasattr(
            sys, "frozen")) or (sickbeard.MY_NAME.lower().find('-console') > 0)

        # Rename the main thread
        threading.currentThread().name = u"MAIN"

        # Do this before importing sickbeard, to prevent locked files and incorrect import
        oldtornado = ek(
            os.path.abspath,
            ek(os.path.join, ek(os.path.dirname, __file__), 'tornado'))
        if ek(os.path.isdir, oldtornado):
            ek(shutil.move, oldtornado, oldtornado + '_kill')
            ek(removetree, oldtornado + '_kill')

        try:
            opts, _ = getopt.getopt(sys.argv[1:], "hqdp::", [
                'help', 'quiet', 'nolaunch', 'daemon', 'pidfile=', 'port=',
                'datadir=', 'config=', 'noresize'
            ])
        except getopt.GetoptError:
            sys.exit(self.help_message())

        for o, a in opts:
            # Prints help message
            if o in ('-h', '--help'):
                sys.exit(self.help_message())

            # For now we'll just silence the logging
            if o in ('-q', '--quiet'):
                self.consoleLogging = False

            # Suppress launching web browser
            # Needed for OSes without default browser assigned
            # Prevent duplicate browser window when restarting in the app
            if o in ('--nolaunch', ):
                self.noLaunch = True

            # Override default/configured port
            if o in ('-p', '--port'):
                try:
                    self.forcedPort = int(a)
                except ValueError:
                    sys.exit("Port: " + str(a) + " is not a number. Exiting.")

            # Run as a double forked daemon
            if o in ('-d', '--daemon'):
                self.runAsDaemon = True
                # When running as daemon disable consoleLogging and don't start browser
                self.consoleLogging = False
                self.noLaunch = True

                if sys.platform == 'win32' or sys.platform == 'darwin':
                    self.runAsDaemon = False

            # Write a pidfile if requested
            if o in ('--pidfile', ):
                self.CREATEPID = True
                self.PIDFILE = str(a)

                # If the pidfile already exists, sickbeard may still be running, so exit
                if ek(os.path.exists, self.PIDFILE):
                    sys.exit("PID file: " + self.PIDFILE +
                             " already exists. Exiting.")

            # Specify folder to load the config file from
            if o in ('--config', ):
                sickbeard.CONFIG_FILE = ek(os.path.abspath, a)

            # Specify folder to use as the data dir
            if o in ('--datadir', ):
                sickbeard.DATA_DIR = ek(os.path.abspath, a)

            # Prevent resizing of the banner/posters even if PIL is installed
            if o in ('--noresize', ):
                sickbeard.NO_RESIZE = True

        # The pidfile is only useful in daemon mode, make sure we can write the file properly
        if self.CREATEPID:
            if self.runAsDaemon:
                pid_dir = ek(os.path.dirname, self.PIDFILE)
                if not ek(os.access, pid_dir, os.F_OK):
                    sys.exit("PID dir: " + pid_dir +
                             " doesn't exist. Exiting.")
                if not ek(os.access, pid_dir, os.W_OK):
                    sys.exit("PID dir: " + pid_dir +
                             " must be writable (write permissions). Exiting.")

            else:
                if self.consoleLogging:
                    sys.stdout.write(
                        u"Not running in daemon mode. PID file creation disabled.\n"
                    )

                self.CREATEPID = False

        # If they don't specify a config file then put it in the data dir
        if not sickbeard.CONFIG_FILE:
            sickbeard.CONFIG_FILE = ek(os.path.join, sickbeard.DATA_DIR,
                                       "config.ini")

        # Make sure that we can create the data dir
        if not ek(os.access, sickbeard.DATA_DIR, os.F_OK):
            try:
                ek(os.makedirs, sickbeard.DATA_DIR, 0o744)
            except os.error:
                raise SystemExit("Unable to create datadir '" +
                                 sickbeard.DATA_DIR + "'")

        # Make sure we can write to the data dir
        if not ek(os.access, sickbeard.DATA_DIR, os.W_OK):
            raise SystemExit("Datadir must be writeable '" +
                             sickbeard.DATA_DIR + "'")

        # Make sure we can write to the config file
        if not ek(os.access, sickbeard.CONFIG_FILE, os.W_OK):
            if ek(os.path.isfile, sickbeard.CONFIG_FILE):
                raise SystemExit("Config file '" + sickbeard.CONFIG_FILE +
                                 "' must be writeable.")
            elif not ek(os.access, ek(os.path.dirname, sickbeard.CONFIG_FILE),
                        os.W_OK):
                raise SystemExit("Config file root dir '" +
                                 ek(os.path.dirname, sickbeard.CONFIG_FILE) +
                                 "' must be writeable.")

        ek(os.chdir, sickbeard.DATA_DIR)

        # Check if we need to perform a restore first
        restoreDir = ek(os.path.join, sickbeard.DATA_DIR, 'restore')
        if ek(os.path.exists, restoreDir):
            success = self.restoreDB(restoreDir, sickbeard.DATA_DIR)
            if self.consoleLogging:
                sys.stdout.write(
                    u"Restore: restoring DB and config.ini %s!\n" %
                    ("FAILED", "SUCCESSFUL")[success])

        # Load the config and publish it to the sickbeard package
        if self.consoleLogging and not ek(os.path.isfile,
                                          sickbeard.CONFIG_FILE):
            sys.stdout.write(u"Unable to find '" + sickbeard.CONFIG_FILE +
                             "' , all settings will be default!" + "\n")

        sickbeard.CFG = ek(ConfigObj, sickbeard.CONFIG_FILE)

        # Initialize the config and our threads
        sickbeard.initialize(consoleLogging=self.consoleLogging)

        if self.runAsDaemon:
            self.daemonize()

        # Get PID
        sickbeard.PID = os.getpid()

        # Build from the DB to start with
        self.loadShowsFromDB()

        if self.forcedPort:
            logger.log(u"Forcing web server to port " + str(self.forcedPort))
            self.startPort = self.forcedPort
        else:
            self.startPort = sickbeard.WEB_PORT

        if sickbeard.WEB_LOG:
            self.log_dir = sickbeard.LOG_DIR
        else:
            self.log_dir = None

        # sickbeard.WEB_HOST is available as a configuration value in various
        # places but is not configurable. It is supported here for historic reasons.
        if sickbeard.WEB_HOST and sickbeard.WEB_HOST != '0.0.0.0':
            self.webhost = sickbeard.WEB_HOST
        else:
            if sickbeard.WEB_IPV6:
                self.webhost = '::'
            else:
                self.webhost = '0.0.0.0'

        # web server options
        self.web_options = {
            'port':
            int(self.startPort),
            'host':
            self.webhost,
            'data_root':
            ek(os.path.join, sickbeard.PROG_DIR, 'gui', sickbeard.GUI_NAME),
            'web_root':
            sickbeard.WEB_ROOT,
            'log_dir':
            self.log_dir,
            'username':
            sickbeard.WEB_USERNAME,
            'password':
            sickbeard.WEB_PASSWORD,
            'enable_https':
            sickbeard.ENABLE_HTTPS,
            'handle_reverse_proxy':
            sickbeard.HANDLE_REVERSE_PROXY,
            'https_cert':
            ek(os.path.join, sickbeard.PROG_DIR, sickbeard.HTTPS_CERT),
            'https_key':
            ek(os.path.join, sickbeard.PROG_DIR, sickbeard.HTTPS_KEY),
        }

        # start web server
        self.webserver = SRWebServer(self.web_options)
        self.webserver.start()

        if self.consoleLogging:
            print("Starting up SickRage " + sickbeard.BRANCH + " from " +
                  sickbeard.CONFIG_FILE)

        # Clean up after update
        if sickbeard.GIT_NEWVER:
            toclean = ek(os.path.join, sickbeard.CACHE_DIR, 'mako')
            for root, dirs, files in ek(os.walk, toclean, topdown=False):
                for name in files:
                    ek(os.remove, ek(os.path.join, root, name))
                for name in dirs:
                    ek(os.rmdir, ek(os.path.join, root, name))
            sickbeard.GIT_NEWVER = False

        # Fire up all our threads
        sickbeard.start()

        # Build internal name cache
        name_cache.buildNameCache()

        # Prepopulate network timezones, it isn't thread safe
        network_timezones.update_network_dict()

        # sure, why not?
        if sickbeard.USE_FAILED_DOWNLOADS:
            failed_history.trimHistory()

        # # Check for metadata indexer updates for shows (Disabled until we use api)
        # sickbeard.showUpdateScheduler.forceRun()

        # Launch browser
        if sickbeard.LAUNCH_BROWSER and not (self.noLaunch
                                             or self.runAsDaemon):
            sickbeard.launchBrowser(
                'https' if sickbeard.ENABLE_HTTPS else 'http', self.startPort,
                sickbeard.WEB_ROOT)

        # main loop
        while True:
            time.sleep(1)
Beispiel #17
0
    def start(self):  # pylint: disable=too-many-branches,too-many-statements
        """
        Start SickRage
        """
        # do some preliminary stuff
        sickbeard.MY_FULLNAME = ek(os.path.normpath,
                                   ek(os.path.abspath, __file__))
        sickbeard.MY_NAME = ek(os.path.basename, sickbeard.MY_FULLNAME)
        sickbeard.PROG_DIR = ek(os.path.dirname, sickbeard.MY_FULLNAME)
        sickbeard.LOCALE_DIR = ek(os.path.join, sickbeard.PROG_DIR, 'locale')
        sickbeard.DATA_DIR = sickbeard.PROG_DIR
        sickbeard.MY_ARGS = sys.argv[1:]

        try:
            locale.setlocale(locale.LC_ALL, '')
            sickbeard.SYS_ENCODING = locale.getpreferredencoding()
        except (locale.Error, IOError):
            sickbeard.SYS_ENCODING = 'UTF-8'

        # pylint: disable=no-member
        if not sickbeard.SYS_ENCODING or sickbeard.SYS_ENCODING.lower() in ('ansi_x3.4-1968', 'us-ascii', 'ascii', 'charmap') or \
                (sys.platform.startswith('win') and sys.getwindowsversion()[0] >= 6 and str(getattr(sys.stdout, 'device', sys.stdout).encoding).lower() in ('cp65001', 'charmap')):
            sickbeard.SYS_ENCODING = 'UTF-8'

        # TODO: Continue working on making this unnecessary, this hack creates all sorts of hellish problems
        if not hasattr(sys, 'setdefaultencoding'):
            reload_module(sys)

        try:
            # On non-unicode builds this will raise an AttributeError, if encoding type is not valid it throws a LookupError
            sys.setdefaultencoding(sickbeard.SYS_ENCODING)  # pylint: disable=no-member
        except (AttributeError, LookupError):
            sys.exit(
                'Sorry, you MUST add the SickRage folder to the PYTHONPATH environment variable\n'
                'or find another way to force Python to use {} for string encoding.'
                .format(sickbeard.SYS_ENCODING))

        # Rename the main thread
        threading.currentThread().name = 'MAIN'

        args = SickRageArgumentParser(sickbeard.PROG_DIR).parse_args()

        if args.force_update:
            result = self.force_update()
            sys.exit(int(not result))  # Ok -> 0 , Error -> 1

        # Need console logging for SickBeard.py and SickBeard-console.exe
        sickbeard.NO_RESIZE = args.noresize
        self.console_logging = (not hasattr(sys, 'frozen')) or (
            sickbeard.MY_NAME.lower().find('-console') > 0) and not args.quiet
        self.no_launch = args.nolaunch
        self.forced_port = args.port
        if args.daemon:
            self.run_as_daemon = platform.system() != 'Windows'
            self.console_logging = False
            self.no_launch = True

        self.create_pid = bool(args.pidfile)
        self.pid_file = args.pidfile
        if self.pid_file and ek(os.path.exists, self.pid_file):
            # If the pid file already exists, SickRage may still be running, so exit
            raise SystemExit('PID file: {0} already exists. Exiting.'.format(
                self.pid_file))

        sickbeard.DATA_DIR = ek(
            os.path.abspath,
            args.datadir) if args.datadir else sickbeard.DATA_DIR
        sickbeard.CONFIG_FILE = ek(
            os.path.abspath, args.config) if args.config else ek(
                os.path.join, sickbeard.DATA_DIR, 'config.ini')

        # The pid file is only useful in daemon mode, make sure we can write the file properly
        if self.create_pid:
            if self.run_as_daemon:
                pid_dir = ek(os.path.dirname, self.pid_file)
                if not ek(os.access, pid_dir, os.F_OK):
                    sys.exit('PID dir: {0} doesn\'t exist. Exiting.'.format(
                        pid_dir))
                if not ek(os.access, pid_dir, os.W_OK):
                    raise SystemExit(
                        'PID dir: {0} must be writable (write permissions). Exiting.'
                        .format(pid_dir))
            else:
                if self.console_logging:
                    sys.stdout.write(
                        'Not running in daemon mode. PID file creation disabled.\n'
                    )
                self.create_pid = False

        # Make sure that we can create the data dir
        if not ek(os.access, sickbeard.DATA_DIR, os.F_OK):
            try:
                ek(os.makedirs, sickbeard.DATA_DIR, 0o744)
            except os.error:
                raise SystemExit('Unable to create data directory: {0}'.format(
                    sickbeard.DATA_DIR))

        # Make sure we can write to the data dir
        if not ek(os.access, sickbeard.DATA_DIR, os.W_OK):
            raise SystemExit('Data directory must be writeable: {0}'.format(
                sickbeard.DATA_DIR))

        # Make sure we can write to the config file
        if not ek(os.access, sickbeard.CONFIG_FILE, os.W_OK):
            if ek(os.path.isfile, sickbeard.CONFIG_FILE):
                raise SystemExit('Config file must be writeable: {0}'.format(
                    sickbeard.CONFIG_FILE))
            elif not ek(os.access, ek(os.path.dirname, sickbeard.CONFIG_FILE),
                        os.W_OK):
                raise SystemExit(
                    'Config file root dir must be writeable: {0}'.format(
                        ek(os.path.dirname, sickbeard.CONFIG_FILE)))

        ek(os.chdir, sickbeard.DATA_DIR)

        # Check if we need to perform a restore first
        restore_dir = ek(os.path.join, sickbeard.DATA_DIR, 'restore')
        if ek(os.path.exists, restore_dir):
            success = self.restore_db(restore_dir, sickbeard.DATA_DIR)
            if self.console_logging:
                sys.stdout.write(
                    'Restore: restoring DB and config.ini {0}!\n'.format(
                        ('FAILED', 'SUCCESSFUL')[success]))

        # Load the config and publish it to the sickbeard package
        if self.console_logging and not ek(os.path.isfile,
                                           sickbeard.CONFIG_FILE):
            sys.stdout.write(
                'Unable to find {0}, all settings will be default!\n'.format(
                    sickbeard.CONFIG_FILE))

        sickbeard.CFG = ConfigObj(sickbeard.CONFIG_FILE, encoding='UTF-8')

        # Initialize the config and our threads
        sickbeard.initialize(consoleLogging=self.console_logging)

        if self.run_as_daemon:
            self.daemonize()

        # Get PID
        sickbeard.PID = os.getpid()

        # Build from the DB to start with
        self.load_shows_from_db()

        logger.log('Starting SickRage [{branch}] using \'{config}\''.format(
            branch=sickbeard.BRANCH, config=sickbeard.CONFIG_FILE))

        self.clear_cache()

        if self.forced_port:
            logger.log('Forcing web server to port {port}'.format(
                port=self.forced_port))
            self.start_port = self.forced_port
        else:
            self.start_port = sickbeard.WEB_PORT

        if sickbeard.WEB_LOG:
            self.log_dir = sickbeard.LOG_DIR
        else:
            self.log_dir = None

        # sickbeard.WEB_HOST is available as a configuration value in various
        # places but is not configurable. It is supported here for historic reasons.
        if sickbeard.WEB_HOST and sickbeard.WEB_HOST != '0.0.0.0':
            self.web_host = sickbeard.WEB_HOST
        else:
            self.web_host = '' if sickbeard.WEB_IPV6 else '0.0.0.0'

        # web server options
        self.web_options = {
            'port':
            int(self.start_port),
            'host':
            self.web_host,
            'data_root':
            ek(os.path.join, sickbeard.PROG_DIR, 'gui', sickbeard.GUI_NAME),
            'web_root':
            sickbeard.WEB_ROOT,
            'log_dir':
            self.log_dir,
            'username':
            sickbeard.WEB_USERNAME,
            'password':
            sickbeard.WEB_PASSWORD,
            'enable_https':
            sickbeard.ENABLE_HTTPS,
            'handle_reverse_proxy':
            sickbeard.HANDLE_REVERSE_PROXY,
            'https_cert':
            ek(os.path.join, sickbeard.PROG_DIR, sickbeard.HTTPS_CERT),
            'https_key':
            ek(os.path.join, sickbeard.PROG_DIR, sickbeard.HTTPS_KEY),
        }

        # start web server
        self.web_server = SRWebServer(self.web_options)
        self.web_server.start()

        # Fire up all our threads
        sickbeard.start()

        # Build internal name cache
        name_cache.buildNameCache()

        # Pre-populate network timezones, it isn't thread safe
        network_timezones.update_network_dict()

        # sure, why not?
        if sickbeard.USE_FAILED_DOWNLOADS:
            failed_history.trimHistory()

        # Check for metadata indexer updates for shows (sets the next aired ep!)
        # sickbeard.showUpdateScheduler.forceRun()

        # Launch browser
        if sickbeard.LAUNCH_BROWSER and not (self.no_launch
                                             or self.run_as_daemon):
            sickbeard.launchBrowser(
                'https' if sickbeard.ENABLE_HTTPS else 'http', self.start_port,
                sickbeard.WEB_ROOT)

        # main loop
        while True:
            time.sleep(1)
Beispiel #18
0
    def start(self):
        # do some preliminary stuff
        sickbeard.MY_FULLNAME = os.path.normpath(os.path.abspath(__file__))
        sickbeard.MY_NAME = os.path.basename(sickbeard.MY_FULLNAME)
        sickbeard.PROG_DIR = os.path.dirname(sickbeard.MY_FULLNAME)
        sickbeard.DATA_DIR = sickbeard.PROG_DIR
        sickbeard.MY_ARGS = sys.argv[1:]
        sickbeard.SYS_ENCODING = None

        try:
            locale.setlocale(locale.LC_ALL, '')
        except (locale.Error, IOError):
            pass
        try:
            sickbeard.SYS_ENCODING = locale.getpreferredencoding()
        except (locale.Error, IOError):
            pass

        # For OSes that are poorly configured I'll just randomly force UTF-8
        if not sickbeard.SYS_ENCODING or sickbeard.SYS_ENCODING in (
                'ANSI_X3.4-1968', 'US-ASCII', 'ASCII'):
            sickbeard.SYS_ENCODING = 'UTF-8'

        if not hasattr(sys, 'setdefaultencoding'):
            moves.reload_module(sys)

        try:
            # pylint: disable=E1101
            # On non-unicode builds this raises an AttributeError, if encoding type is not valid it throws a LookupError
            sys.setdefaultencoding(sickbeard.SYS_ENCODING)
        except (StandardError, Exception):
            print(
                'Sorry, you MUST add the SickGear folder to the PYTHONPATH environment variable'
            )
            print(
                'or find another way to force Python to use %s for string encoding.'
                % sickbeard.SYS_ENCODING)
            sys.exit(1)

        # Need console logging for sickgear.py and SickBeard-console.exe
        self.console_logging = (not hasattr(
            sys, 'frozen')) or (sickbeard.MY_NAME.lower().find('-console') > 0)

        # Rename the main thread
        threading.currentThread().name = 'MAIN'

        try:
            opts, args = getopt.getopt(sys.argv[1:], 'hfqdsp::', [
                'help', 'forceupdate', 'quiet', 'nolaunch', 'daemon',
                'systemd', 'pidfile=', 'port=', 'datadir=', 'config=',
                'noresize'
            ])  # @UnusedVariable
        except getopt.GetoptError:
            sys.exit(self.help_message())

        for o, a in opts:
            # Prints help message
            if o in ('-h', '--help'):
                sys.exit(self.help_message())

            # For now we'll just silence the logging
            if o in ('-q', '--quiet'):
                self.console_logging = False

            # Should we update (from indexer) all shows in the DB right away?
            if o in ('-f', '--forceupdate'):
                self.force_update = True

            # Suppress launching web browser
            # Needed for OSes without default browser assigned
            # Prevent duplicate browser window when restarting in the app
            if o in ('--nolaunch', ):
                self.no_launch = True

            # Override default/configured port
            if o in ('-p', '--port'):
                try:
                    self.forced_port = int(a)
                except ValueError:
                    sys.exit('Port: %s is not a number. Exiting.' % a)

            # Run as a double forked daemon
            if o in ('-d', '--daemon'):
                self.run_as_daemon = True
                # When running as daemon disable console_logging and don't start browser
                self.console_logging = False
                self.no_launch = True

                if 'win32' == sys.platform:
                    self.run_as_daemon = False

            # Run as a systemd service
            if o in ('-s', '--systemd') and 'win32' != sys.platform:
                self.run_as_systemd = True
                self.run_as_daemon = False
                self.console_logging = False
                self.no_launch = True

            # Write a pidfile if requested
            if o in ('--pidfile', ):
                self.create_pid = True
                self.pid_file = str(a)

                # If the pidfile already exists, sickbeard may still be running, so exit
                if os.path.exists(self.pid_file):
                    sys.exit('PID file: %s already exists. Exiting.' %
                             self.pid_file)

            # Specify folder to load the config file from
            if o in ('--config', ):
                sickbeard.CONFIG_FILE = os.path.abspath(a)

            # Specify folder to use as the data dir
            if o in ('--datadir', ):
                sickbeard.DATA_DIR = os.path.abspath(a)

            # Prevent resizing of the banner/posters even if PIL is installed
            if o in ('--noresize', ):
                sickbeard.NO_RESIZE = True

        # The pidfile is only useful in daemon mode, make sure we can write the file properly
        if self.create_pid:
            if self.run_as_daemon:
                pid_dir = os.path.dirname(self.pid_file)
                if not os.access(pid_dir, os.F_OK):
                    sys.exit(u"PID dir: %s doesn't exist. Exiting." % pid_dir)
                if not os.access(pid_dir, os.W_OK):
                    sys.exit(
                        u'PID dir: %s must be writable (write permissions). Exiting.'
                        % pid_dir)

            else:
                if self.console_logging:
                    print(
                        u'Not running in daemon mode. PID file creation disabled'
                    )

                self.create_pid = False

        # If they don't specify a config file then put it in the data dir
        if not sickbeard.CONFIG_FILE:
            sickbeard.CONFIG_FILE = os.path.join(sickbeard.DATA_DIR,
                                                 'config.ini')

        # Make sure that we can create the data dir
        if not os.access(sickbeard.DATA_DIR, os.F_OK):
            try:
                os.makedirs(sickbeard.DATA_DIR, 0o744)
            except os.error:
                sys.exit(u'Unable to create data directory: %s Exiting.' %
                         sickbeard.DATA_DIR)

        # Make sure we can write to the data dir
        if not os.access(sickbeard.DATA_DIR, os.W_OK):
            sys.exit(
                u'Data directory: %s must be writable (write permissions). Exiting.'
                % sickbeard.DATA_DIR)

        # Make sure we can write to the config file
        if not os.access(sickbeard.CONFIG_FILE, os.W_OK):
            if os.path.isfile(sickbeard.CONFIG_FILE):
                sys.exit(
                    u'Config file: %s must be writeable (write permissions). Exiting.'
                    % sickbeard.CONFIG_FILE)
            elif not os.access(os.path.dirname(sickbeard.CONFIG_FILE),
                               os.W_OK):
                sys.exit(
                    u'Config file directory: %s must be writeable (write permissions). Exiting'
                    % os.path.dirname(sickbeard.CONFIG_FILE))
        os.chdir(sickbeard.DATA_DIR)

        if self.console_logging:
            print(u'Starting up SickGear from %s' % sickbeard.CONFIG_FILE)

        # Load the config and publish it to the sickbeard package
        if not os.path.isfile(sickbeard.CONFIG_FILE):
            print(u'Unable to find "%s", all settings will be default!' %
                  sickbeard.CONFIG_FILE)

        sickbeard.CFG = ConfigObj(sickbeard.CONFIG_FILE)
        try:
            stack_size = int(sickbeard.CFG['General']['stack_size'])
        except (StandardError, Exception):
            stack_size = None

        if stack_size:
            try:
                threading.stack_size(stack_size)
            except (StandardError, Exception) as er:
                print('Stack Size %s not set: %s' % (stack_size, er.message))

        if self.run_as_daemon:
            self.daemonize()

        # Get PID
        sickbeard.PID = os.getpid()

        # Initialize the config
        sickbeard.initialize(console_logging=self.console_logging)

        if self.forced_port:
            logger.log(u'Forcing web server to port %s' % self.forced_port)
            self.start_port = self.forced_port
        else:
            self.start_port = sickbeard.WEB_PORT

        if sickbeard.WEB_LOG:
            self.log_dir = sickbeard.LOG_DIR
        else:
            self.log_dir = None

        # sickbeard.WEB_HOST is available as a configuration value in various
        # places but is not configurable. It is supported here for historic reasons.
        if sickbeard.WEB_HOST and sickbeard.WEB_HOST != '0.0.0.0':
            self.webhost = sickbeard.WEB_HOST
        else:
            self.webhost = (('0.0.0.0', '::')[sickbeard.WEB_IPV6],
                            '')[sickbeard.WEB_IPV64]

        # web server options
        self.web_options = dict(
            host=self.webhost,
            port=int(self.start_port),
            web_root=sickbeard.WEB_ROOT,
            data_root=os.path.join(sickbeard.PROG_DIR, 'gui',
                                   sickbeard.GUI_NAME),
            log_dir=self.log_dir,
            username=sickbeard.WEB_USERNAME,
            password=sickbeard.WEB_PASSWORD,
            handle_reverse_proxy=sickbeard.HANDLE_REVERSE_PROXY,
            enable_https=False,
            https_cert=None,
            https_key=None,
        )
        if sickbeard.ENABLE_HTTPS:
            self.web_options.update(
                dict(enable_https=sickbeard.ENABLE_HTTPS,
                     https_cert=os.path.join(sickbeard.PROG_DIR,
                                             sickbeard.HTTPS_CERT),
                     https_key=os.path.join(sickbeard.PROG_DIR,
                                            sickbeard.HTTPS_KEY)))

        # start web server
        try:
            # used to check if existing SG instances have been started
            sickbeard.helpers.wait_for_free_port(
                sickbeard.WEB_IPV6 and '::1' or self.web_options['host'],
                self.web_options['port'])

            self.webserver = WebServer(self.web_options)
            self.webserver.start()
            sickbeard.started = True
        except (StandardError, Exception):
            logger.log(
                u'Unable to start web server, is something else running on port %d?'
                % self.start_port, logger.ERROR)
            if self.run_as_systemd:
                self.exit(0)
            if sickbeard.LAUNCH_BROWSER and not self.no_launch:
                logger.log(u'Launching browser and exiting', logger.ERROR)
                sickbeard.launch_browser(self.start_port)
            self.exit(1)

        # Launch browser
        if sickbeard.LAUNCH_BROWSER and not self.no_launch:
            sickbeard.launch_browser(self.start_port)

        # check all db versions
        for d, min_v, max_v, base_v, mo in [
            ('failed.db', sickbeard.failed_db.MIN_DB_VERSION,
             sickbeard.failed_db.MAX_DB_VERSION,
             sickbeard.failed_db.TEST_BASE_VERSION, 'FailedDb'),
            ('cache.db', sickbeard.cache_db.MIN_DB_VERSION,
             sickbeard.cache_db.MAX_DB_VERSION,
             sickbeard.cache_db.TEST_BASE_VERSION, 'CacheDb'),
            ('sickbeard.db', sickbeard.mainDB.MIN_DB_VERSION,
             sickbeard.mainDB.MAX_DB_VERSION,
             sickbeard.mainDB.TEST_BASE_VERSION, 'MainDb')
        ]:
            cur_db_version = db.DBConnection(d).checkDBVersion()

            # handling of standalone TEST db versions
            load_msg = 'Downgrading %s to production version' % d
            if cur_db_version >= 100000 and cur_db_version != max_v:
                sickbeard.classes.loading_msg.set_msg_progress(
                    load_msg, 'Rollback')
                print(
                    'Your [%s] database version (%s) is a test db version and doesn\'t match SickGear required '
                    'version (%s), downgrading to production db' %
                    (d, cur_db_version, max_v))
                self.execute_rollback(mo, max_v, load_msg)
                cur_db_version = db.DBConnection(d).checkDBVersion()
                if cur_db_version >= 100000:
                    print(u'Rollback to production failed.')
                    sys.exit(
                        u'If you have used other forks, your database may be unusable due to their changes'
                    )
                if 100000 <= max_v and None is not base_v:
                    max_v = base_v  # set max_v to the needed base production db for test_db
                print(u'Rollback to production of [%s] successful.' % d)
                sickbeard.classes.loading_msg.set_msg_progress(
                    load_msg, 'Finished')

            # handling of production db versions
            if 0 < cur_db_version < 100000:
                if cur_db_version < min_v:
                    print(
                        u'Your [%s] database version (%s) is too old to migrate from with this version of SickGear'
                        % (d, cur_db_version))
                    sys.exit(u'Upgrade using a previous version of SG first,' +
                             u' or start with no database file to begin fresh')
                if cur_db_version > max_v:
                    sickbeard.classes.loading_msg.set_msg_progress(
                        load_msg, 'Rollback')
                    print(
                        u'Your [%s] database version (%s) has been incremented past'
                        u' what this version of SickGear supports. Trying to rollback now. Please wait...'
                        % (d, cur_db_version))
                    self.execute_rollback(mo, max_v, load_msg)
                    if db.DBConnection(d).checkDBVersion() > max_v:
                        print(u'Rollback failed.')
                        sys.exit(
                            u'If you have used other forks, your database may be unusable due to their changes'
                        )
                    print(u'Rollback of [%s] successful.' % d)
                    sickbeard.classes.loading_msg.set_msg_progress(
                        load_msg, 'Finished')

        # free memory
        global rollback_loaded
        rollback_loaded = None
        sickbeard.classes.loading_msg.message = 'Init SickGear'

        # Initialize the threads and other stuff
        sickbeard.initialize(console_logging=self.console_logging)

        # Check if we need to perform a restore first
        restore_dir = os.path.join(sickbeard.DATA_DIR, 'restore')
        if os.path.exists(restore_dir):
            sickbeard.classes.loading_msg.message = 'Restoring files'
            if self.restore(restore_dir, sickbeard.DATA_DIR):
                logger.log(u'Restore successful...')
            else:
                logger.log_error_and_exit(u'Restore FAILED!')

        # Build from the DB to start with
        sickbeard.classes.loading_msg.message = 'Loading shows from db'
        self.load_shows_from_db()

        # Fire up all our threads
        sickbeard.classes.loading_msg.message = 'Starting threads'
        sickbeard.start()

        # Build internal name cache
        sickbeard.classes.loading_msg.message = 'Build name cache'
        name_cache.buildNameCache()

        # refresh network timezones
        sickbeard.classes.loading_msg.message = 'Checking network timezones'
        network_timezones.update_network_dict()

        # load all ids from xem
        sickbeard.classes.loading_msg.message = 'Loading xem data'
        startup_background_tasks = threading.Thread(
            name='FETCH-XEMDATA',
            target=sickbeard.scene_exceptions.get_xem_ids)
        startup_background_tasks.start()

        sickbeard.classes.loading_msg.message = 'Checking history'
        # check history snatched_proper update
        if not db.DBConnection().has_flag('history_snatch_proper'):
            # noinspection PyUnresolvedReferences
            history_snatched_proper_task = threading.Thread(
                name='UPGRADE-HISTORY-ACTION',
                target=sickbeard.history.history_snatched_proper_fix)
            history_snatched_proper_task.start()

        if sickbeard.USE_FAILED_DOWNLOADS:
            failed_history.remove_old_history()

        # Start an update if we're supposed to
        if self.force_update or sickbeard.UPDATE_SHOWS_ON_START:
            sickbeard.classes.loading_msg.message = 'Starting a forced show update'
            sickbeard.showUpdateScheduler.action.run(
                force=True)  # @UndefinedVariable

        sickbeard.classes.loading_msg.message = 'Switching to default web server'
        time.sleep(2)
        self.webserver.switch_handlers()

        # # Launch browser
        # if sickbeard.LAUNCH_BROWSER and not self.no_launch:
        #     sickbeard.launch_browser(self.start_port)

        # main loop
        while True:
            time.sleep(1)
Beispiel #19
0
    def run(self):

        ShowQueueItem.run(self)

        logging.info("Starting to add show {}".format(self.showDir))
        # make sure the Indexer IDs are valid
        try:

            lINDEXER_API_PARMS = sickbeard.indexerApi(self.indexer).api_params.copy()
            if self.lang:
                lINDEXER_API_PARMS[b"language"] = self.lang

            logging.info("" + str(sickbeard.indexerApi(self.indexer).name) + ": " + repr(lINDEXER_API_PARMS))

            t = sickbeard.indexerApi(self.indexer).indexer(**lINDEXER_API_PARMS)
            s = t[self.indexer_id]

            # this usually only happens if they have an NFO in their show dir which gave us a Indexer ID that has no proper english version of the show
            if getattr(s, "seriesname", None) is None:
                logging.error(
                    "Show in "
                    + self.showDir
                    + " has no name on "
                    + str(sickbeard.indexerApi(self.indexer).name)
                    + ", probably the wrong language used to search with."
                )
                ui.notifications.error(
                    "Unable to add show",
                    "Show in "
                    + self.showDir
                    + " has no name on "
                    + str(sickbeard.indexerApi(self.indexer).name)
                    + ", probably the wrong language. Delete .nfo and add manually in the correct language.",
                )
                self._finishEarly()
                return
            # if the show has no episodes/seasons
            if not s:
                logging.error(
                    "Show "
                    + str(s[b"seriesname"])
                    + " is on "
                    + str(sickbeard.indexerApi(self.indexer).name)
                    + " but contains no season/episode data."
                )
                ui.notifications.error(
                    "Unable to add show",
                    "Show "
                    + str(s[b"seriesname"])
                    + " is on "
                    + str(sickbeard.indexerApi(self.indexer).name)
                    + " but contains no season/episode data.",
                )
                self._finishEarly()
                return
        except Exception as e:
            logging.error(
                "%s Error while loading information from indexer %s. Error: %r"
                % (self.indexer_id, sickbeard.indexerApi(self.indexer).name, ex(e))
            )

            ui.notifications.error(
                "Unable to add show",
                "Unable to look up the show in %s on %s using ID %s, not using the NFO. Delete .nfo and try adding manually again."
                % (self.showDir, sickbeard.indexerApi(self.indexer).name, self.indexer_id),
            )

            if sickbeard.USE_TRAKT:

                trakt_id = sickbeard.indexerApi(self.indexer).config[b"trakt_id"]
                trakt_api = TraktAPI(sickbeard.SSL_VERIFY, sickbeard.TRAKT_TIMEOUT)

                title = self.showDir.split("/")[-1]
                data = {"shows": [{"title": title, "ids": {}}]}
                if trakt_id == "tvdb_id":
                    data[b"shows"][0][b"ids"][b"tvdb"] = self.indexer_id
                else:
                    data[b"shows"][0][b"ids"][b"tvrage"] = self.indexer_id

                trakt_api.traktRequest("sync/watchlist/remove", data, method="POST")

            self._finishEarly()
            return

        try:
            newShow = TVShow(self.indexer, self.indexer_id, self.lang)
            newShow.loadFromIndexer()

            self.show = newShow

            # set up initial values
            self.show.location = self.showDir
            self.show.subtitles = self.subtitles if self.subtitles != None else sickbeard.SUBTITLES_DEFAULT
            self.show.quality = self.quality if self.quality else sickbeard.QUALITY_DEFAULT
            self.show.flatten_folders = (
                self.flatten_folders if self.flatten_folders != None else sickbeard.FLATTEN_FOLDERS_DEFAULT
            )
            self.show.anime = self.anime if self.anime != None else sickbeard.ANIME_DEFAULT
            self.show.scene = self.scene if self.scene != None else sickbeard.SCENE_DEFAULT
            self.show.archive_firstmatch = self.archive if self.archive != None else sickbeard.ARCHIVE_DEFAULT
            self.show.paused = self.paused if self.paused != None else False

            # set up default new/missing episode status
            logging.info("Setting all episodes to the specified default status: " + str(self.show.default_ep_status))
            self.show.default_ep_status = self.default_status

            if self.show.anime:
                self.show.release_groups = BlackAndWhiteList(self.show.indexerid)
                if self.blacklist:
                    self.show.release_groups.set_black_keywords(self.blacklist)
                if self.whitelist:
                    self.show.release_groups.set_white_keywords(self.whitelist)

                    # # be smartish about this
                    # if self.show.genre and "talk show" in self.show.genre.lower():
                    #     self.show.air_by_date = 1
                    # if self.show.genre and "documentary" in self.show.genre.lower():
                    #     self.show.air_by_date = 0
                    # if self.show.classification and "sports" in self.show.classification.lower():
                    #     self.show.sports = 1

        except sickbeard.indexer_exception as e:
            logging.error(
                "Unable to add show due to an error with "
                + sickbeard.indexerApi(self.indexer).name
                + ": {}".format(ex(e))
            )
            if self.show:
                ui.notifications.error(
                    "Unable to add "
                    + str(self.show.name)
                    + " due to an error with "
                    + sickbeard.indexerApi(self.indexer).name
                    + ""
                )
            else:
                ui.notifications.error(
                    "Unable to add show due to an error with " + sickbeard.indexerApi(self.indexer).name + ""
                )
            self._finishEarly()
            return

        except MultipleShowObjectsException:
            logging.warning("The show in " + self.showDir + " is already in your show list, skipping")
            ui.notifications.error("Show skipped", "The show in " + self.showDir + " is already in your show list")
            self._finishEarly()
            return

        except Exception as e:
            logging.error("Error trying to add show: {}".format(ex(e)))
            logging.debug(traceback.format_exc())
            self._finishEarly()
            raise

        logging.debug("Retrieving show info from IMDb")
        try:
            self.show.loadIMDbInfo()
        except imdb_exceptions.IMDbError as e:
            logging.warning(" Something wrong on IMDb api: {}".format(ex(e)))
        except Exception as e:
            logging.error("Error loading IMDb info: {}".format(ex(e)))

        try:
            self.show.saveToDB()
        except Exception as e:
            logging.error("Error saving the show to the database: {}".format(ex(e)))
            logging.debug(traceback.format_exc())
            self._finishEarly()
            raise

        # add it to the show list
        sickbeard.showList.append(self.show)

        try:
            self.show.loadEpisodesFromIndexer()
        except Exception as e:
            logging.error(
                "Error with "
                + sickbeard.indexerApi(self.show.indexer).name
                + ", not creating episode list: {}".format(ex(e))
            )
            logging.debug(traceback.format_exc())

        # update internal name cache
        name_cache.buildNameCache()

        try:
            self.show.loadEpisodesFromDir()
        except Exception as e:
            logging.error("Error searching dir for episodes: {}".format(ex(e)))
            logging.debug(traceback.format_exc())

        # if they set default ep status to WANTED then run the backlog to search for episodes
        # FIXME: This needs to be a backlog queue item!!!
        if self.show.default_ep_status == WANTED:
            logging.info("Launching backlog for this show since its episodes are WANTED")
            sickbeard.backlogSearchScheduler.action.searchBacklog([self.show])

        self.show.writeMetadata()
        self.show.updateMetadata()
        self.show.populateCache()

        self.show.flushEpisodes()

        if sickbeard.USE_TRAKT:
            # if there are specific episodes that need to be added by trakt
            sickbeard.traktCheckerScheduler.action.manageNewShow(self.show)

            # add show to trakt.tv library
            if sickbeard.TRAKT_SYNC:
                sickbeard.traktCheckerScheduler.action.addShowToTraktLibrary(self.show)

            if sickbeard.TRAKT_SYNC_WATCHLIST:
                logging.info("update watchlist")
                notifiers.trakt_notifier.update_watchlist(show_obj=self.show)

        # Load XEM data to DB for show
        scene_numbering.xem_refresh(self.show.indexerid, self.show.indexer, force=True)

        # check if show has XEM mapping so we can determin if searches should go by scene numbering or indexer numbering.
        if not self.scene and scene_numbering.get_xem_numbering_for_show(self.show.indexerid, self.show.indexer):
            self.show.scene = 1

        # After initial add, set to default_status_after.
        self.show.default_ep_status = self.default_status_after

        self.finish()
    def setUp(self):
        super(SceneExceptionTestCase, self).setUp()

        sickbeard.showList = [Show(1, 79604), Show(1, 251085)]
        scene_exceptions.retrieve_exceptions()
        name_cache.buildNameCache()
Beispiel #21
0
    def start(self):
        # do some preliminary stuff
        sickbeard.MY_FULLNAME = os.path.normpath(os.path.abspath(__file__))
        sickbeard.MY_NAME = os.path.basename(sickbeard.MY_FULLNAME)
        sickbeard.PROG_DIR = os.path.dirname(sickbeard.MY_FULLNAME)
        sickbeard.DATA_DIR = sickbeard.PROG_DIR
        sickbeard.MY_ARGS = sys.argv[1:]
        sickbeard.SYS_ENCODING = None

        try:
            locale.setlocale(locale.LC_ALL, '')
        except (locale.Error, IOError):
            pass
        try:
            sickbeard.SYS_ENCODING = locale.getpreferredencoding()
        except (locale.Error, IOError):
            pass

        # For OSes that are poorly configured I'll just randomly force UTF-8
        if not sickbeard.SYS_ENCODING or sickbeard.SYS_ENCODING in ('ANSI_X3.4-1968', 'US-ASCII', 'ASCII'):
            sickbeard.SYS_ENCODING = 'UTF-8'

        if not hasattr(sys, 'setdefaultencoding'):
            reload(sys)

        try:
            # pylint: disable=E1101
            # On non-unicode builds this will raise an AttributeError, if encoding type is not valid it throws a LookupError
            sys.setdefaultencoding(sickbeard.SYS_ENCODING)
        except:
            print 'Sorry, you MUST add the SickGear folder to the PYTHONPATH environment variable'
            print 'or find another way to force Python to use %s for string encoding.' % sickbeard.SYS_ENCODING
            sys.exit(1)

        # Need console logging for SickBeard.py and SickBeard-console.exe
        self.consoleLogging = (not hasattr(sys, 'frozen')) or (sickbeard.MY_NAME.lower().find('-console') > 0)

        # Rename the main thread
        threading.currentThread().name = 'MAIN'

        try:
            opts, args = getopt.getopt(sys.argv[1:], 'hfqdp::',
                                       ['help', 'forceupdate', 'quiet', 'nolaunch', 'daemon', 'pidfile=', 'port=',
                                        'datadir=', 'config=', 'noresize'])  # @UnusedVariable
        except getopt.GetoptError:
            sys.exit(self.help_message())

        for o, a in opts:
            # Prints help message
            if o in ('-h', '--help'):
                sys.exit(self.help_message())

            # For now we'll just silence the logging
            if o in ('-q', '--quiet'):
                self.consoleLogging = False

            # Should we update (from indexer) all shows in the DB right away?
            if o in ('-f', '--forceupdate'):
                self.forceUpdate = True

            # Suppress launching web browser
            # Needed for OSes without default browser assigned
            # Prevent duplicate browser window when restarting in the app
            if o in ('--nolaunch',):
                self.noLaunch = True

            # Override default/configured port
            if o in ('-p', '--port'):
                try:
                    self.forcedPort = int(a)
                except ValueError:
                    sys.exit('Port: %s is not a number. Exiting.' % a)

            # Run as a double forked daemon
            if o in ('-d', '--daemon'):
                self.runAsDaemon = True
                # When running as daemon disable consoleLogging and don't start browser
                self.consoleLogging = False
                self.noLaunch = True

                if sys.platform == 'win32':
                    self.runAsDaemon = False

            # Write a pidfile if requested
            if o in ('--pidfile',):
                self.CREATEPID = True
                self.PIDFILE = str(a)

                # If the pidfile already exists, sickbeard may still be running, so exit
                if os.path.exists(self.PIDFILE):
                    sys.exit('PID file: %s already exists. Exiting.' % self.PIDFILE)

            # Specify folder to load the config file from
            if o in ('--config',):
                sickbeard.CONFIG_FILE = os.path.abspath(a)

            # Specify folder to use as the data dir
            if o in ('--datadir',):
                sickbeard.DATA_DIR = os.path.abspath(a)

            # Prevent resizing of the banner/posters even if PIL is installed
            if o in ('--noresize',):
                sickbeard.NO_RESIZE = True

        # The pidfile is only useful in daemon mode, make sure we can write the file properly
        if self.CREATEPID:
            if self.runAsDaemon:
                pid_dir = os.path.dirname(self.PIDFILE)
                if not os.access(pid_dir, os.F_OK):
                    sys.exit(u"PID dir: %s doesn't exist. Exiting." % pid_dir)
                if not os.access(pid_dir, os.W_OK):
                    sys.exit(u'PID dir: %s must be writable (write permissions). Exiting.' % pid_dir)

            else:
                if self.consoleLogging:
                    print u'Not running in daemon mode. PID file creation disabled'

                self.CREATEPID = False

        # If they don't specify a config file then put it in the data dir
        if not sickbeard.CONFIG_FILE:
            sickbeard.CONFIG_FILE = os.path.join(sickbeard.DATA_DIR, 'config.ini')

        # Make sure that we can create the data dir
        if not os.access(sickbeard.DATA_DIR, os.F_OK):
            try:
                os.makedirs(sickbeard.DATA_DIR, 0744)
            except os.error:
                sys.exit(u'Unable to create data directory: %s Exiting.' % sickbeard.DATA_DIR)

        # Make sure we can write to the data dir
        if not os.access(sickbeard.DATA_DIR, os.W_OK):
            sys.exit(u'Data directory: %s must be writable (write permissions). Exiting.' % sickbeard.DATA_DIR)

        # Make sure we can write to the config file
        if not os.access(sickbeard.CONFIG_FILE, os.W_OK):
            if os.path.isfile(sickbeard.CONFIG_FILE):
                sys.exit(u'Config file: %s must be writeable (write permissions). Exiting.' % sickbeard.CONFIG_FILE)
            elif not os.access(os.path.dirname(sickbeard.CONFIG_FILE), os.W_OK):
                sys.exit(u'Config file directory: %s must be writeable (write permissions). Exiting'
                         % os.path.dirname(sickbeard.CONFIG_FILE))
        os.chdir(sickbeard.DATA_DIR)

        if self.consoleLogging:
            print u'Starting up SickGear from %s' % sickbeard.CONFIG_FILE

        # Load the config and publish it to the sickbeard package
        if not os.path.isfile(sickbeard.CONFIG_FILE):
            print u'Unable to find "%s", all settings will be default!' % sickbeard.CONFIG_FILE

        sickbeard.CFG = ConfigObj(sickbeard.CONFIG_FILE)

        CUR_DB_VERSION = db.DBConnection().checkDBVersion()

        if CUR_DB_VERSION > 0:
            if CUR_DB_VERSION < MIN_DB_VERSION:
                print u'Your database version (%s) is too old to migrate from with this version of SickGear' \
                      % CUR_DB_VERSION
                sys.exit(u'Upgrade using a previous version of SG first, or start with no database file to begin fresh')
            if CUR_DB_VERSION > MAX_DB_VERSION:
                print u'Your database version (%s) has been incremented past what this version of SickGear supports' \
                      % CUR_DB_VERSION
                sys.exit(
                    u'If you have used other forks of SG, your database may be unusable due to their modifications')

        # Initialize the config and our threads
        sickbeard.initialize(consoleLogging=self.consoleLogging)

        if self.runAsDaemon:
            self.daemonize()

        # Get PID
        sickbeard.PID = os.getpid()

        if self.forcedPort:
            logger.log(u'Forcing web server to port %s' % self.forcedPort)
            self.startPort = self.forcedPort
        else:
            self.startPort = sickbeard.WEB_PORT

        if sickbeard.WEB_LOG:
            self.log_dir = sickbeard.LOG_DIR
        else:
            self.log_dir = None

        # sickbeard.WEB_HOST is available as a configuration value in various
        # places but is not configurable. It is supported here for historic reasons.
        if sickbeard.WEB_HOST and sickbeard.WEB_HOST != '0.0.0.0':
            self.webhost = sickbeard.WEB_HOST
        else:
            if sickbeard.WEB_IPV6:
                self.webhost = '::'
            else:
                self.webhost = '0.0.0.0'

        # web server options
        self.web_options = {
            'port': int(self.startPort),
            'host': self.webhost,
            'data_root': os.path.join(sickbeard.PROG_DIR, 'gui', sickbeard.GUI_NAME),
            'web_root': sickbeard.WEB_ROOT,
            'log_dir': self.log_dir,
            'username': sickbeard.WEB_USERNAME,
            'password': sickbeard.WEB_PASSWORD,
            'enable_https': sickbeard.ENABLE_HTTPS,
            'handle_reverse_proxy': sickbeard.HANDLE_REVERSE_PROXY,
            'https_cert': os.path.join(sickbeard.PROG_DIR, sickbeard.HTTPS_CERT),
            'https_key': os.path.join(sickbeard.PROG_DIR, sickbeard.HTTPS_KEY),
        }

        # start web server
        try:
            # used to check if existing SG instances have been started
            sickbeard.helpers.wait_for_free_port(self.web_options['host'], self.web_options['port'])

            self.webserver = WebServer(self.web_options)
            self.webserver.start()
        except Exception:
            logger.log(u'Unable to start web server, is something else running on port %d?' % self.startPort,
                       logger.ERROR)
            if sickbeard.LAUNCH_BROWSER and not self.runAsDaemon:
                logger.log(u'Launching browser and exiting', logger.ERROR)
                sickbeard.launchBrowser(self.startPort)
            os._exit(1)

        # Check if we need to perform a restore first
        restoreDir = os.path.join(sickbeard.DATA_DIR, 'restore')
        if os.path.exists(restoreDir):
            if self.restore(restoreDir, sickbeard.DATA_DIR):
                logger.log(u'Restore successful...')
            else:
                logger.log_error_and_exit(u'Restore FAILED!')

        # Build from the DB to start with
        self.loadShowsFromDB()

        # Fire up all our threads
        sickbeard.start()

        # Build internal name cache
        name_cache.buildNameCache()

        # refresh network timezones
        network_timezones.update_network_dict()

        # sure, why not?
        if sickbeard.USE_FAILED_DOWNLOADS:
            failed_history.trimHistory()

        # Start an update if we're supposed to
        if self.forceUpdate or sickbeard.UPDATE_SHOWS_ON_START:
            sickbeard.showUpdateScheduler.action.run(force=True)  # @UndefinedVariable

        # Launch browser
        if sickbeard.LAUNCH_BROWSER and not (self.noLaunch or self.runAsDaemon):
            sickbeard.launchBrowser(self.startPort)

        # main loop
        while True:
            time.sleep(1)
Beispiel #22
0
    def run(self):  # pylint: disable=too-many-branches, too-many-statements, too-many-return-statements

        super(QueueItemAdd, self).run()

        if self.showDir:
            try:
                assert isinstance(self.showDir, six.text_type)
            except AssertionError:
                logger.log(traceback.format_exc(), logger.WARNING)
                self._finish_early()
                return

        logger.log('Starting to add show {0}'.format(
            'by ShowDir: {0}'.format(self.showDir) if self.
            showDir else 'by Indexer Id: {0}'.format(self.indexer_id)))
        # make sure the Indexer IDs are valid
        try:

            lINDEXER_API_PARMS = sickbeard.indexerApi(
                self.indexer).api_params.copy()
            lINDEXER_API_PARMS[
                'language'] = self.lang or sickbeard.INDEXER_DEFAULT_LANGUAGE

            logger.log('{0}: {1!r}'.format(
                sickbeard.indexerApi(self.indexer).name, lINDEXER_API_PARMS))

            t = sickbeard.indexerApi(
                self.indexer).indexer(**lINDEXER_API_PARMS)
            s = t[self.indexer_id]

            # Let's try to create the show Dir if it's not provided. This way we force the show dir to build build using the
            # Indexers provided series name
            if self.root_dir and not self.showDir:
                show_name = get_showname_from_indexer(self.indexer,
                                                      self.indexer_id,
                                                      self.lang)
                if not show_name:
                    logger.log(
                        'Unable to get a show {0}, can\'t add the show'.format(
                            self.showDir))
                    self._finish_early()
                    return

                self.showDir = ek(os.path.join, self.root_dir,
                                  sanitize_filename(show_name))

                dir_exists = makeDir(self.showDir)
                if not dir_exists:
                    logger.log(
                        'Unable to create the folder {0}, can\'t add the show'.
                        format(self.showDir))
                    self._finish_early()
                    return

                chmodAsParent(self.showDir)

            # this usually only happens if they have an NFO in their show dir which gave us a Indexer ID that has no proper english version of the show
            if getattr(s, 'seriesname', None) is None:
                # noinspection PyPep8
                error_string = 'Show in {0} has no name on {1}, probably searched with the wrong language. Delete .nfo and add manually in the correct language.'.format(
                    self.showDir,
                    sickbeard.indexerApi(self.indexer).name)

                logger.log(error_string, logger.WARNING)
                ui.notifications.error('Unable to add show', error_string)

                self._finish_early()
                return

            # if the show has no episodes/seasons
            if not s:
                error_string = 'Show {0} is on {1} but contains no season/episode data.'.format(
                    s[b'seriesname'],
                    sickbeard.indexerApi(self.indexer).name)

                logger.log(error_string)
                ui.notifications.error('Unable to add show', error_string)

                self._finish_early()
                return
        except Exception as error:
            error_string = 'Unable to look up the show in {0} on {1} using ID {2}, not using the NFO. Delete .nfo and try adding manually again.'.format(
                self.showDir,
                sickbeard.indexerApi(self.indexer).name, self.indexer_id)

            logger.log('{0}: {1}'.format(error_string, error), logger.ERROR)
            ui.notifications.error('Unable to add show', error_string)

            if sickbeard.USE_TRAKT:
                trakt_id = sickbeard.indexerApi(
                    self.indexer).config[b'trakt_id']
                trakt_api = TraktAPI(sickbeard.SSL_VERIFY,
                                     sickbeard.TRAKT_TIMEOUT)

                title = self.showDir.split('/')[-1]
                data = {'shows': [{'title': title, 'ids': {}}]}
                if trakt_id == 'tvdb_id':
                    data['shows'][0]['ids']['tvdb'] = self.indexer_id
                else:
                    data['shows'][0]['ids']['tvrage'] = self.indexer_id

                trakt_api.traktRequest('sync/watchlist/remove',
                                       data,
                                       method='POST')

            self._finish_early()
            return

        try:
            try:
                newShow = TVShow(self.indexer, self.indexer_id, self.lang)
            except MultipleShowObjectsException as error:
                # If we have the show in our list, but the location is wrong, lets fix it and refresh!
                existing_show = Show.find(sickbeard.showList, self.indexer_id)
                # noinspection PyProtectedMember
                if existing_show and not ek(os.path.isdir,
                                            existing_show._location):  # pylint: disable=protected-access
                    newShow = existing_show
                else:
                    raise error

            newShow.loadFromIndexer()

            self.show = newShow

            # set up initial values
            self.show.location = self.showDir
            self.show.subtitles = self.subtitles if self.subtitles is not None else sickbeard.SUBTITLES_DEFAULT
            self.show.subtitles_sr_metadata = self.subtitles_sr_metadata
            self.show.quality = self.quality if self.quality else sickbeard.QUALITY_DEFAULT
            self.show.season_folders = self.season_folders if self.season_folders is not None else sickbeard.SEASON_FOLDERS_DEFAULT
            self.show.anime = self.anime if self.anime is not None else sickbeard.ANIME_DEFAULT
            self.show.scene = self.scene if self.scene is not None else sickbeard.SCENE_DEFAULT
            self.show.paused = self.paused if self.paused is not None else False

            # set up default new/missing episode status
            logger.log(
                'Setting all episodes to the specified default status: {0}'.
                format(self.show.default_ep_status))
            self.show.default_ep_status = self.default_status

            if self.show.anime:
                self.show.release_groups = BlackAndWhiteList(
                    self.show.indexerid)
                if self.blacklist:
                    self.show.release_groups.set_black_keywords(self.blacklist)
                if self.whitelist:
                    self.show.release_groups.set_white_keywords(self.whitelist)

            # # be smart-ish about this
            # if self.show.genre and 'talk show' in self.show.genre.lower():
            #     self.show.air_by_date = 1
            # if self.show.genre and 'documentary' in self.show.genre.lower():
            #     self.show.air_by_date = 0
            # if self.show.classification and 'sports' in self.show.classification.lower():
            #     self.show.sports = 1

        except sickbeard.indexer_exception as error:
            error_string = 'Unable to add {0} due to an error with {1}'.format(
                self.show.name if self.show else 'show',
                sickbeard.indexerApi(self.indexer).name)

            logger.log('{0}: {1}'.format(error_string, error), logger.ERROR)
            ui.notifications.error('Unable to add show', error_string)

            self._finish_early()
            return

        except MultipleShowObjectsException:
            error_string = 'The show in {0} is already in your show list, skipping'.format(
                self.showDir)
            logger.log(error_string, logger.WARNING)
            ui.notifications.error('Show skipped', error_string)

            self._finish_early()
            return

        except Exception as error:
            logger.log('Error trying to add show: {0}'.format(error),
                       logger.ERROR)
            logger.log(traceback.format_exc(), logger.DEBUG)
            self._finish_early()
            raise

        logger.log('Retrieving show info from IMDb', logger.DEBUG)
        try:
            self.show.loadIMDbInfo()
        except imdb_exceptions.IMDbError as error:
            logger.log(' Something wrong on IMDb api: {0}'.format(error),
                       logger.WARNING)
        except Exception as error:
            logger.log('Error loading IMDb info: {0}'.format(error),
                       logger.ERROR)

        try:
            self.show.saveToDB()
        except Exception as error:
            logger.log(
                'Error saving the show to the database: {0}'.format(error),
                logger.ERROR)
            logger.log(traceback.format_exc(), logger.DEBUG)
            self._finish_early()
            raise

        # add it to the show list
        if not Show.find(sickbeard.showList, self.indexer_id):
            sickbeard.showList.append(self.show)

        try:
            self.show.loadEpisodesFromIndexer()
        except Exception as error:
            logger.log(
                'Error with {0}, not creating episode list: {1}'.format(
                    sickbeard.indexerApi(self.show.indexer).name, error),
                logger.ERROR)
            logger.log(traceback.format_exc(), logger.DEBUG)

        # update internal name cache
        name_cache.buildNameCache(self.show)

        try:
            self.show.loadEpisodesFromDir()
        except Exception as error:
            logger.log('Error searching dir for episodes: {0}'.format(error),
                       logger.ERROR)
            logger.log(traceback.format_exc(), logger.DEBUG)

        # if they set default ep status to WANTED then run the backlog to search for episodes
        # FIXME: This needs to be a backlog queue item!!!
        if self.show.default_ep_status == WANTED:
            logger.log(
                'Launching backlog for this show since its episodes are WANTED'
            )
            sickbeard.backlogSearchScheduler.action.searchBacklog([self.show])

        self.show.writeMetadata()
        self.show.updateMetadata()
        self.show.populateCache()

        self.show.flushEpisodes()

        if sickbeard.USE_TRAKT:
            # if there are specific episodes that need to be added by trakt
            sickbeard.traktCheckerScheduler.action.manageNewShow(self.show)
            # add show to trakt.tv library
            if sickbeard.TRAKT_SYNC:
                sickbeard.traktCheckerScheduler.action.addShowToTraktLibrary(
                    self.show)

            if sickbeard.TRAKT_SYNC_WATCHLIST:
                logger.log('update watchlist')
                notifiers.trakt_notifier.update_watchlist(show_obj=self.show)

        # Load XEM data to DB for show
        scene_numbering.xem_refresh(self.show.indexerid,
                                    self.show.indexer,
                                    force=True)

        # check if show has XEM mapping so we can determine if searches should go by scene numbering or indexer numbering.
        if not self.scene and scene_numbering.get_xem_numbering_for_show(
                self.show.indexerid, self.show.indexer):
            self.show.scene = 1

        # After initial add, set to default_status_after.
        self.show.default_ep_status = self.default_status_after

        super(QueueItemAdd, self).finish()
        self.finish()
Beispiel #23
0
    def start(self):
        # do some preliminary stuff
        sickbeard.MY_FULLNAME = ek(os.path.normpath, ek(os.path.abspath, __file__))
        sickbeard.MY_NAME = ek(os.path.basename, sickbeard.MY_FULLNAME)
        sickbeard.PROG_DIR = ek(os.path.dirname, sickbeard.MY_FULLNAME)
        sickbeard.DATA_DIR = sickbeard.PROG_DIR
        sickbeard.MY_ARGS = sys.argv[1:]

        try:
            locale.setlocale(locale.LC_ALL, "")
            sickbeard.SYS_ENCODING = locale.getpreferredencoding()
        except (locale.Error, IOError):
            sickbeard.SYS_ENCODING = 'UTF-8'

        # pylint: disable=no-member
        if not sickbeard.SYS_ENCODING or sickbeard.SYS_ENCODING.lower() in ('ansi_x3.4-1968', 'us-ascii', 'ascii', 'charmap') or \
            (sys.platform.startswith('win') and sys.getwindowsversion()[0] >= 6 and str(getattr(sys.stdout, 'device', sys.stdout).encoding).lower() in ('cp65001', 'charmap')):
            sickbeard.SYS_ENCODING = 'UTF-8'

        # TODO: Continue working on making this unnecessary, this hack creates all sorts of hellish problems
        if not hasattr(sys, "setdefaultencoding"):
            reload(sys)

        try:
            # pylint: disable=no-member
            # On non-unicode builds this will raise an AttributeError, if encoding type is not valid it throws a LookupError
            sys.setdefaultencoding(sickbeard.SYS_ENCODING)
        except Exception:
            sys.exit("Sorry, you MUST add the SickRage folder to the PYTHONPATH environment variable\n" +
                     "or find another way to force Python to use " + sickbeard.SYS_ENCODING + " for string encoding.")

        # Need console logging for SickBeard.py and SickBeard-console.exe
        self.consoleLogging = (not hasattr(sys, "frozen")) or (sickbeard.MY_NAME.lower().find('-console') > 0)

        # Rename the main thread
        threading.currentThread().name = u"MAIN"

        try:
            opts, _ = getopt.getopt(
                sys.argv[1:], "hqdp::",
                ['help', 'quiet', 'nolaunch', 'daemon', 'pidfile=', 'port=', 'datadir=', 'config=', 'noresize']
            )
        except getopt.GetoptError:
            sys.exit(self.help_message())

        for o, a in opts:
            # Prints help message
            if o in ('-h', '--help'):
                sys.exit(self.help_message())

            # For now we'll just silence the logging
            if o in ('-q', '--quiet'):
                self.consoleLogging = False

            # Suppress launching web browser
            # Needed for OSes without default browser assigned
            # Prevent duplicate browser window when restarting in the app
            if o in ('--nolaunch',):
                self.noLaunch = True

            # Override default/configured port
            if o in ('-p', '--port'):
                try:
                    self.forcedPort = int(a)
                except ValueError:
                    sys.exit("Port: " + str(a) + " is not a number. Exiting.")

            # Run as a double forked daemon
            if o in ('-d', '--daemon'):
                self.runAsDaemon = True
                # When running as daemon disable consoleLogging and don't start browser
                self.consoleLogging = False
                self.noLaunch = True

                if sys.platform == 'win32' or sys.platform == 'darwin':
                    self.runAsDaemon = False

            # Write a pidfile if requested
            if o in ('--pidfile',):
                self.CREATEPID = True
                self.PIDFILE = str(a)

                # If the pidfile already exists, sickbeard may still be running, so exit
                if ek(os.path.exists, self.PIDFILE):
                    sys.exit("PID file: " + self.PIDFILE + " already exists. Exiting.")

            # Specify folder to load the config file from
            if o in ('--config',):
                sickbeard.CONFIG_FILE = ek(os.path.abspath, a)

            # Specify folder to use as the data dir
            if o in ('--datadir',):
                sickbeard.DATA_DIR = ek(os.path.abspath, a)

            # Prevent resizing of the banner/posters even if PIL is installed
            if o in ('--noresize',):
                sickbeard.NO_RESIZE = True

        # The pidfile is only useful in daemon mode, make sure we can write the file properly
        if self.CREATEPID:
            if self.runAsDaemon:
                pid_dir = ek(os.path.dirname, self.PIDFILE)
                if not ek(os.access, pid_dir, os.F_OK):
                    sys.exit("PID dir: " + pid_dir + " doesn't exist. Exiting.")
                if not ek(os.access, pid_dir, os.W_OK):
                    sys.exit("PID dir: " + pid_dir + " must be writable (write permissions). Exiting.")

            else:
                if self.consoleLogging:
                    sys.stdout.write(u"Not running in daemon mode. PID file creation disabled.\n")

                self.CREATEPID = False

        # If they don't specify a config file then put it in the data dir
        if not sickbeard.CONFIG_FILE:
            sickbeard.CONFIG_FILE = ek(os.path.join, sickbeard.DATA_DIR, "config.ini")

        # Make sure that we can create the data dir
        if not ek(os.access, sickbeard.DATA_DIR, os.F_OK):
            try:
                ek(os.makedirs, sickbeard.DATA_DIR, 0744)
            except os.error:
                raise SystemExit("Unable to create datadir '" + sickbeard.DATA_DIR + "'")

        # Make sure we can write to the data dir
        if not ek(os.access, sickbeard.DATA_DIR, os.W_OK):
            raise SystemExit("Datadir must be writeable '" + sickbeard.DATA_DIR + "'")

        # Make sure we can write to the config file
        if not ek(os.access, sickbeard.CONFIG_FILE, os.W_OK):
            if ek(os.path.isfile, sickbeard.CONFIG_FILE):
                raise SystemExit("Config file '" + sickbeard.CONFIG_FILE + "' must be writeable.")
            elif not ek(os.access, ek(os.path.dirname, sickbeard.CONFIG_FILE), os.W_OK):
                raise SystemExit(
                    "Config file root dir '" + ek(os.path.dirname, sickbeard.CONFIG_FILE) + "' must be writeable.")

        ek(os.chdir, sickbeard.DATA_DIR)

        # Check if we need to perform a restore first
        restoreDir = ek(os.path.join, sickbeard.DATA_DIR, 'restore')
        if ek(os.path.exists, restoreDir):
            success = self.restoreDB(restoreDir, sickbeard.DATA_DIR)
            if self.consoleLogging:
                sys.stdout.write(u"Restore: restoring DB and config.ini %s!\n" % ("FAILED", "SUCCESSFUL")[success])

        # Load the config and publish it to the sickbeard package
        if self.consoleLogging and not ek(os.path.isfile, sickbeard.CONFIG_FILE):
            sys.stdout.write(u"Unable to find '" + sickbeard.CONFIG_FILE + "' , all settings will be default!" + "\n")

        sickbeard.CFG = ConfigObj(sickbeard.CONFIG_FILE)

        # Initialize the config and our threads
        sickbeard.initialize(consoleLogging=self.consoleLogging)

        if self.runAsDaemon:
            self.daemonize()

        # Get PID
        sickbeard.PID = os.getpid()

        # Build from the DB to start with
        self.loadShowsFromDB()

        if self.forcedPort:
            logger.log(u"Forcing web server to port " + str(self.forcedPort))
            self.startPort = self.forcedPort
        else:
            self.startPort = sickbeard.WEB_PORT

        if sickbeard.WEB_LOG:
            self.log_dir = sickbeard.LOG_DIR
        else:
            self.log_dir = None

        # sickbeard.WEB_HOST is available as a configuration value in various
        # places but is not configurable. It is supported here for historic reasons.
        if sickbeard.WEB_HOST and sickbeard.WEB_HOST != '0.0.0.0':
            self.webhost = sickbeard.WEB_HOST
        else:
            if sickbeard.WEB_IPV6:
                self.webhost = ''
            else:
                self.webhost = '0.0.0.0'

        # web server options
        self.web_options = {
            'port': int(self.startPort),
            'host': self.webhost,
            'data_root': ek(os.path.join, sickbeard.PROG_DIR, 'gui', sickbeard.GUI_NAME),
            'web_root': sickbeard.WEB_ROOT,
            'log_dir': self.log_dir,
            'username': sickbeard.WEB_USERNAME,
            'password': sickbeard.WEB_PASSWORD,
            'enable_https': sickbeard.ENABLE_HTTPS,
            'handle_reverse_proxy': sickbeard.HANDLE_REVERSE_PROXY,
            'https_cert': ek(os.path.join, sickbeard.PROG_DIR, sickbeard.HTTPS_CERT),
            'https_key': ek(os.path.join, sickbeard.PROG_DIR, sickbeard.HTTPS_KEY),
        }

        # start web server
        self.webserver = SRWebServer(self.web_options)
        self.webserver.start()

        if self.consoleLogging:
            print "Starting up SickRage " + sickbeard.BRANCH + " from " + sickbeard.CONFIG_FILE

        # Clean up after update
        if sickbeard.GIT_NEWVER:
            toclean = ek(os.path.join, sickbeard.CACHE_DIR, 'mako')
            for root, dirs, files in ek(os.walk, toclean, topdown=False):
                for name in files:
                    ek(os.remove, ek(os.path.join, root, name))
                for name in dirs:
                    ek(os.rmdir, ek(os.path.join, root, name))
            sickbeard.GIT_NEWVER = False

        # Fire up all our threads
        sickbeard.start()

        # Build internal name cache
        name_cache.buildNameCache()

        # Prepopulate network timezones, it isn't thread safe
        network_timezones.update_network_dict()

        # sure, why not?
        if sickbeard.USE_FAILED_DOWNLOADS:
            failed_history.trimHistory()

        # # Check for metadata indexer updates for shows (Disabled until we use api)
        # sickbeard.showUpdateScheduler.forceRun()

        # Launch browser
        if sickbeard.LAUNCH_BROWSER and not (self.noLaunch or self.runAsDaemon):
            sickbeard.launchBrowser('https' if sickbeard.ENABLE_HTTPS else 'http', self.startPort, sickbeard.WEB_ROOT)

        # main loop
        while True:
            time.sleep(1)
Beispiel #24
0
class SickRage(object):
    def __init__(self):
        # system event callback for shutdown/restart
        sickbeard.events = Events(self.shutdown)

        # daemon constants
        self.runAsDaemon = False
        self.CREATEPID = False
        self.PIDFILE = ''

        # webserver constants
        self.webserver = None
        self.forceUpdate = False
        self.forcedPort = None
        self.noLaunch = False

    def help_message(self):
        """
        print help message for commandline options
        """
        help_msg = "\n"
        help_msg += "Usage: " + sickbeard.MY_FULLNAME + " <option> <another option>\n"
        help_msg += "\n"
        help_msg += "Options:\n"
        help_msg += "\n"
        help_msg += "    -h          --help              Prints this message\n"
        help_msg += "    -f          --forceupdate       Force update all shows in the DB (from tvdb) on startup\n"
        help_msg += "    -q          --quiet             Disables logging to console\n"
        help_msg += "                --nolaunch          Suppress launching web browser on startup\n"

        if sys.platform == 'win32' or sys.platform == 'darwin':
            help_msg += "    -d          --daemon            Running as real daemon is not supported on Windows\n"
            help_msg += "                                    On Windows and MAC, --daemon is substituted with: --quiet --nolaunch\n"
        else:
            help_msg += "    -d          --daemon            Run as double forked daemon (includes options --quiet --nolaunch)\n"
            help_msg += "                --pidfile=<path>    Combined with --daemon creates a pidfile (full path including filename)\n"

        help_msg += "    -p <port>   --port=<port>       Override default/configured port to listen on\n"
        help_msg += "                --datadir=<path>    Override folder (full path) as location for\n"
        help_msg += "                                    storing database, configfile, cache, logfiles \n"
        help_msg += "                                    Default: " + sickbeard.PROG_DIR + "\n"
        help_msg += "                --config=<path>     Override config filename (full path including filename)\n"
        help_msg += "                                    to load configuration from \n"
        help_msg += "                                    Default: config.ini in " + sickbeard.PROG_DIR + " or --datadir location\n"
        help_msg += "                --noresize          Prevent resizing of the banner/posters even if PIL is installed\n"

        return help_msg

    def start(self):
        # do some preliminary stuff
        sickbeard.MY_FULLNAME = os.path.normpath(os.path.abspath(__file__))
        sickbeard.MY_NAME = os.path.basename(sickbeard.MY_FULLNAME)
        sickbeard.PROG_DIR = os.path.dirname(sickbeard.MY_FULLNAME)
        sickbeard.DATA_DIR = sickbeard.PROG_DIR
        sickbeard.MY_ARGS = sys.argv[1:]
        sickbeard.SYS_ENCODING = None

        try:
            locale.setlocale(locale.LC_ALL, "")
            sickbeard.SYS_ENCODING = locale.getpreferredencoding()
        except (locale.Error, IOError):
            pass

        # For OSes that are poorly configured I'll just randomly force UTF-8
        if not sickbeard.SYS_ENCODING or sickbeard.SYS_ENCODING in (
                'ANSI_X3.4-1968', 'US-ASCII', 'ASCII'):
            sickbeard.SYS_ENCODING = 'UTF-8'

        if not hasattr(sys, "setdefaultencoding"):
            reload(sys)

        if sys.platform == 'win32':
            if sys.getwindowsversion(
            )[0] >= 6 and sys.stdout.encoding == 'cp65001':
                sickbeard.SYS_ENCODING = 'UTF-8'

        try:
            # pylint: disable=E1101
            # On non-unicode builds this will raise an AttributeError, if encoding type is not valid it throws a LookupError
            sys.setdefaultencoding(sickbeard.SYS_ENCODING)
        except:
            sys.exit(
                "Sorry, you MUST add the SickRage folder to the PYTHONPATH environment variable\n"
                + "or find another way to force Python to use " +
                sickbeard.SYS_ENCODING + " for string encoding.")

        # Need console logging for SickBeard.py and SickBeard-console.exe
        self.consoleLogging = (not hasattr(
            sys, "frozen")) or (sickbeard.MY_NAME.lower().find('-console') > 0)

        # Rename the main thread
        threading.currentThread().name = "MAIN"

        try:
            opts, args = getopt.getopt(sys.argv[1:], "hfqdp::", [
                'help', 'forceupdate', 'quiet', 'nolaunch', 'daemon',
                'pidfile=', 'port=', 'datadir=', 'config=', 'noresize'
            ])  # @UnusedVariable
        except getopt.GetoptError:
            sys.exit(self.help_message())

        for o, a in opts:
            # Prints help message
            if o in ('-h', '--help'):
                sys.exit(self.help_message())

            # For now we'll just silence the logging
            if o in ('-q', '--quiet'):
                self.consoleLogging = False

            # Should we update (from indexer) all shows in the DB right away?
            if o in ('-f', '--forceupdate'):
                self.forceUpdate = True

            # Suppress launching web browser
            # Needed for OSes without default browser assigned
            # Prevent duplicate browser window when restarting in the app
            if o in ('--nolaunch', ):
                self.noLaunch = True

            # Override default/configured port
            if o in ('-p', '--port'):
                try:
                    self.forcedPort = int(a)
                except ValueError:
                    sys.exit("Port: " + str(a) + " is not a number. Exiting.")

            # Run as a double forked daemon
            if o in ('-d', '--daemon'):
                self.runAsDaemon = True
                # When running as daemon disable consoleLogging and don't start browser
                self.consoleLogging = False
                self.noLaunch = True

                if sys.platform == 'win32' or sys.platform == 'darwin':
                    self.runAsDaemon = False

            # Write a pidfile if requested
            if o in ('--pidfile', ):
                self.CREATEPID = True
                self.PIDFILE = str(a)

                # If the pidfile already exists, sickbeard may still be running, so exit
                if os.path.exists(self.PIDFILE):
                    sys.exit("PID file: " + self.PIDFILE +
                             " already exists. Exiting.")

            # Specify folder to load the config file from
            if o in ('--config', ):
                sickbeard.CONFIG_FILE = os.path.abspath(a)

            # Specify folder to use as the data dir
            if o in ('--datadir', ):
                sickbeard.DATA_DIR = os.path.abspath(a)

            # Prevent resizing of the banner/posters even if PIL is installed
            if o in ('--noresize', ):
                sickbeard.NO_RESIZE = True

        # The pidfile is only useful in daemon mode, make sure we can write the file properly
        if self.CREATEPID:
            if self.runAsDaemon:
                pid_dir = os.path.dirname(self.PIDFILE)
                if not os.access(pid_dir, os.F_OK):
                    sys.exit("PID dir: " + pid_dir +
                             " doesn't exist. Exiting.")
                if not os.access(pid_dir, os.W_OK):
                    sys.exit("PID dir: " + pid_dir +
                             " must be writable (write permissions). Exiting.")

            else:
                if self.consoleLogging:
                    sys.stdout.write(
                        "Not running in daemon mode. PID file creation disabled.\n"
                    )

                self.CREATEPID = False

        # If they don't specify a config file then put it in the data dir
        if not sickbeard.CONFIG_FILE:
            sickbeard.CONFIG_FILE = os.path.join(sickbeard.DATA_DIR,
                                                 "config.ini")

        # Make sure that we can create the data dir
        if not os.access(sickbeard.DATA_DIR, os.F_OK):
            try:
                os.makedirs(sickbeard.DATA_DIR, 0744)
            except os.error, e:
                raise SystemExit("Unable to create datadir '" +
                                 sickbeard.DATA_DIR + "'")

        # Make sure we can write to the data dir
        if not os.access(sickbeard.DATA_DIR, os.W_OK):
            raise SystemExit("Datadir must be writeable '" +
                             sickbeard.DATA_DIR + "'")

        # Make sure we can write to the config file
        if not os.access(sickbeard.CONFIG_FILE, os.W_OK):
            if os.path.isfile(sickbeard.CONFIG_FILE):
                raise SystemExit("Config file '" + sickbeard.CONFIG_FILE +
                                 "' must be writeable.")
            elif not os.access(os.path.dirname(sickbeard.CONFIG_FILE),
                               os.W_OK):
                raise SystemExit("Config file root dir '" +
                                 os.path.dirname(sickbeard.CONFIG_FILE) +
                                 "' must be writeable.")

        os.chdir(sickbeard.DATA_DIR)

        # Check if we need to perform a restore first
        try:
            restoreDir = os.path.join(sickbeard.DATA_DIR, 'restore')
            if self.consoleLogging and os.path.exists(restoreDir):
                if self.restoreDB(restoreDir, sickbeard.DATA_DIR):
                    sys.stdout.write(
                        "Restore: restoring DB and config.ini successful...\n")
                else:
                    sys.stdout.write(
                        "Restore: restoring DB and config.ini FAILED!\n")
        except Exception as e:
            sys.stdout.write("Restore: restoring DB and config.ini FAILED!\n")

        # Load the config and publish it to the sickbeard package
        if self.consoleLogging and not os.path.isfile(sickbeard.CONFIG_FILE):
            sys.stdout.write("Unable to find '" + sickbeard.CONFIG_FILE +
                             "' , all settings will be default!" + "\n")

        sickbeard.CFG = ConfigObj(sickbeard.CONFIG_FILE)

        # Initialize the config and our threads
        sickbeard.initialize(consoleLogging=self.consoleLogging)

        if self.runAsDaemon:
            self.daemonize()

        # Get PID
        sickbeard.PID = os.getpid()

        # Build from the DB to start with
        self.loadShowsFromDB()

        if self.forcedPort:
            logger.log(u"Forcing web server to port " + str(self.forcedPort))
            self.startPort = self.forcedPort
        else:
            self.startPort = sickbeard.WEB_PORT

        if sickbeard.WEB_LOG:
            self.log_dir = sickbeard.LOG_DIR
        else:
            self.log_dir = None

        # sickbeard.WEB_HOST is available as a configuration value in various
        # places but is not configurable. It is supported here for historic reasons.
        if sickbeard.WEB_HOST and sickbeard.WEB_HOST != '0.0.0.0':
            self.webhost = sickbeard.WEB_HOST
        else:
            if sickbeard.WEB_IPV6:
                self.webhost = '::'
            else:
                self.webhost = '0.0.0.0'

        # web server options
        self.web_options = {
            'port':
            int(self.startPort),
            'host':
            self.webhost,
            'data_root':
            os.path.join(sickbeard.PROG_DIR, 'gui', sickbeard.GUI_NAME),
            'web_root':
            sickbeard.WEB_ROOT,
            'log_dir':
            self.log_dir,
            'username':
            sickbeard.WEB_USERNAME,
            'password':
            sickbeard.WEB_PASSWORD,
            'enable_https':
            sickbeard.ENABLE_HTTPS,
            'handle_reverse_proxy':
            sickbeard.HANDLE_REVERSE_PROXY,
            'https_cert':
            os.path.join(sickbeard.PROG_DIR, sickbeard.HTTPS_CERT),
            'https_key':
            os.path.join(sickbeard.PROG_DIR, sickbeard.HTTPS_KEY),
        }

        # start web server
        try:
            self.webserver = SRWebServer(self.web_options)
            self.webserver.start()
        except IOError:
            logger.log(
                u"Unable to start web server, is something else running on port %d?"
                % self.startPort, logger.ERROR)
            if sickbeard.LAUNCH_BROWSER and not self.runAsDaemon:
                logger.log(u"Launching browser and exiting", logger.ERROR)
                sickbeard.launchBrowser(
                    'https' if sickbeard.ENABLE_HTTPS else 'http',
                    self.startPort, sickbeard.WEB_ROOT)
            os._exit(1)

        if self.consoleLogging:
            print "Starting up SickRage " + sickbeard.BRANCH + " from " + sickbeard.CONFIG_FILE

        # Fire up all our threads
        sickbeard.start()

        # Build internal name cache
        name_cache.buildNameCache()

        # refresh network timezones
        network_timezones.update_network_dict()

        # sure, why not?
        if sickbeard.USE_FAILED_DOWNLOADS:
            failed_history.trimHistory()

        # Start an update if we're supposed to
        if self.forceUpdate or sickbeard.UPDATE_SHOWS_ON_START:
            sickbeard.showUpdateScheduler.forceRun()

        # Launch browser
        if sickbeard.LAUNCH_BROWSER and not (self.noLaunch
                                             or self.runAsDaemon):
            sickbeard.launchBrowser(
                'https' if sickbeard.ENABLE_HTTPS else 'http', self.startPort,
                sickbeard.WEB_ROOT)

        # main loop
        while (True):
            time.sleep(1)
Beispiel #25
0
            self._finishEarly()
            raise

        # add it to the show list
        sickbeard.showList.append(self.show)

        try:
            self.show.loadEpisodesFromIndexer()
        except Exception, e:
            logger.log(
                u"Error with " + sickbeard.indexerApi(self.show.indexer).name + ", not creating episode list: " + ex(e),
                logger.ERROR)
            logger.log(traceback.format_exc(), logger.DEBUG)

        # update internal name cache
        name_cache.buildNameCache()

        try:
            self.show.loadEpisodesFromDir()
        except Exception, e:
            logger.log(u"Error searching dir for episodes: " + ex(e), logger.ERROR)
            logger.log(traceback.format_exc(), logger.DEBUG)

        # if they gave a custom status then change all the eps to it
        if self.default_status != SKIPPED:
            logger.log(u"Setting all episodes to the specified default status: " + str(self.default_status))
            myDB = db.DBConnection()
            myDB.action("UPDATE tv_episodes SET status = ? WHERE status = ? AND showid = ? AND season != 0",
                        [self.default_status, SKIPPED, self.show.indexerid])

        # if they started with WANTED eps then run the backlog
Beispiel #26
0
    def run(self):

        ShowQueueItem.run(self)

        logger.log('Starting to add show %s' % self.showDir)
        # make sure the Indexer IDs are valid
        try:

            lINDEXER_API_PARMS = sickbeard.indexerApi(
                self.indexer).api_params.copy()
            if self.lang:
                lINDEXER_API_PARMS['language'] = self.lang

            logger.log(u'' + str(sickbeard.indexerApi(self.indexer).name) +
                       ': ' + repr(lINDEXER_API_PARMS))

            t = sickbeard.indexerApi(
                self.indexer).indexer(**lINDEXER_API_PARMS)
            s = t[self.indexer_id, False]

            if getattr(t, 'show_not_found', False):
                logger.log(
                    'Show %s was not found on %s, maybe show was deleted' %
                    (self.show_name, sickbeard.indexerApi(self.indexer).name),
                    logger.ERROR)
                self._finishEarly()
                return

            # this usually only happens if they have an NFO in their show dir which gave us a Indexer ID that has no proper english version of the show
            if getattr(s, 'seriesname', None) is None:
                logger.log(
                    'Show in %s has no name on %s, probably the wrong language used to search with.'
                    % (self.showDir, sickbeard.indexerApi(self.indexer).name),
                    logger.ERROR)
                ui.notifications.error(
                    'Unable to add show',
                    'Show in %s has no name on %s, probably the wrong language. Delete .nfo and add manually in the correct language.'
                    % (self.showDir, sickbeard.indexerApi(self.indexer).name))
                self._finishEarly()
                return
        except Exception as e:
            logger.log(
                'Unable to find show ID:%s on Indexer: %s' %
                (self.indexer_id, sickbeard.indexerApi(self.indexer).name),
                logger.ERROR)
            ui.notifications.error(
                'Unable to add show',
                'Unable to look up the show in %s on %s using ID %s, not using the NFO. Delete .nfo and try adding manually again.'
                % (self.showDir, sickbeard.indexerApi(
                    self.indexer).name, self.indexer_id))
            self._finishEarly()
            return

        try:
            newShow = TVShow(self.indexer, self.indexer_id, self.lang)
            newShow.loadFromIndexer()

            self.show = newShow

            # set up initial values
            self.show.location = self.showDir
            self.show.subtitles = self.subtitles if None is not self.subtitles else sickbeard.SUBTITLES_DEFAULT
            self.show.quality = self.quality if self.quality else sickbeard.QUALITY_DEFAULT
            self.show.upgrade_once = self.upgrade_once
            self.show.flatten_folders = self.flatten_folders if None is not self.flatten_folders else sickbeard.FLATTEN_FOLDERS_DEFAULT
            self.show.anime = self.anime if None is not self.anime else sickbeard.ANIME_DEFAULT
            self.show.scene = self.scene if None is not self.scene else sickbeard.SCENE_DEFAULT
            self.show.paused = self.paused if None is not self.paused else False
            self.show.tag = self.tag if None is not self.tag else 'Show List'

            if self.show.anime:
                self.show.release_groups = BlackAndWhiteList(
                    self.show.indexerid)
                if self.blacklist:
                    self.show.release_groups.set_black_keywords(self.blacklist)
                if self.whitelist:
                    self.show.release_groups.set_white_keywords(self.whitelist)

            # be smartish about this
            if self.show.genre and 'talk show' in self.show.genre.lower():
                self.show.air_by_date = 1
            if self.show.genre and 'documentary' in self.show.genre.lower():
                self.show.air_by_date = 0
            if self.show.classification and 'sports' in self.show.classification.lower(
            ):
                self.show.sports = 1

        except sickbeard.indexer_exception as e:
            logger.log(
                'Unable to add show due to an error with %s: %s' %
                (sickbeard.indexerApi(self.indexer).name, ex(e)), logger.ERROR)
            if self.show:
                ui.notifications.error(
                    'Unable to add %s due to an error with %s' %
                    (self.show.name, sickbeard.indexerApi(self.indexer).name))
            else:
                ui.notifications.error(
                    'Unable to add show due to an error with %s' %
                    sickbeard.indexerApi(self.indexer).name)
            self._finishEarly()
            return

        except exceptions.MultipleShowObjectsException:
            logger.log(
                'The show in %s is already in your show list, skipping' %
                self.showDir, logger.ERROR)
            ui.notifications.error(
                'Show skipped',
                'The show in %s is already in your show list' % self.showDir)
            self._finishEarly()
            return

        except Exception as e:
            logger.log('Error trying to add show: %s' % ex(e), logger.ERROR)
            logger.log(traceback.format_exc(), logger.ERROR)
            self._finishEarly()
            raise

        self.show.load_imdb_info()

        try:
            self.show.saveToDB()
        except Exception as e:
            logger.log('Error saving the show to the database: %s' % ex(e),
                       logger.ERROR)
            logger.log(traceback.format_exc(), logger.ERROR)
            self._finishEarly()
            raise

        # add it to the show list
        sickbeard.showList.append(self.show)

        try:
            self.show.loadEpisodesFromIndexer()
        except Exception as e:
            logger.log(
                'Error with %s, not creating episode list: %s' %
                (sickbeard.indexerApi(self.show.indexer).name, ex(e)),
                logger.ERROR)
            logger.log(traceback.format_exc(), logger.ERROR)

        try:
            self.show.loadEpisodesFromDir()
        except Exception as e:
            logger.log('Error searching directory for episodes: %s' % ex(e),
                       logger.ERROR)
            logger.log(traceback.format_exc(), logger.ERROR)

        # if they gave a custom status then change all the eps to it
        my_db = db.DBConnection()
        if self.default_status != SKIPPED:
            logger.log(
                'Setting all episodes to the specified default status: %s' %
                sickbeard.common.statusStrings[self.default_status])
            my_db.action(
                'UPDATE tv_episodes SET status = ? WHERE status = ? AND showid = ? AND season != 0',
                [self.default_status, SKIPPED, self.show.indexerid])

        # if they gave a number to start or number to end as wanted, then change those eps to it
        def get_wanted(db_obj, wanted_max, latest):
            actual = 0
            if wanted_max:
                select_id = 'FROM [tv_episodes] t5 JOIN (SELECT t3.indexerid, t3.status, t3.season*1000000+t3.episode AS t3_se, t2.start_season FROM [tv_episodes] t3'\
                            + ' JOIN (SELECT t1.showid, M%s(t1.season) AS start_season' % ('IN', 'AX')[latest]\
                            + ', MAX(t1.airdate) AS airdate, t1.episode, t1.season*1000000+t1.episode AS se FROM [tv_episodes] t1'\
                            + ' WHERE %s=t1.showid' % self.show.indexerid\
                            + ' AND 0<t1.season AND t1.status NOT IN (%s)) AS t2' % UNAIRED\
                            + ' ON t2.showid=t3.showid AND 0<t3.season AND t2.se>=t3_se ORDER BY t3_se %sSC' % ('A', 'DE')[latest]\
                            + ' %s) as t4' % (' LIMIT %s' % wanted_max, '')[-1 == wanted_max]\
                            + ' ON t4.indexerid=t5.indexerid'\
                            + '%s' % ('', ' AND t4.start_season=t5.season')[-1 == wanted_max]\
                            + ' AND t4.status NOT IN (%s)' % ','.join([str(x) for x in sickbeard.common.Quality.DOWNLOADED + [WANTED]])
                select = 'SELECT t5.indexerid as indexerid, t5.season as season, t5.episode as episode, t5.status as status ' + select_id
                update = 'UPDATE [tv_episodes] SET status=%s WHERE indexerid IN (SELECT t5.indexerid %s)' % (
                    WANTED, select_id)

                wanted_updates = db_obj.select(select)
                db_obj.action(update)
                result = db_obj.select(
                    'SELECT changes() as last FROM [tv_episodes]')
                for cur_result in result:
                    actual = cur_result['last']
                    break

                action_log = 'didn\'t find any episodes that need to be set wanted'
                if actual:
                    action_log = ('updated %s %s episodes > %s' % (
                        (((('%s of %s' % (actual, wanted_max)),
                           ('%s of max %s limited' %
                            (actual, wanted_max)))[10 == wanted_max]),
                         ('max %s available' % actual))[-1 == wanted_max],
                        ('first season', 'latest')[latest], ','.join(
                            [('S%02dE%02d=%d' %
                              (a['season'], a['episode'], a['status']))
                             for a in wanted_updates])))
                logger.log('Get wanted ' + action_log)
            return actual

        items_wanted = get_wanted(my_db,
                                  self.default_wanted_begin,
                                  latest=False)
        items_wanted += get_wanted(my_db,
                                   self.default_wanted_latest,
                                   latest=True)

        self.show.writeMetadata()
        self.show.updateMetadata()
        self.show.populateCache()

        self.show.flushEpisodes()

        # load ids
        self.show.ids

        # if sickbeard.USE_TRAKT:
        #     # if there are specific episodes that need to be added by trakt
        #     sickbeard.traktCheckerScheduler.action.manageNewShow(self.show)
        #
        #     # add show to trakt.tv library
        #     if sickbeard.TRAKT_SYNC:
        #         sickbeard.traktCheckerScheduler.action.addShowToTraktLibrary(self.show)

        # Load XEM data to DB for show
        sickbeard.scene_numbering.xem_refresh(self.show.indexerid,
                                              self.show.indexer,
                                              force=True)
        # check if show has XEM mapping and if user disabled scene numbering during add show, output availability to log
        if not self.scene and self.show.indexerid in sickbeard.scene_exceptions.xem_ids_list[
                self.show.indexer]:
            logger.log(
                'Alternative scene episode numbers were disabled during add show. Edit show to enable them for searching.'
            )

        # update internal name cache
        name_cache.buildNameCache(self.show)

        self.show.loadEpisodesFromDB()

        msg = ' the specified show into ' + self.showDir
        # if started with WANTED eps then run the backlog
        if WANTED == self.default_status or items_wanted:
            logger.log(
                'Launching backlog for this show since episodes are WANTED')
            sickbeard.backlogSearchScheduler.action.search_backlog(
                [self.show])  #@UndefinedVariable
            ui.notifications.message(
                'Show added/search',
                'Adding and searching for episodes of' + msg)
        else:
            ui.notifications.message('Show added', 'Adding' + msg)

        self.finish()
Beispiel #27
0
    def run(self):  # pylint: disable=too-many-branches, too-many-statements, too-many-return-statements

        super(QueueItemAdd, self).run()

        if self.showDir:
            try:
                assert isinstance(self.showDir, six.text_type)
            except AssertionError:
                logger.log(traceback.format_exc(), logger.WARNING)
                self._finish_early()
                return

        logger.log('Starting to add show {0}'.format('by ShowDir: {0}'.format(self.showDir) if self.showDir else 'by Indexer Id: {0}'.format(self.indexer_id)))
        # make sure the Indexer IDs are valid
        try:

            lINDEXER_API_PARMS = sickbeard.indexerApi(self.indexer).api_params.copy()
            lINDEXER_API_PARMS['language'] = self.lang or sickbeard.INDEXER_DEFAULT_LANGUAGE

            logger.log('{0}: {1!r}'.format(sickbeard.indexerApi(self.indexer).name, lINDEXER_API_PARMS))

            t = sickbeard.indexerApi(self.indexer).indexer(**lINDEXER_API_PARMS)
            s = t[self.indexer_id]

            # Let's try to create the show Dir if it's not provided. This way we force the show dir to build build using the
            # Indexers provided series name
            if self.root_dir and not self.showDir:
                show_name = get_showname_from_indexer(self.indexer, self.indexer_id, self.lang)
                if not show_name:
                    logger.log('Unable to get a show {0}, can\'t add the show'.format(self.showDir))
                    self._finish_early()
                    return

                self.showDir = ek(os.path.join, self.root_dir, sanitize_filename(show_name))

                dir_exists = makeDir(self.showDir)
                if not dir_exists:
                    logger.log('Unable to create the folder {0}, can\'t add the show'.format(self.showDir))
                    self._finish_early()
                    return

                chmodAsParent(self.showDir)

            # this usually only happens if they have an NFO in their show dir which gave us a Indexer ID that has no proper english version of the show
            if getattr(s, 'seriesname', None) is None:
                # noinspection PyPep8
                error_string = 'Show in {0} has no name on {1}, probably searched with the wrong language. Delete .nfo and add manually in the correct language.'.format(
                    self.showDir, sickbeard.indexerApi(self.indexer).name)

                logger.log(error_string, logger.WARNING)
                ui.notifications.error('Unable to add show', error_string)

                self._finish_early()
                return

            # if the show has no episodes/seasons
            if not s:
                error_string = 'Show {0} is on {1} but contains no season/episode data.'.format(
                    s[b'seriesname'], sickbeard.indexerApi(self.indexer).name)

                logger.log(error_string)
                ui.notifications.error('Unable to add show', error_string)

                self._finish_early()
                return
        except Exception as error:
            error_string = 'Unable to look up the show in {0} on {1} using ID {2}, not using the NFO. Delete .nfo and try adding manually again.'.format(
                self.showDir, sickbeard.indexerApi(self.indexer).name, self.indexer_id)

            logger.log('{0}: {1}'.format(error_string, error), logger.ERROR)
            ui.notifications.error(
                'Unable to add show', error_string)

            if sickbeard.USE_TRAKT:
                trakt_id = sickbeard.indexerApi(self.indexer).config[b'trakt_id']
                trakt_api = TraktAPI(sickbeard.SSL_VERIFY, sickbeard.TRAKT_TIMEOUT)

                title = self.showDir.split('/')[-1]
                data = {
                    'shows': [
                        {
                            'title': title,
                            'ids': {}
                        }
                    ]
                }
                if trakt_id == 'tvdb_id':
                    data['shows'][0]['ids']['tvdb'] = self.indexer_id
                else:
                    data['shows'][0]['ids']['tvrage'] = self.indexer_id

                trakt_api.traktRequest('sync/watchlist/remove', data, method='POST')

            self._finish_early()
            return

        try:
            try:
                newShow = TVShow(self.indexer, self.indexer_id, self.lang)
            except MultipleShowObjectsException as error:
                # If we have the show in our list, but the location is wrong, lets fix it and refresh!
                existing_show = Show.find(sickbeard.showList, self.indexer_id)
                # noinspection PyProtectedMember
                if existing_show and not ek(os.path.isdir, existing_show._location):  # pylint: disable=protected-access
                    newShow = existing_show
                else:
                    raise error

            newShow.loadFromIndexer()

            self.show = newShow

            # set up initial values
            self.show.location = self.showDir
            self.show.subtitles = self.subtitles if self.subtitles is not None else sickbeard.SUBTITLES_DEFAULT
            self.show.subtitles_sr_metadata = self.subtitles_sr_metadata
            self.show.quality = self.quality if self.quality else sickbeard.QUALITY_DEFAULT
            self.show.season_folders = self.season_folders if self.season_folders is not None else sickbeard.SEASON_FOLDERS_DEFAULT
            self.show.anime = self.anime if self.anime is not None else sickbeard.ANIME_DEFAULT
            self.show.scene = self.scene if self.scene is not None else sickbeard.SCENE_DEFAULT
            self.show.paused = self.paused if self.paused is not None else False

            # set up default new/missing episode status
            logger.log('Setting all episodes to the specified default status: {0}' .format(self.show.default_ep_status))
            self.show.default_ep_status = self.default_status

            if self.show.anime:
                self.show.release_groups = BlackAndWhiteList(self.show.indexerid)
                if self.blacklist:
                    self.show.release_groups.set_black_keywords(self.blacklist)
                if self.whitelist:
                    self.show.release_groups.set_white_keywords(self.whitelist)

            # # be smart-ish about this
            # if self.show.genre and 'talk show' in self.show.genre.lower():
            #     self.show.air_by_date = 1
            # if self.show.genre and 'documentary' in self.show.genre.lower():
            #     self.show.air_by_date = 0
            # if self.show.classification and 'sports' in self.show.classification.lower():
            #     self.show.sports = 1

        except sickbeard.indexer_exception as error:
            error_string = 'Unable to add {0} due to an error with {1}'.format(
                self.show.name if self.show else 'show', sickbeard.indexerApi(self.indexer).name)

            logger.log('{0}: {1}'.format(error_string, error), logger.ERROR)
            ui.notifications.error('Unable to add show', error_string)

            self._finish_early()
            return

        except MultipleShowObjectsException:
            error_string = 'The show in {0} is already in your show list, skipping'.format(self.showDir)
            logger.log(error_string, logger.WARNING)
            ui.notifications.error('Show skipped', error_string)

            self._finish_early()
            return

        except Exception as error:
            logger.log('Error trying to add show: {0}'.format(error), logger.ERROR)
            logger.log(traceback.format_exc(), logger.DEBUG)
            self._finish_early()
            raise

        logger.log('Retrieving show info from IMDb', logger.DEBUG)
        try:
            self.show.loadIMDbInfo()
        except imdb_exceptions.IMDbError as error:
            logger.log(' Something wrong on IMDb api: {0}'.format(error), logger.WARNING)
        except Exception as error:
            logger.log('Error loading IMDb info: {0}'.format(error), logger.ERROR)

        try:
            self.show.saveToDB()
        except Exception as error:
            logger.log('Error saving the show to the database: {0}'.format(error), logger.ERROR)
            logger.log(traceback.format_exc(), logger.DEBUG)
            self._finish_early()
            raise

        # add it to the show list
        if not Show.find(sickbeard.showList, self.indexer_id):
            sickbeard.showList.append(self.show)

        try:
            self.show.loadEpisodesFromIndexer()
        except Exception as error:
            logger.log(
                'Error with {0}, not creating episode list: {1}'.format
                (sickbeard.indexerApi(self.show.indexer).name, error), logger.ERROR)
            logger.log(traceback.format_exc(), logger.DEBUG)

        # update internal name cache
        name_cache.buildNameCache(self.show)

        try:
            self.show.loadEpisodesFromDir()
        except Exception as error:
            logger.log('Error searching dir for episodes: {0}'.format(error), logger.ERROR)
            logger.log(traceback.format_exc(), logger.DEBUG)

        # if they set default ep status to WANTED then run the backlog to search for episodes
        # FIXME: This needs to be a backlog queue item!!!
        if self.show.default_ep_status == WANTED:
            logger.log('Launching backlog for this show since its episodes are WANTED')
            sickbeard.backlogSearchScheduler.action.searchBacklog([self.show])

        self.show.writeMetadata()
        self.show.updateMetadata()
        self.show.populateCache()

        self.show.flushEpisodes()

        if sickbeard.USE_TRAKT:
            # if there are specific episodes that need to be added by trakt
            sickbeard.traktCheckerScheduler.action.manageNewShow(self.show)
            # add show to trakt.tv library
            if sickbeard.TRAKT_SYNC:
                sickbeard.traktCheckerScheduler.action.addShowToTraktLibrary(self.show)

            if sickbeard.TRAKT_SYNC_WATCHLIST:
                logger.log('update watchlist')
                notifiers.trakt_notifier.update_watchlist(show_obj=self.show)

        # Load XEM data to DB for show
        scene_numbering.xem_refresh(self.show.indexerid, self.show.indexer, force=True)

        # check if show has XEM mapping so we can determine if searches should go by scene numbering or indexer numbering.
        if not self.scene and scene_numbering.get_xem_numbering_for_show(self.show.indexerid, self.show.indexer):
            self.show.scene = 1

        # After initial add, set to default_status_after.
        self.show.default_ep_status = self.default_status_after

        super(QueueItemAdd, self).finish()
        self.finish()
Beispiel #28
0
    def start(self):
        # Rename the main thread
        threading.currentThread().name = "MAIN"

        # initalize encoding defaults
        encodingInit()

        # do some preliminary stuff
        sickbeard.MY_FULLNAME = ek(os.path.normpath, ek(os.path.abspath, __file__))
        sickbeard.MY_NAME = ek(os.path.basename, sickbeard.MY_FULLNAME)
        sickbeard.PROG_DIR = ek(os.path.dirname, sickbeard.MY_FULLNAME)
        sickbeard.DATA_DIR = sickbeard.PROG_DIR
        sickbeard.MY_ARGS = sys.argv[1:]

        # Need console logging for SickBeard.py and SickBeard-console.exe
        self.consoleLogging = (not hasattr(sys, "frozen")) or (sickbeard.MY_NAME.lower().find('-console') > 0)

        # Do this before importing sickbeard, to prevent locked files and incorrect import
        oldtornado = ek(os.path.abspath, ek(os.path.join, ek(os.path.dirname, __file__), 'tornado'))
        if ek(os.path.isdir, oldtornado):
            ek(shutil.move, oldtornado, oldtornado + '_kill')
            ek(removetree, oldtornado + '_kill')

        try:
            opts, _ = getopt.getopt(
                    sys.argv[1:], "hqdp::",
                    ['help', 'quiet', 'nolaunch', 'daemon', 'pidfile=', 'port=', 'datadir=', 'config=', 'noresize']
            )
        except getopt.GetoptError:
            sys.exit(self.help_message())

        for o, a in opts:
            # Prints help message
            if o in ('-h', '--help'):
                sys.exit(self.help_message())

            # For now we'll just silence the logging
            if o in ('-q', '--quiet'):
                self.consoleLogging = False

            # Suppress launching web browser
            # Needed for OSes without default browser assigned
            # Prevent duplicate browser window when restarting in the app
            if o in ('--nolaunch',):
                self.noLaunch = True

            # Override default/configured port
            if o in ('-p', '--port'):
                try:
                    self.forcedPort = int(a)
                except ValueError:
                    sys.exit("Port: " + str(a) + " is not a number. Exiting.")

            # Run as a double forked daemon
            if o in ('-d', '--daemon'):
                self.runAsDaemon = True
                # When running as daemon disable consoleLogging and don't start browser
                self.consoleLogging = False
                self.noLaunch = True

                if sys.platform == 'win32' or sys.platform == 'darwin':
                    self.runAsDaemon = False

            # Write a pidfile if requested
            if o in ('--pidfile',):
                self.CREATEPID = True
                self.PIDFILE = str(a)

                # If the pidfile already exists, sickbeard may still be running, so exit
                if ek(os.path.exists, self.PIDFILE):
                    sys.exit("PID file: " + self.PIDFILE + " already exists. Exiting.")

            # Specify folder to load the config file from
            if o in ('--config',):
                sickbeard.CONFIG_FILE = ek(os.path.abspath, a)

            # Specify folder to use as the data dir
            if o in ('--datadir',):
                sickbeard.DATA_DIR = ek(os.path.abspath, a)

            # Prevent resizing of the banner/posters even if PIL is installed
            if o in ('--noresize',):
                sickbeard.NO_RESIZE = True

        # The pidfile is only useful in daemon mode, make sure we can write the file properly
        if self.CREATEPID:
            if self.runAsDaemon:
                pid_dir = ek(os.path.dirname, self.PIDFILE)
                if not ek(os.access, pid_dir, os.F_OK):
                    sys.exit("PID dir: " + pid_dir + " doesn't exist. Exiting.")
                if not ek(os.access, pid_dir, os.W_OK):
                    sys.exit("PID dir: " + pid_dir + " must be writable (write permissions). Exiting.")

            else:
                if self.consoleLogging:
                    sys.stdout.write("Not running in daemon mode. PID file creation disabled.\n")

                self.CREATEPID = False

        # If they don't specify a config file then put it in the data dir
        if not sickbeard.CONFIG_FILE:
            sickbeard.CONFIG_FILE = ek(os.path.join, sickbeard.DATA_DIR, "config.ini")

        # Make sure that we can create the data dir
        if not ek(os.access, sickbeard.DATA_DIR, os.F_OK):
            try:
                ek(os.makedirs, sickbeard.DATA_DIR, 0o744)
            except os.error:
                raise SystemExit("Unable to create datadir '" + sickbeard.DATA_DIR + "'")

        # Make sure we can write to the data dir
        if not ek(os.access, sickbeard.DATA_DIR, os.W_OK):
            raise SystemExit("Datadir must be writeable '" + sickbeard.DATA_DIR + "'")

        # Make sure we can write to the config file
        if not ek(os.access, sickbeard.CONFIG_FILE, os.W_OK):
            if ek(os.path.isfile, sickbeard.CONFIG_FILE):
                raise SystemExit("Config file '" + sickbeard.CONFIG_FILE + "' must be writeable.")
            elif not ek(os.access, ek(os.path.dirname, sickbeard.CONFIG_FILE), os.W_OK):
                raise SystemExit(
                        "Config file root dir '" + ek(os.path.dirname, sickbeard.CONFIG_FILE) + "' must be writeable.")

        ek(os.chdir, sickbeard.DATA_DIR)

        # Check if we need to perform a restore first
        restoreDir = ek(os.path.join, sickbeard.DATA_DIR, 'restore')
        if ek(os.path.exists, restoreDir):
            success = self.restoreDB(restoreDir, sickbeard.DATA_DIR)
            if self.consoleLogging:
                sys.stdout.write("Restore: restoring DB and config.ini %s!\n" % ("FAILED", "SUCCESSFUL")[success])

        # Load the config and publish it to the sickbeard package
        if self.consoleLogging and not ek(os.path.isfile, sickbeard.CONFIG_FILE):
            sys.stdout.write("Unable to find '" + sickbeard.CONFIG_FILE + "' , all settings will be default!" + "\n")

        sickbeard.CFG = ConfigObj(sickbeard.CONFIG_FILE)

        # Initialize the config and our threads
        sickbeard.initialize(consoleLogging=self.consoleLogging)

        if self.runAsDaemon:
            self.daemonize()

        # Get PID
        sickbeard.PID = os.getpid()

        # Build from the DB to start with
        self.loadShowsFromDB()

        if self.forcedPort:
            logging.info("Forcing web server to port " + str(self.forcedPort))
            self.startPort = self.forcedPort
        else:
            self.startPort = sickbeard.WEB_PORT

        if sickbeard.WEB_LOG:
            self.log_dir = sickbeard.LOG_DIR
        else:
            self.log_dir = None

        # sickbeard.WEB_HOST is available as a configuration value in various
        # places but is not configurable. It is supported here for historic reasons.
        if sickbeard.WEB_HOST and sickbeard.WEB_HOST != '0.0.0.0':
            self.webhost = sickbeard.WEB_HOST
        else:
            if sickbeard.WEB_IPV6:
                self.webhost = '::'
            else:
                self.webhost = '0.0.0.0'

        # start tornado web server
        self.webserver = SRWebServer({
            'port': int(self.startPort),
            'host': self.webhost,
            'data_root': ek(os.path.join, sickbeard.PROG_DIR, 'gui', sickbeard.GUI_NAME),
            'web_root': sickbeard.WEB_ROOT,
            'log_dir': self.log_dir,
            'username': sickbeard.WEB_USERNAME,
            'password': sickbeard.WEB_PASSWORD,
            'enable_https': sickbeard.ENABLE_HTTPS,
            'handle_reverse_proxy': sickbeard.HANDLE_REVERSE_PROXY,
            'https_cert': ek(os.path.join, sickbeard.PROG_DIR, sickbeard.HTTPS_CERT),
            'https_key': ek(os.path.join, sickbeard.PROG_DIR, sickbeard.HTTPS_KEY),
        }).start()

        # Clean up after update
        if sickbeard.GIT_NEWVER:
            toclean = ek(os.path.join, sickbeard.CACHE_DIR, 'mako')
            for root, dirs, files in ek(os.walk, toclean, topdown=False):
                for name in files:
                    ek(os.remove, ek(os.path.join, root, name))
                for name in dirs:
                    ek(os.rmdir, ek(os.path.join, root, name))
            sickbeard.GIT_NEWVER = False

        # Fire up all our threads
        logging.info("Starting SiCKRAGE BRANCH:[{}] CONFIG:[{}]".format(sickbeard.BRANCH, sickbeard.CONFIG_FILE))
        sickbeard.start()

        # Build internal name cache
        name_cache.buildNameCache()

        # Prepopulate network timezones, it isn't thread safe
        network_timezones.update_network_dict()

        # sure, why not?
        if sickbeard.USE_FAILED_DOWNLOADS:
            failed_history.trimHistory()

        # # Check for metadata indexer updates for shows (Disabled until we use api)
        # sickbeard.showUpdateScheduler.forceRun()

        # Launch browser
        if sickbeard.LAUNCH_BROWSER and not (self.noLaunch or self.runAsDaemon):
            sickbeard.launchBrowser('https' if sickbeard.ENABLE_HTTPS else 'http', self.startPort, sickbeard.WEB_ROOT)

        # main loop
        while True:
            time.sleep(1)