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