Exemple #1
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)

        if PY2:
            try:
                # On non-unicode builds this raises an AttributeError,
                # if encoding type is not valid it throws a LookupError
                # noinspection PyUnresolvedReferences
                sys.setdefaultencoding(sickbeard.SYS_ENCODING)
            except (BaseException, 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 (0 < sickbeard.MY_NAME.lower().find('-console'))

        # 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'
            ])
        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 (BaseException, Exception):
            stack_size = None

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

        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 '0.0.0.0' != sickbeard.WEB_HOST:
            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(options=self.web_options)
            self.webserver.start()
            # wait for server thread to be started
            self.webserver.wait_server_start()
            sickbeard.started = True
        except (BaseException, 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 100000 <= 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 100000 <= cur_db_version:
                    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 version higher then current base of test db
            if isinstance(base_v, integer_types
                          ) and max_v >= 100000 > cur_db_version > base_v:
                sickbeard.classes.loading_msg.set_msg_progress(
                    load_msg, 'Rollback')
                print(
                    'Your [%s] database version (%s) is a db version and doesn\'t match SickGear required '
                    'version (%s), downgrading to production base db' %
                    (d, cur_db_version, max_v))
                self.execute_rollback(mo, base_v, load_msg)
                cur_db_version = db.DBConnection(d).checkDBVersion()
                if 100000 <= cur_db_version:
                    print(u'Rollback to production base 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 base 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')

        # migrate the config if it needs it
        from sickbeard.config import ConfigMigrator
        migrator = ConfigMigrator(sickbeard.CFG)
        if migrator.config_version > migrator.expected_config_version:
            self.execute_rollback('ConfigFile',
                                  migrator.expected_config_version,
                                  'Downgrading config.ini')
            migrator = ConfigMigrator(sickbeard.CFG)
        migrator.migrate_config()

        # 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 not db.DBConnection().has_flag('kodi_nfo_default_removed'):
            sickbeard.metadata.kodi.remove_default_attr()

        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()

        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)
Exemple #2
0
    def run(self, force=False):

        self.amActive = True

        try:
            update_datetime = datetime.datetime.now()
            update_date = update_datetime.date()

            # refresh network timezones
            try:
                network_timezones.update_network_dict()
            except Exception:
                logger.log('network timezone update error', logger.ERROR)
                logger.log(traceback.format_exc(), logger.ERROR)

            # refresh webdl types
            try:
                properFinder.load_webdl_types()
            except (StandardError, Exception):
                logger.log('error loading webdl_types', logger.DEBUG)

            # update xem id lists
            try:
                sickbeard.scene_exceptions.get_xem_ids()
            except Exception:
                logger.log('xem id list update error', logger.ERROR)
                logger.log(traceback.format_exc(), logger.ERROR)

            # update scene exceptions
            try:
                sickbeard.scene_exceptions.retrieve_exceptions()
            except Exception:
                logger.log('scene exceptions update error', logger.ERROR)
                logger.log(traceback.format_exc(), logger.ERROR)

            # sure, why not?
            if sickbeard.USE_FAILED_DOWNLOADS:
                try:
                    failed_history.remove_old_history()
                except Exception:
                    logger.log('Failed History cleanup error', logger.ERROR)
                    logger.log(traceback.format_exc(), logger.ERROR)

            # clear the data of unused providers
            try:
                sickbeard.helpers.clear_unused_providers()
            except Exception:
                logger.log('unused provider cleanup error', logger.ERROR)
                logger.log(traceback.format_exc(), logger.ERROR)

            # cleanup image cache
            try:
                sickbeard.helpers.cleanup_cache()
            except Exception:
                logger.log('image cache cleanup error', logger.ERROR)
                logger.log(traceback.format_exc(), logger.ERROR)

            # cleanup manual search history
            sickbeard.search_queue.remove_old_fifo(sickbeard.search_queue.MANUAL_SEARCH_HISTORY)

            # add missing mapped ids
            if not sickbeard.background_mapping_task.is_alive():
                logger.log(u'Updating the Indexer mappings')
                import threading
                try:
                    sickbeard.background_mapping_task = threading.Thread(
                        name='LOAD-MAPPINGS', target=sickbeard.indexermapper.load_mapped_ids, kwargs={'update': True})
                    sickbeard.background_mapping_task.start()
                except Exception:
                    logger.log('missing mapped ids update error', logger.ERROR)
                    logger.log(traceback.format_exc(), logger.ERROR)

            logger.log(u'Doing full update on all shows')

            # clean out cache directory, remove everything > 12 hours old
            try:
                sickbeard.helpers.clearCache()
            except Exception:
                logger.log('cache dir cleanup error', logger.ERROR)
                logger.log(traceback.format_exc(), logger.ERROR)

            # select 10 'Ended' tv_shows updated more than 90 days ago
            # and all shows not updated more then 180 days ago to include in this update
            stale_should_update = []
            stale_update_date = (update_date - datetime.timedelta(days=90)).toordinal()
            stale_update_date_max = (update_date - datetime.timedelta(days=180)).toordinal()

            # last_update_date <= 90 days, sorted ASC because dates are ordinal
            my_db = db.DBConnection()
            sql_results = my_db.mass_action([
                ['SELECT indexer_id FROM tv_shows WHERE last_update_indexer <= ? AND ' +
                 'last_update_indexer >= ? ORDER BY last_update_indexer ASC LIMIT 10;',
                 [stale_update_date, stale_update_date_max]],
                ['SELECT indexer_id FROM tv_shows WHERE last_update_indexer < ?;', [stale_update_date_max]]])

            for sql_result in sql_results:
                for cur_result in sql_result:
                    stale_should_update.append(int(cur_result['indexer_id']))

            # start update process
            pi_list = []
            for curShow in sickbeard.showList:

                try:
                    # get next episode airdate
                    curShow.nextEpisode()

                    # if should_update returns True (not 'Ended') or show is selected stale 'Ended' then update,
                    # otherwise just refresh
                    if curShow.should_update(update_date=update_date) or curShow.indexerid in stale_should_update:
                        cur_queue_item = sickbeard.showQueueScheduler.action.updateShow(curShow, scheduled_update=True)
                    else:
                        logger.log(
                            u'Not updating episodes for show ' + curShow.name + ' because it\'s marked as ended and ' +
                            'last/next episode is not within the grace period.', logger.DEBUG)
                        cur_queue_item = sickbeard.showQueueScheduler.action.refreshShow(curShow, True, True, force_image_cache=True)

                    pi_list.append(cur_queue_item)

                except (exceptions.CantUpdateException, exceptions.CantRefreshException) as e:
                    logger.log(u'Automatic update failed: ' + ex(e), logger.ERROR)

            ui.ProgressIndicators.setIndicator('dailyUpdate', ui.QueueProgressIndicator('Daily Update', pi_list))

            logger.log(u'Added all shows to show queue for full update')

        finally:
            self.amActive = False
Exemple #3
0
    def run(self, force=False):

        self.amActive = True

        try:
            update_datetime = datetime.datetime.now()
            update_date = update_datetime.date()

            # refresh network timezones
            try:
                network_timezones.update_network_dict()
            except Exception:
                logger.log('network timezone update error', logger.ERROR)
                logger.log(traceback.format_exc(), logger.ERROR)

            # refresh webdl types
            try:
                properFinder.load_webdl_types()
            except (StandardError, Exception):
                logger.log('error loading webdl_types', logger.DEBUG)

            # update xem id lists
            try:
                sickbeard.scene_exceptions.get_xem_ids()
            except Exception:
                logger.log('xem id list update error', logger.ERROR)
                logger.log(traceback.format_exc(), logger.ERROR)

            # update scene exceptions
            try:
                sickbeard.scene_exceptions.retrieve_exceptions()
            except Exception:
                logger.log('scene exceptions update error', logger.ERROR)
                logger.log(traceback.format_exc(), logger.ERROR)

            # sure, why not?
            if sickbeard.USE_FAILED_DOWNLOADS:
                try:
                    failed_history.remove_old_history()
                except Exception:
                    logger.log('Failed History cleanup error', logger.ERROR)
                    logger.log(traceback.format_exc(), logger.ERROR)

            # clear the data of unused providers
            try:
                sickbeard.helpers.clear_unused_providers()
            except Exception:
                logger.log('unused provider cleanup error', logger.ERROR)
                logger.log(traceback.format_exc(), logger.ERROR)

            # cleanup image cache
            try:
                sickbeard.helpers.cleanup_cache()
            except Exception:
                logger.log('image cache cleanup error', logger.ERROR)
                logger.log(traceback.format_exc(), logger.ERROR)

            # cleanup manual search history
            sickbeard.search_queue.remove_old_fifo(sickbeard.search_queue.MANUAL_SEARCH_HISTORY)

            # add missing mapped ids
            if not sickbeard.background_mapping_task.is_alive():
                logger.log(u'Updating the Indexer mappings')
                import threading
                try:
                    sickbeard.background_mapping_task = threading.Thread(
                        name='LOAD-MAPPINGS', target=sickbeard.indexermapper.load_mapped_ids, kwargs={'update': True})
                    sickbeard.background_mapping_task.start()
                except Exception:
                    logger.log('missing mapped ids update error', logger.ERROR)
                    logger.log(traceback.format_exc(), logger.ERROR)

            logger.log(u'Doing full update on all shows')

            # clean out cache directory, remove everything > 12 hours old
            try:
                sickbeard.helpers.clearCache()
            except Exception:
                logger.log('cache dir cleanup error', logger.ERROR)
                logger.log(traceback.format_exc(), logger.ERROR)

            # select 10 'Ended' tv_shows updated more than 90 days ago
            # and all shows not updated more then 180 days ago to include in this update
            stale_should_update = []
            stale_update_date = (update_date - datetime.timedelta(days=90)).toordinal()
            stale_update_date_max = (update_date - datetime.timedelta(days=180)).toordinal()

            # last_update_date <= 90 days, sorted ASC because dates are ordinal
            my_db = db.DBConnection()
            sql_results = my_db.mass_action([
                ['SELECT indexer_id FROM tv_shows WHERE last_update_indexer <= ? AND ' +
                 'last_update_indexer >= ? ORDER BY last_update_indexer ASC LIMIT 10;',
                 [stale_update_date, stale_update_date_max]],
                ['SELECT indexer_id FROM tv_shows WHERE last_update_indexer < ?;', [stale_update_date_max]]])

            for sql_result in sql_results:
                for cur_result in sql_result:
                    stale_should_update.append(int(cur_result['indexer_id']))

            # start update process
            pi_list = []
            for curShow in sickbeard.showList:

                try:
                    # get next episode airdate
                    curShow.nextEpisode()

                    # if should_update returns True (not 'Ended') or show is selected stale 'Ended' then update,
                    # otherwise just refresh
                    if curShow.should_update(update_date=update_date) or curShow.indexerid in stale_should_update:
                        cur_queue_item = sickbeard.showQueueScheduler.action.updateShow(curShow, scheduled_update=True)
                    else:
                        logger.log(
                            u'Not updating episodes for show ' + curShow.name + ' because it\'s marked as ended and ' +
                            'last/next episode is not within the grace period.', logger.DEBUG)
                        cur_queue_item = sickbeard.showQueueScheduler.action.refreshShow(curShow, True, True)

                    pi_list.append(cur_queue_item)

                except (exceptions.CantUpdateException, exceptions.CantRefreshException) as e:
                    logger.log(u'Automatic update failed: ' + ex(e), logger.ERROR)

            ui.ProgressIndicators.setIndicator('dailyUpdate', ui.QueueProgressIndicator('Daily Update', pi_list))

            logger.log(u'Added all shows to show queue for full update')

        finally:
            self.amActive = False
Exemple #4
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)