Example #1
0
    def start(self):
        self.started = True

        # thread name
        threading.currentThread().setName('CORE')

        # patch modules with encoding kludge
        patch_modules()

        # init core classes
        self.notifier_providers = NotifierProviders()
        self.metadata_providers = MetadataProviders()
        self.search_providers = SearchProviders()
        self.log = Logger()
        self.config = Config()
        self.alerts = Notifications()
        self.main_db = MainDB()
        self.cache_db = CacheDB()
        self.scheduler = TornadoScheduler()
        self.wserver = WebServer()
        self.name_cache = NameCache()
        self.show_queue = ShowQueue()
        self.search_queue = SearchQueue()
        self.postprocessor_queue = PostProcessorQueue()
        self.version_updater = VersionUpdater()
        self.show_updater = ShowUpdater()
        self.daily_searcher = DailySearcher()
        self.failed_snatch_searcher = FailedSnatchSearcher()
        self.backlog_searcher = BacklogSearcher()
        self.proper_searcher = ProperSearcher()
        self.trakt_searcher = TraktSearcher()
        self.subtitle_searcher = SubtitleSearcher()
        self.auto_postprocessor = AutoPostProcessor()
        self.upnp_client = UPNPClient()
        self.quicksearch_cache = QuicksearchCache()

        # setup oidc client
        realm = KeycloakRealm(server_url='https://auth.sickrage.ca',
                              realm_name='sickrage')
        self.oidc_client = realm.open_id_connect(
            client_id='sickrage-app',
            client_secret='5d4710b2-ca70-4d39-b5a3-0705e2c5e703')

        # Check if we need to perform a restore first
        if os.path.exists(
                os.path.abspath(os.path.join(self.data_dir, 'restore'))):
            success = restoreSR(
                os.path.abspath(os.path.join(self.data_dir, 'restore')),
                self.data_dir)
            print("Restoring SiCKRAGE backup: %s!\n" %
                  ("FAILED", "SUCCESSFUL")[success])
            if success:
                shutil.rmtree(os.path.abspath(
                    os.path.join(self.data_dir, 'restore')),
                              ignore_errors=True)

        # migrate old database file names to new ones
        if os.path.isfile(
                os.path.abspath(os.path.join(self.data_dir, 'sickbeard.db'))):
            if os.path.isfile(os.path.join(self.data_dir, 'sickrage.db')):
                helpers.moveFile(
                    os.path.join(self.data_dir, 'sickrage.db'),
                    os.path.join(
                        self.data_dir, '{}.bak-{}'.format(
                            'sickrage.db',
                            datetime.datetime.now().strftime(
                                '%Y%m%d_%H%M%S'))))

            helpers.moveFile(
                os.path.abspath(os.path.join(self.data_dir, 'sickbeard.db')),
                os.path.abspath(os.path.join(self.data_dir, 'sickrage.db')))

        # load config
        self.config.load()

        # set language
        self.config.change_gui_lang(self.config.gui_lang)

        # set socket timeout
        socket.setdefaulttimeout(self.config.socket_timeout)

        # setup logger settings
        self.log.logSize = self.config.log_size
        self.log.logNr = self.config.log_nr
        self.log.logFile = os.path.join(self.data_dir, 'logs', 'sickrage.log')
        self.log.debugLogging = self.config.debug
        self.log.consoleLogging = not self.quite

        # start logger
        self.log.start()

        # user agent
        if self.config.random_user_agent:
            self.user_agent = UserAgent().random

        urlparse.uses_netloc.append('scgi')
        urllib.FancyURLopener.version = self.user_agent

        # set torrent client web url
        torrent_webui_url(True)

        # Check available space
        try:
            total_space, available_space = getFreeSpace(self.data_dir)
            if available_space < 100:
                self.log.error(
                    'Shutting down as SiCKRAGE needs some space to work. You\'ll get corrupted data '
                    'otherwise. Only %sMB left', available_space)
                return
        except Exception:
            self.log.error('Failed getting disk space: %s',
                           traceback.format_exc())

        # perform database startup actions
        for db in [self.main_db, self.cache_db]:
            # initialize database
            db.initialize()

            # check integrity of database
            db.check_integrity()

            # migrate database
            db.migrate()

            # misc database cleanups
            db.cleanup()

            # upgrade database
            db.upgrade()

        # compact main database
        if self.config.last_db_compact < time.time() - 604800:  # 7 days
            self.main_db.compact()
            self.config.last_db_compact = int(time.time())

        # load name cache
        self.name_cache.load()

        # load data for shows from database
        self.load_shows()

        if self.config.default_page not in ('schedule', 'history', 'IRC'):
            self.config.default_page = 'home'

        # cleanup cache folder
        for folder in ['mako', 'sessions', 'indexers']:
            try:
                shutil.rmtree(os.path.join(sickrage.app.cache_dir, folder),
                              ignore_errors=True)
            except Exception:
                continue

        # init anidb connection
        if self.config.use_anidb:

            def anidb_logger(msg):
                return self.log.debug("AniDB: {} ".format(msg))

            try:
                self.adba_connection = adba.Connection(keepAlive=True,
                                                       log=anidb_logger)
                self.adba_connection.auth(self.config.anidb_username,
                                          self.config.anidb_password)
            except Exception as e:
                self.log.warning("AniDB exception msg: %r " % repr(e))

        if self.config.web_port < 21 or self.config.web_port > 65535:
            self.config.web_port = 8081

        if not self.config.web_cookie_secret:
            self.config.web_cookie_secret = generate_secret()

        # attempt to help prevent users from breaking links by using a bad url
        if not self.config.anon_redirect.endswith('?'):
            self.config.anon_redirect = ''

        if not re.match(r'\d+\|[^|]+(?:\|[^|]+)*', self.config.root_dirs):
            self.config.root_dirs = ''

        self.config.naming_force_folders = check_force_season_folders()

        if self.config.nzb_method not in ('blackhole', 'sabnzbd', 'nzbget'):
            self.config.nzb_method = 'blackhole'

        if self.config.torrent_method not in ('blackhole', 'utorrent',
                                              'transmission', 'deluge',
                                              'deluged', 'download_station',
                                              'rtorrent', 'qbittorrent',
                                              'mlnet', 'putio'):
            self.config.torrent_method = 'blackhole'

        if self.config.autopostprocessor_freq < self.config.min_autopostprocessor_freq:
            self.config.autopostprocessor_freq = self.config.min_autopostprocessor_freq
        if self.config.daily_searcher_freq < self.config.min_daily_searcher_freq:
            self.config.daily_searcher_freq = self.config.min_daily_searcher_freq
        self.config.min_backlog_searcher_freq = get_backlog_cycle_time()
        if self.config.backlog_searcher_freq < self.config.min_backlog_searcher_freq:
            self.config.backlog_searcher_freq = self.config.min_backlog_searcher_freq
        if self.config.version_updater_freq < self.config.min_version_updater_freq:
            self.config.version_updater_freq = self.config.min_version_updater_freq
        if self.config.subtitle_searcher_freq < self.config.min_subtitle_searcher_freq:
            self.config.subtitle_searcher_freq = self.config.min_subtitle_searcher_freq
        if self.config.failed_snatch_age < self.config.min_failed_snatch_age:
            self.config.failed_snatch_age = self.config.min_failed_snatch_age
        if self.config.proper_searcher_interval not in ('15m', '45m', '90m',
                                                        '4h', 'daily'):
            self.config.proper_searcher_interval = 'daily'
        if self.config.showupdate_hour < 0 or self.config.showupdate_hour > 23:
            self.config.showupdate_hour = 0
        if self.config.subtitles_languages[0] == '':
            self.config.subtitles_languages = []

        # add version checker job
        self.scheduler.add_job(
            self.version_updater.run,
            IntervalTrigger(hours=self.config.version_updater_freq),
            name=self.version_updater.name,
            id=self.version_updater.name)

        # add network timezones updater job
        self.scheduler.add_job(update_network_dict,
                               IntervalTrigger(days=1),
                               name="TZUPDATER",
                               id="TZUPDATER")

        # add show updater job
        self.scheduler.add_job(self.show_updater.run,
                               IntervalTrigger(
                                   days=1,
                                   start_date=datetime.datetime.now().replace(
                                       hour=self.config.showupdate_hour)),
                               name=self.show_updater.name,
                               id=self.show_updater.name)

        # add daily search job
        self.scheduler.add_job(
            self.daily_searcher.run,
            IntervalTrigger(minutes=self.config.daily_searcher_freq,
                            start_date=datetime.datetime.now() +
                            datetime.timedelta(minutes=4)),
            name=self.daily_searcher.name,
            id=self.daily_searcher.name)

        # add failed snatch search job
        self.scheduler.add_job(
            self.failed_snatch_searcher.run,
            IntervalTrigger(hours=1,
                            start_date=datetime.datetime.now() +
                            datetime.timedelta(minutes=4)),
            name=self.failed_snatch_searcher.name,
            id=self.failed_snatch_searcher.name)

        # add backlog search job
        self.scheduler.add_job(
            self.backlog_searcher.run,
            IntervalTrigger(minutes=self.config.backlog_searcher_freq,
                            start_date=datetime.datetime.now() +
                            datetime.timedelta(minutes=30)),
            name=self.backlog_searcher.name,
            id=self.backlog_searcher.name)

        # add auto-postprocessing job
        self.scheduler.add_job(
            self.auto_postprocessor.run,
            IntervalTrigger(minutes=self.config.autopostprocessor_freq),
            name=self.auto_postprocessor.name,
            id=self.auto_postprocessor.name)

        # add find proper job
        self.scheduler.add_job(
            self.proper_searcher.run,
            IntervalTrigger(minutes={
                '15m': 15,
                '45m': 45,
                '90m': 90,
                '4h': 4 * 60,
                'daily': 24 * 60
            }[self.config.proper_searcher_interval]),
            name=self.proper_searcher.name,
            id=self.proper_searcher.name)

        # add trakt.tv checker job
        self.scheduler.add_job(self.trakt_searcher.run,
                               IntervalTrigger(hours=1),
                               name=self.trakt_searcher.name,
                               id=self.trakt_searcher.name)

        # add subtitles finder job
        self.scheduler.add_job(
            self.subtitle_searcher.run,
            IntervalTrigger(hours=self.config.subtitle_searcher_freq),
            name=self.subtitle_searcher.name,
            id=self.subtitle_searcher.name)

        # add upnp client job
        self.scheduler.add_job(
            self.upnp_client.run,
            IntervalTrigger(seconds=self.upnp_client._nat_portmap_lifetime),
            name=self.upnp_client.name,
            id=self.upnp_client.name)

        # start scheduler service
        self.scheduler.start()

        # start queue's
        self.search_queue.start()
        self.show_queue.start()
        self.postprocessor_queue.start()

        # start webserver
        self.wserver.start()

        # start ioloop
        self.io_loop.start()
Example #2
0
 class General(base):
     __tablename__ = 'general'
     id = Column(Integer, primary_key=True, autoincrement=True)
     server_id = Column(Text, default='')
     enable_sickrage_api = Column(Boolean, default=True)
     log_size = Column(Integer, default=1048576)
     calendar_unprotected = Column(Boolean, default=False)
     https_key = Column(Text, default='server.key')
     https_cert = Column(Text, default='server.crt')
     allow_high_priority = Column(Boolean, default=False)
     anon_redirect = Column(Text, default='https://anonym.to/?')
     series_provider_timeout = Column(Integer, default=20)
     web_use_gzip = Column(Boolean, default=True)
     daily_searcher_freq = Column(Integer, default=40)
     ignore_words = Column(Text, default=','.join(['german', 'french', 'core2hd', 'dutch', 'swedish', 'reenc', 'MrLss']))
     api_v1_key = Column(Text, default=generate_api_key())
     sso_api_key = Column(Text, default='')
     sso_auth_enabled = Column(Boolean, default=True)
     local_auth_enabled = Column(Boolean, default=False)
     ip_whitelist_enabled = Column(Boolean, default=False)
     ip_whitelist_localhost_enabled = Column(Boolean, default=False)
     ip_whitelist = Column(Text, default='')
     proper_searcher_interval = Column(Enum(CheckPropersInterval), default=CheckPropersInterval.DAILY)
     nzb_method = Column(Enum(NzbMethod), default=NzbMethod.BLACKHOLE)
     web_cookie_secret = Column(Text, default=generate_secret())
     ssl_verify = Column(Boolean, default=False)
     enable_upnp = Column(Boolean, default=False)
     version_notify = Column(Boolean, default=False)
     web_root = Column(Text, default='')
     web_log = Column(Text, default='')
     add_shows_wo_dir = Column(Boolean, default=False)
     debug = Column(Boolean, default=False)
     series_provider_default = Column(Enum(SeriesProviderID), default=SeriesProviderID.THETVDB)
     use_torrents = Column(Boolean, default=True)
     display_all_seasons = Column(Boolean, default=True)
     usenet_retention = Column(Integer, default=500)
     download_propers = Column(Boolean, default=True)
     pip3_path = Column(Text, default='pip3')
     del_rar_contents = Column(Boolean, default=False)
     process_method = Column(Enum(ProcessMethod), default=ProcessMethod.COPY)
     file_timestamp_timezone = Column(Enum(FileTimestampTimezone), default=FileTimestampTimezone.NETWORK)
     auto_update = Column(Boolean, default=True)
     tv_download_dir = Column(Text, default='')
     naming_custom_abd = Column(Boolean, default=False)
     scene_default = Column(Boolean, default=False)
     skip_downloaded_default = Column(Boolean, default=False)
     add_show_year_default = Column(Boolean, default=False)
     naming_sports_pattern = Column(Text, default='%SN - %A-D - %EN')
     create_missing_show_dirs = Column(Boolean, default=False)
     trash_rotate_logs = Column(Boolean, default=False)
     airdate_episodes = Column(Boolean, default=False)
     notify_on_update = Column(Boolean, default=True)
     backup_on_update = Column(Boolean, default=True)
     backlog_days = Column(Integer, default=7)
     root_dirs = Column(Text, default='')
     naming_pattern = Column(Text, default='Season %0S/%SN - S%0SE%0E - %EN')
     sort_article = Column(Boolean, default=False)
     handle_reverse_proxy = Column(Boolean, default=False)
     postpone_if_sync_files = Column(Boolean, default=True)
     cpu_preset = Column(Enum(CpuPreset), default=CpuPreset.NORMAL)
     nfo_rename = Column(Boolean, default=True)
     naming_anime_multi_ep = Column(Enum(MultiEpNaming), default=MultiEpNaming.REPEAT)
     use_nzbs = Column(Boolean, default=False)
     web_ipv6 = Column(Boolean, default=False)
     anime_default = Column(Boolean, default=False)
     default_page = Column(Enum(DefaultHomePage), default=DefaultHomePage.HOME)
     version_updater_freq = Column(Integer, default=1)
     download_url = Column(Text, default='')
     show_update_hour = Column(Integer, default=3)
     enable_rss_cache = Column(Boolean, default=True)
     torrent_file_to_magnet = Column(Boolean, default=False)
     torrent_magnet_to_file = Column(Boolean, default=True)
     download_unverified_magnet_link = Column(Boolean, default=False)
     status_default = Column(Enum(EpisodeStatus), default=EpisodeStatus.SKIPPED)
     naming_anime = Column(Integer, default=3)
     naming_custom_sports = Column(Boolean, default=False)
     naming_custom_anime = Column(Boolean, default=False)
     naming_anime_pattern = Column(Text, default='Season %0S/%SN - S%0SE%0E - %EN')
     randomize_providers = Column(Boolean, default=False)
     process_automatically = Column(Boolean, default=False)
     git_path = Column(Text, default='git')
     sync_files = Column(Text, default=','.join(['!sync', 'lftp-pget-status', 'part', 'bts', '!qb']))
     web_port = Column(Integer, default=8081)
     web_external_port = Column(Integer, default=random.randint(49152, 65536))
     launch_browser = Column(Boolean, default=False)
     unpack = Column(Boolean, default=False)
     unpack_dir = Column(Text, default='')
     delete_non_associated_files = Column(Boolean, default=True)
     move_associated_files = Column(Boolean, default=False)
     naming_multi_ep = Column(Enum(MultiEpNaming), default=MultiEpNaming.REPEAT)
     random_user_agent = Column(Boolean, default=False)
     torrent_method = Column(Enum(TorrentMethod), default=TorrentMethod.BLACKHOLE)
     trash_remove_show = Column(Boolean, default=False)
     enable_https = Column(Boolean, default=False)
     no_delete = Column(Boolean, default=False)
     naming_abd_pattern = Column(Text, default='%SN - %A.D - %EN')
     socket_timeout = Column(Integer, default=30)
     proxy_setting = Column(Text, default='')
     backlog_searcher_freq = Column(Integer, default=1440)
     subtitle_searcher_freq = Column(Integer, default=1)
     auto_postprocessor_freq = Column(Integer, default=10)
     notify_on_login = Column(Boolean, default=False)
     rename_episodes = Column(Boolean, default=True)
     quality_default = Column(IntFlag(Qualities), default=Qualities.SD)
     extra_scripts = Column(Text, default='')
     flatten_folders_default = Column(Boolean, default=False)
     series_provider_default_language = Column(Text, default='eng')
     show_update_stale = Column(Boolean, default=True)
     ep_default_deleted_status = Column(Enum(EpisodeStatus), default=EpisodeStatus.ARCHIVED)
     no_restart = Column(Boolean, default=False)
     allowed_video_file_exts = Column(Text, default=','.join(['avi', 'mkv', 'mpg', 'mpeg', 'wmv', 'ogm', 'mp4', 'iso', 'img', 'divx', 'm2ts', 'm4v', 'ts',
                                                              'flv', 'f4v', 'mov', 'rmvb', 'vob', 'dvr-ms', 'wtv', 'ogv', '3gp', 'webm', 'tp']))
     require_words = Column(Text, default='')
     naming_strip_year = Column(Boolean, default=False)
     proxy_series_providers = Column(Boolean, default=True)
     log_nr = Column(Integer, default=5)
     git_reset = Column(Boolean, default=True)
     search_format_default = Column(Enum(SearchFormat), default=SearchFormat.STANDARD)
     skip_removed_files = Column(Boolean, default=False)
     status_default_after = Column(Enum(EpisodeStatus), default=EpisodeStatus.WANTED)
     ignored_subs_list = Column(Text, default=','.join(['dk', 'fin', 'heb', 'kor', 'nor', 'nordic', 'pl', 'swe']))
     calendar_icons = Column(Boolean, default=False)
     keep_processed_dir = Column(Boolean, default=True)
     processor_follow_symlinks = Column(Boolean, default=False)
     allowed_extensions = Column(Text, default=','.join(['srt', 'nfo', 'srr', 'sfv']))
     view_changelog = Column(Boolean, default=False)
     strip_special_file_bits = Column(Boolean, default=True)
     max_queue_workers = Column(Integer, default=5)
Example #3
0
    def start(self):
        self.started = True
        self.io_loop = IOLoop.current()

        # thread name
        threading.currentThread().setName('CORE')

        # patch modules with encoding kludge
        patch_modules()

        # init core classes
        self.notifier_providers = NotifierProviders()
        self.metadata_providers = MetadataProviders()
        self.search_providers = SearchProviders()
        self.log = Logger()
        self.config = Config()
        self.alerts = Notifications()
        self.main_db = MainDB()
        self.cache_db = CacheDB()
        self.scheduler = TornadoScheduler()
        self.wserver = WebServer()
        self.name_cache = NameCache()
        self.show_queue = ShowQueue()
        self.search_queue = SearchQueue()
        self.postprocessor_queue = PostProcessorQueue()
        self.event_queue = EventQueue()
        self.version_updater = VersionUpdater()
        self.show_updater = ShowUpdater()
        self.tz_updater = TimeZoneUpdater()
        self.rsscache_updater = RSSCacheUpdater()
        self.daily_searcher = DailySearcher()
        self.failed_snatch_searcher = FailedSnatchSearcher()
        self.backlog_searcher = BacklogSearcher()
        self.proper_searcher = ProperSearcher()
        self.trakt_searcher = TraktSearcher()
        self.subtitle_searcher = SubtitleSearcher()
        self.auto_postprocessor = AutoPostProcessor()
        self.upnp_client = UPNPClient()
        self.quicksearch_cache = QuicksearchCache()

        # setup oidc client
        realm = KeycloakRealm(server_url='https://auth.sickrage.ca', realm_name='sickrage')
        self.oidc_client = realm.open_id_connect(client_id='sickrage-app',
                                                 client_secret='5d4710b2-ca70-4d39-b5a3-0705e2c5e703')

        # Check if we need to perform a restore first
        if os.path.exists(os.path.abspath(os.path.join(self.data_dir, 'restore'))):
            success = restoreSR(os.path.abspath(os.path.join(self.data_dir, 'restore')), self.data_dir)
            self.log.info("Restoring SiCKRAGE backup: {}!".format(("FAILED", "SUCCESSFUL")[success]))
            if success:
                shutil.rmtree(os.path.abspath(os.path.join(self.data_dir, 'restore')), ignore_errors=True)

        # migrate old database file names to new ones
        if os.path.isfile(os.path.abspath(os.path.join(self.data_dir, 'sickbeard.db'))):
            if os.path.isfile(os.path.join(self.data_dir, 'sickrage.db')):
                helpers.move_file(os.path.join(self.data_dir, 'sickrage.db'),
                                  os.path.join(self.data_dir, '{}.bak-{}'
                                               .format('sickrage.db',
                                                       datetime.datetime.now().strftime(
                                                           '%Y%m%d_%H%M%S'))))

            helpers.move_file(os.path.abspath(os.path.join(self.data_dir, 'sickbeard.db')),
                              os.path.abspath(os.path.join(self.data_dir, 'sickrage.db')))

        # load config
        self.config.load()

        # set language
        self.config.change_gui_lang(self.config.gui_lang)

        # set socket timeout
        socket.setdefaulttimeout(self.config.socket_timeout)

        # setup logger settings
        self.log.logSize = self.config.log_size
        self.log.logNr = self.config.log_nr
        self.log.logFile = os.path.join(self.data_dir, 'logs', 'sickrage.log')
        self.log.debugLogging = self.config.debug
        self.log.consoleLogging = not self.quiet

        # start logger
        self.log.start()

        # user agent
        if self.config.random_user_agent:
            self.user_agent = UserAgent().random

        urlparse.uses_netloc.append('scgi')
        urllib.FancyURLopener.version = self.user_agent

        # set torrent client web url
        torrent_webui_url(True)

        # Check available space
        try:
            total_space, available_space = getFreeSpace(self.data_dir)
            if available_space < 100:
                self.log.error('Shutting down as SiCKRAGE needs some space to work. You\'ll get corrupted data '
                               'otherwise. Only %sMB left', available_space)
                return
        except Exception:
            self.log.error('Failed getting disk space: %s', traceback.format_exc())

        # perform database startup actions
        for db in [self.main_db, self.cache_db]:
            # initialize database
            db.initialize()

            # check integrity of database
            db.check_integrity()

            # migrate database
            db.migrate()

            # misc database cleanups
            db.cleanup()

            # upgrade database
            db.upgrade()

        # compact main database
        if self.config.last_db_compact < time.time() - 604800:  # 7 days
            self.main_db.compact()
            self.config.last_db_compact = int(time.time())

        # load name cache
        self.name_cache.load()

        # load data for shows from database
        self.load_shows()

        if self.config.default_page not in ('schedule', 'history', 'IRC'):
            self.config.default_page = 'home'

        # cleanup cache folder
        for folder in ['mako', 'sessions', 'indexers']:
            try:
                shutil.rmtree(os.path.join(sickrage.app.cache_dir, folder), ignore_errors=True)
            except Exception:
                continue

        if self.config.web_port < 21 or self.config.web_port > 65535:
            self.config.web_port = 8081

        if not self.config.web_cookie_secret:
            self.config.web_cookie_secret = generate_secret()

        # attempt to help prevent users from breaking links by using a bad url
        if not self.config.anon_redirect.endswith('?'):
            self.config.anon_redirect = ''

        if not re.match(r'\d+\|[^|]+(?:\|[^|]+)*', self.config.root_dirs):
            self.config.root_dirs = ''

        self.config.naming_force_folders = check_force_season_folders()

        if self.config.nzb_method not in ('blackhole', 'sabnzbd', 'nzbget'):
            self.config.nzb_method = 'blackhole'

        if self.config.torrent_method not in ('blackhole', 'utorrent', 'transmission', 'deluge', 'deluged',
                                              'download_station', 'rtorrent', 'qbittorrent', 'mlnet', 'putio'):
            self.config.torrent_method = 'blackhole'

        if self.config.autopostprocessor_freq < self.config.min_autopostprocessor_freq:
            self.config.autopostprocessor_freq = self.config.min_autopostprocessor_freq
        if self.config.daily_searcher_freq < self.config.min_daily_searcher_freq:
            self.config.daily_searcher_freq = self.config.min_daily_searcher_freq
        if self.config.backlog_searcher_freq < self.config.min_backlog_searcher_freq:
            self.config.backlog_searcher_freq = self.config.min_backlog_searcher_freq
        if self.config.version_updater_freq < self.config.min_version_updater_freq:
            self.config.version_updater_freq = self.config.min_version_updater_freq
        if self.config.subtitle_searcher_freq < self.config.min_subtitle_searcher_freq:
            self.config.subtitle_searcher_freq = self.config.min_subtitle_searcher_freq
        if self.config.failed_snatch_age < self.config.min_failed_snatch_age:
            self.config.failed_snatch_age = self.config.min_failed_snatch_age
        if self.config.proper_searcher_interval not in ('15m', '45m', '90m', '4h', 'daily'):
            self.config.proper_searcher_interval = 'daily'
        if self.config.showupdate_hour < 0 or self.config.showupdate_hour > 23:
            self.config.showupdate_hour = 0

        # add version checker job
        self.scheduler.add_job(
            self.version_updater.run,
            IntervalTrigger(
                hours=self.config.version_updater_freq,
            ),
            name=self.version_updater.name,
            id=self.version_updater.name
        )

        # add network timezones updater job
        self.scheduler.add_job(
            self.tz_updater.run,
            IntervalTrigger(
                days=1,
            ),
            name=self.tz_updater.name,
            id=self.tz_updater.name
        )

        # add show updater job
        self.scheduler.add_job(
            self.show_updater.run,
            IntervalTrigger(
                days=1,
                start_date=datetime.datetime.now().replace(hour=self.config.showupdate_hour)
            ),
            name=self.show_updater.name,
            id=self.show_updater.name
        )

        # add rss cache updater job
        self.scheduler.add_job(
            self.rsscache_updater.run,
            IntervalTrigger(
                minutes=15,
            ),
            name=self.rsscache_updater.name,
            id=self.rsscache_updater.name
        )

        # add daily search job
        self.scheduler.add_job(
            self.daily_searcher.run,
            IntervalTrigger(
                minutes=self.config.daily_searcher_freq,
                start_date=datetime.datetime.now() + datetime.timedelta(minutes=4)
            ),
            name=self.daily_searcher.name,
            id=self.daily_searcher.name
        )

        # add failed snatch search job
        self.scheduler.add_job(
            self.failed_snatch_searcher.run,
            IntervalTrigger(
                hours=1,
                start_date=datetime.datetime.now() + datetime.timedelta(minutes=4)
            ),
            name=self.failed_snatch_searcher.name,
            id=self.failed_snatch_searcher.name
        )

        # add backlog search job
        self.scheduler.add_job(
            self.backlog_searcher.run,
            IntervalTrigger(
                minutes=self.config.backlog_searcher_freq,
                start_date=datetime.datetime.now() + datetime.timedelta(minutes=30)
            ),
            name=self.backlog_searcher.name,
            id=self.backlog_searcher.name
        )

        # add auto-postprocessing job
        self.scheduler.add_job(
            self.auto_postprocessor.run,
            IntervalTrigger(
                minutes=self.config.autopostprocessor_freq
            ),
            name=self.auto_postprocessor.name,
            id=self.auto_postprocessor.name
        )

        # add find proper job
        self.scheduler.add_job(
            self.proper_searcher.run,
            IntervalTrigger(
                minutes={
                    '15m': 15,
                    '45m': 45,
                    '90m': 90,
                    '4h': 4 * 60,
                    'daily': 24 * 60
                }[self.config.proper_searcher_interval]
            ),
            name=self.proper_searcher.name,
            id=self.proper_searcher.name
        )

        # add trakt.tv checker job
        self.scheduler.add_job(
            self.trakt_searcher.run,
            IntervalTrigger(
                hours=1
            ),
            name=self.trakt_searcher.name,
            id=self.trakt_searcher.name
        )

        # add subtitles finder job
        self.scheduler.add_job(
            self.subtitle_searcher.run,
            IntervalTrigger(
                hours=self.config.subtitle_searcher_freq
            ),
            name=self.subtitle_searcher.name,
            id=self.subtitle_searcher.name
        )

        # add upnp client job
        self.scheduler.add_job(
            self.upnp_client.run,
            IntervalTrigger(
                seconds=self.upnp_client._nat_portmap_lifetime
            ),
            name=self.upnp_client.name,
            id=self.upnp_client.name
        )

        # add namecache update job
        self.scheduler.add_job(
            self.name_cache.build_all,
            IntervalTrigger(
                days=1,
            ),
            name=self.name_cache.name,
            id=self.name_cache.name
        )

        # start scheduler service
        self.scheduler.start()

        # start queue's
        self.search_queue.start()
        self.show_queue.start()
        self.postprocessor_queue.start()
        self.event_queue.start()

        # fire off startup events
        self.event_queue.fire_event(self.name_cache.build_all)
        self.event_queue.fire_event(self.version_updater.run)
        self.event_queue.fire_event(self.tz_updater.run)

        # start webserver
        self.wserver.start()

        # launch browser window
        if all([not sickrage.app.no_launch, sickrage.app.config.launch_browser]):
            self.event_queue.fire_event(lambda: launch_browser(('http', 'https')[sickrage.app.config.enable_https],
                                                               sickrage.app.config.web_host,
                                                               sickrage.app.config.web_port))

        # start ioloop
        self.io_loop.start()
Example #4
0
    def start(self):
        self.started = True
        self.io_loop = IOLoop.current()

        # thread name
        threading.currentThread().setName('CORE')

        # init core classes
        self.main_db = MainDB(self.db_type, self.db_prefix, self.db_host, self.db_port, self.db_username, self.db_password)
        self.cache_db = CacheDB(self.db_type, self.db_prefix, self.db_host, self.db_port, self.db_username, self.db_password)
        self.notifier_providers = NotifierProviders()
        self.metadata_providers = MetadataProviders()
        self.search_providers = SearchProviders()
        self.log = Logger()
        self.config = Config()
        self.alerts = Notifications()
        self.scheduler = TornadoScheduler({'apscheduler.timezone': 'UTC'})
        self.wserver = WebServer()
        self.name_cache = NameCache()
        self.show_queue = ShowQueue()
        self.search_queue = SearchQueue()
        self.postprocessor_queue = PostProcessorQueue()
        self.version_updater = VersionUpdater()
        self.show_updater = ShowUpdater()
        self.tz_updater = TimeZoneUpdater()
        self.rsscache_updater = RSSCacheUpdater()
        self.daily_searcher = DailySearcher()
        self.failed_snatch_searcher = FailedSnatchSearcher()
        self.backlog_searcher = BacklogSearcher()
        self.proper_searcher = ProperSearcher()
        self.trakt_searcher = TraktSearcher()
        self.subtitle_searcher = SubtitleSearcher()
        self.auto_postprocessor = AutoPostProcessor()
        self.upnp_client = UPNPClient()
        self.quicksearch_cache = QuicksearchCache()

        # setup oidc client
        realm = KeycloakRealm(server_url='https://auth.sickrage.ca', realm_name='sickrage')
        self.oidc_client = realm.open_id_connect(client_id=self.oidc_client_id, client_secret=self.oidc_client_secret)

        # Check if we need to perform a restore first
        if os.path.exists(os.path.abspath(os.path.join(self.data_dir, 'restore'))):
            success = restore_app_data(os.path.abspath(os.path.join(self.data_dir, 'restore')), self.data_dir)
            self.log.info("Restoring SiCKRAGE backup: %s!" % ("FAILED", "SUCCESSFUL")[success])
            if success:
                shutil.rmtree(os.path.abspath(os.path.join(self.data_dir, 'restore')), ignore_errors=True)

        # migrate old database file names to new ones
        if os.path.isfile(os.path.abspath(os.path.join(self.data_dir, 'sickbeard.db'))):
            if os.path.isfile(os.path.join(self.data_dir, 'sickrage.db')):
                helpers.move_file(os.path.join(self.data_dir, 'sickrage.db'),
                                  os.path.join(self.data_dir, '{}.bak-{}'
                                               .format('sickrage.db',
                                                       datetime.datetime.now().strftime(
                                                           '%Y%m%d_%H%M%S'))))

            helpers.move_file(os.path.abspath(os.path.join(self.data_dir, 'sickbeard.db')),
                              os.path.abspath(os.path.join(self.data_dir, 'sickrage.db')))

        # init encryption public and private keys
        encryption.initialize()

        # load config
        self.config.load()

        # set language
        self.config.change_gui_lang(self.config.gui_lang)

        # set socket timeout
        socket.setdefaulttimeout(self.config.socket_timeout)

        # setup logger settings
        self.log.logSize = self.config.log_size
        self.log.logNr = self.config.log_nr
        self.log.logFile = os.path.join(self.data_dir, 'logs', 'sickrage.log')
        self.log.debugLogging = self.config.debug
        self.log.consoleLogging = not self.quiet

        # start logger
        self.log.start()

        # user agent
        if self.config.random_user_agent:
            self.user_agent = UserAgent().random

        uses_netloc.append('scgi')
        FancyURLopener.version = self.user_agent

        # set torrent client web url
        torrent_webui_url(True)

        # Check available space
        try:
            total_space, available_space = get_free_space(self.data_dir)
            if available_space < 100:
                self.log.warning('Shutting down as SiCKRAGE needs some space to work. You\'ll get corrupted data otherwise. Only %sMB left', available_space)
                return
        except Exception:
            self.log.error('Failed getting disk space: %s', traceback.format_exc())

        # perform database startup actions
        for db in [self.main_db, self.cache_db]:
            # perform integrity check
            db.integrity_check()

            # migrate database
            db.migrate()

            # sync database repo
            db.sync_db_repo()

            # cleanup
            db.cleanup()

        # load name cache
        self.name_cache.load()

        if self.config.default_page not in ('schedule', 'history', 'IRC'):
            self.config.default_page = 'home'

        # cleanup cache folder
        for folder in ['mako', 'sessions', 'indexers']:
            try:
                shutil.rmtree(os.path.join(sickrage.app.cache_dir, folder), ignore_errors=True)
            except Exception:
                continue

        if self.config.web_port < 21 or self.config.web_port > 65535:
            self.config.web_port = 8081

        if not self.config.web_cookie_secret:
            self.config.web_cookie_secret = generate_secret()

        # attempt to help prevent users from breaking links by using a bad url
        if not self.config.anon_redirect.endswith('?'):
            self.config.anon_redirect = ''

        if not re.match(r'\d+\|[^|]+(?:\|[^|]+)*', self.config.root_dirs):
            self.config.root_dirs = ''

        self.config.naming_force_folders = check_force_season_folders()

        if self.config.nzb_method not in ('blackhole', 'sabnzbd', 'nzbget'):
            self.config.nzb_method = 'blackhole'

        if self.config.torrent_method not in ('blackhole', 'utorrent', 'transmission', 'deluge', 'deluged',
                                              'download_station', 'rtorrent', 'qbittorrent', 'mlnet', 'putio'):
            self.config.torrent_method = 'blackhole'

        if self.config.autopostprocessor_freq < self.config.min_autopostprocessor_freq:
            self.config.autopostprocessor_freq = self.config.min_autopostprocessor_freq
        if self.config.daily_searcher_freq < self.config.min_daily_searcher_freq:
            self.config.daily_searcher_freq = self.config.min_daily_searcher_freq
        if self.config.backlog_searcher_freq < self.config.min_backlog_searcher_freq:
            self.config.backlog_searcher_freq = self.config.min_backlog_searcher_freq
        if self.config.version_updater_freq < self.config.min_version_updater_freq:
            self.config.version_updater_freq = self.config.min_version_updater_freq
        if self.config.subtitle_searcher_freq < self.config.min_subtitle_searcher_freq:
            self.config.subtitle_searcher_freq = self.config.min_subtitle_searcher_freq
        if self.config.failed_snatch_age < self.config.min_failed_snatch_age:
            self.config.failed_snatch_age = self.config.min_failed_snatch_age
        if self.config.proper_searcher_interval not in ('15m', '45m', '90m', '4h', 'daily'):
            self.config.proper_searcher_interval = 'daily'
        if self.config.showupdate_hour < 0 or self.config.showupdate_hour > 23:
            self.config.showupdate_hour = 0

        # add API token refresh job
        self.scheduler.add_job(
            API().refresh_token,
            IntervalTrigger(
                hours=1,
            ),
            name='SR-API',
            id='SR-API'
        )

        # add version checker job
        self.scheduler.add_job(
            self.version_updater.run,
            IntervalTrigger(
                hours=self.config.version_updater_freq,
            ),
            name=self.version_updater.name,
            id=self.version_updater.name
        )

        # add network timezones updater job
        self.scheduler.add_job(
            self.tz_updater.run,
            IntervalTrigger(
                days=1,
            ),
            name=self.tz_updater.name,
            id=self.tz_updater.name
        )

        # add show updater job
        self.scheduler.add_job(
            self.show_updater.run,
            IntervalTrigger(
                days=1,
                start_date=datetime.datetime.now().replace(hour=self.config.showupdate_hour)
            ),
            name=self.show_updater.name,
            id=self.show_updater.name
        )

        # add rss cache updater job
        self.scheduler.add_job(
            self.rsscache_updater.run,
            IntervalTrigger(
                minutes=15,
            ),
            name=self.rsscache_updater.name,
            id=self.rsscache_updater.name
        )

        # add daily search job
        self.scheduler.add_job(
            self.daily_searcher.run,
            IntervalTrigger(
                minutes=self.config.daily_searcher_freq,
                start_date=datetime.datetime.now() + datetime.timedelta(minutes=4)
            ),
            name=self.daily_searcher.name,
            id=self.daily_searcher.name
        )

        # add failed snatch search job
        self.scheduler.add_job(
            self.failed_snatch_searcher.run,
            IntervalTrigger(
                hours=1,
                start_date=datetime.datetime.now() + datetime.timedelta(minutes=4)
            ),
            name=self.failed_snatch_searcher.name,
            id=self.failed_snatch_searcher.name
        )

        # add backlog search job
        self.scheduler.add_job(
            self.backlog_searcher.run,
            IntervalTrigger(
                minutes=self.config.backlog_searcher_freq,
                start_date=datetime.datetime.now() + datetime.timedelta(minutes=30)
            ),
            name=self.backlog_searcher.name,
            id=self.backlog_searcher.name
        )

        # add auto-postprocessing job
        self.scheduler.add_job(
            self.auto_postprocessor.run,
            IntervalTrigger(
                minutes=self.config.autopostprocessor_freq
            ),
            name=self.auto_postprocessor.name,
            id=self.auto_postprocessor.name
        )

        # add find proper job
        self.scheduler.add_job(
            self.proper_searcher.run,
            IntervalTrigger(
                minutes={
                    '15m': 15,
                    '45m': 45,
                    '90m': 90,
                    '4h': 4 * 60,
                    'daily': 24 * 60
                }[self.config.proper_searcher_interval]
            ),
            name=self.proper_searcher.name,
            id=self.proper_searcher.name
        )

        # add trakt.tv checker job
        self.scheduler.add_job(
            self.trakt_searcher.run,
            IntervalTrigger(
                hours=1
            ),
            name=self.trakt_searcher.name,
            id=self.trakt_searcher.name
        )

        # add subtitles finder job
        self.scheduler.add_job(
            self.subtitle_searcher.run,
            IntervalTrigger(
                hours=self.config.subtitle_searcher_freq
            ),
            name=self.subtitle_searcher.name,
            id=self.subtitle_searcher.name
        )

        # add upnp client job
        self.scheduler.add_job(
            self.upnp_client.run,
            IntervalTrigger(
                seconds=self.upnp_client._nat_portmap_lifetime
            ),
            name=self.upnp_client.name,
            id=self.upnp_client.name
        )

        # add namecache update job
        self.scheduler.add_job(
            self.name_cache.build_all,
            IntervalTrigger(
                days=1,
            ),
            name=self.name_cache.name,
            id=self.name_cache.name
        )

        # start scheduler service
        self.scheduler.start()

        # start queue's
        self.io_loop.add_callback(self.search_queue.watch)
        self.io_loop.add_callback(self.show_queue.watch)
        self.io_loop.add_callback(self.postprocessor_queue.watch)

        # fire off startup events
        self.io_loop.run_in_executor(None, self.quicksearch_cache.run)
        self.io_loop.run_in_executor(None, self.name_cache.run)
        self.io_loop.run_in_executor(None, self.version_updater.run)
        self.io_loop.run_in_executor(None, self.tz_updater.run)

        # start web server
        self.wserver.start()

        # launch browser window
        if all([not sickrage.app.no_launch, sickrage.app.config.launch_browser]):
            self.io_loop.run_in_executor(None, functools.partial(launch_browser, ('http', 'https')[sickrage.app.config.enable_https],
                                                                 sickrage.app.config.web_host, sickrage.app.config.web_port))

        def started():
            self.log.info("SiCKRAGE :: STARTED")
            self.log.info("SiCKRAGE :: APP VERSION:[{}]".format(sickrage.version()))
            self.log.info("SiCKRAGE :: CONFIG VERSION:[v{}]".format(self.config.config_version))
            self.log.info("SiCKRAGE :: DATABASE VERSION:[v{}]".format(self.main_db.version))
            self.log.info("SiCKRAGE :: DATABASE TYPE:[{}]".format(self.db_type))
            self.log.info("SiCKRAGE :: URL:[{}://{}:{}{}]".format(('http', 'https')[self.config.enable_https], self.config.web_host, self.config.web_port,
                                                                  self.config.web_root))

        # start io_loop
        self.io_loop.add_callback(started)
        self.io_loop.start()
Example #5
0
    def start(self):
        self.started = True

        # load languages
        tornado.locale.load_gettext_translations(sickrage.LOCALE_DIR,
                                                 'messages')

        # clear mako cache folder
        mako_cache = os.path.join(sickrage.app.cache_dir, 'mako')
        if os.path.isdir(mako_cache):
            shutil.rmtree(mako_cache)

        # video root
        if sickrage.app.config.root_dirs:
            root_dirs = sickrage.app.config.root_dirs.split('|')
            self.video_root = root_dirs[int(root_dirs[0]) + 1]

        # web root
        if sickrage.app.config.web_root:
            sickrage.app.config.web_root = sickrage.app.config.web_root = (
                '/' + sickrage.app.config.web_root.lstrip('/').strip('/'))

        # api root
        self.api_root = r'%s/api/%s' % (sickrage.app.config.web_root,
                                        sickrage.app.config.api_key)

        # tornado setup
        if sickrage.app.config.enable_https:
            # If either the HTTPS certificate or key do not exist, make some self-signed ones.
            if not create_https_certificates(sickrage.app.config.https_cert,
                                             sickrage.app.config.https_key):
                sickrage.app.log.info(
                    "Unable to create CERT/KEY files, disabling HTTPS")
                sickrage.app.config.enable_https = False

            if not (os.path.exists(sickrage.app.config.https_cert)
                    and os.path.exists(sickrage.app.config.https_key)):
                sickrage.app.log.warning(
                    "Disabled HTTPS because of missing CERT and KEY files")
                sickrage.app.config.enable_https = False

        # Load the app
        self.app = Application(
            debug=True,
            autoreload=False,
            gzip=sickrage.app.config.web_use_gzip,
            cookie_secret=sickrage.app.config.web_cookie_secret,
            login_url='%s/login/' % sickrage.app.config.web_root,
            httpclient_secret=generate_secret())

        # Websocket handler
        self.app.add_handlers(
            '.*$',
            [(r'%s/ws/ui' % sickrage.app.config.web_root, WebSocketUIHandler)])

        # Static File Handlers
        self.app.add_handlers(
            '.*$',
            [
                # api
                (r'%s/api/(\w{32})(/?.*)' % sickrage.app.config.web_root,
                 ApiHandler),

                # redirect to home
                (r"(%s)(/?)" % sickrage.app.config.web_root, RedirectHandler, {
                    "url": "%s/home" % sickrage.app.config.web_root
                }),

                # api builder
                (r'%s/api/builder' % sickrage.app.config.web_root,
                 RedirectHandler, {
                     "url": sickrage.app.config.web_root + '/apibuilder/'
                 }),

                # login
                (r'%s/login(/?)' % sickrage.app.config.web_root, LoginHandler),

                # logout
                (r'%s/logout(/?)' % sickrage.app.config.web_root, LogoutHandler
                 ),

                # favicon
                (r'%s/(favicon\.ico)' % sickrage.app.config.web_root,
                 StaticNoCacheFileHandler, {
                     "path":
                     os.path.join(sickrage.app.config.gui_static_dir,
                                  'images/favicon.ico')
                 }),

                # images
                (r'%s/images/(.*)' % sickrage.app.config.web_root,
                 StaticImageHandler, {
                     "path":
                     os.path.join(sickrage.app.config.gui_static_dir, 'images')
                 }),

                # css
                (r'%s/css/(.*)' % sickrage.app.config.web_root,
                 StaticNoCacheFileHandler, {
                     "path":
                     os.path.join(sickrage.app.config.gui_static_dir, 'css')
                 }),

                # scss
                (r'%s/scss/(.*)' % sickrage.app.config.web_root,
                 StaticNoCacheFileHandler, {
                     "path":
                     os.path.join(sickrage.app.config.gui_static_dir, 'scss')
                 }),

                # fonts
                (r'%s/fonts/(.*)' % sickrage.app.config.web_root,
                 StaticNoCacheFileHandler, {
                     "path":
                     os.path.join(sickrage.app.config.gui_static_dir, 'fonts')
                 }),

                # javascript
                (r'%s/js/(.*)' % sickrage.app.config.web_root,
                 StaticNoCacheFileHandler, {
                     "path": os.path.join(sickrage.app.config.gui_static_dir,
                                          'js')
                 }),

                # videos
                (r'%s/videos/(.*)' % sickrage.app.config.web_root,
                 StaticNoCacheFileHandler, {
                     "path": self.video_root
                 }),
            ])

        # Handlers
        self.app.add_handlers('.*$', [
            (r'%s/robots.txt' % sickrage.app.config.web_root,
             RobotsDotTxtHandler),
            (r'%s/messages.po' % sickrage.app.config.web_root,
             MessagesDotPoHandler),
            (r'%s/quicksearch.json' % sickrage.app.config.web_root,
             QuicksearchDotJsonHandler),
            (r'%s/apibuilder(/?)' % sickrage.app.config.web_root,
             APIBulderHandler),
            (r'%s/setHomeLayout(/?)' % sickrage.app.config.web_root,
             SetHomeLayoutHandler),
            (r'%s/setPosterSortBy(/?)' % sickrage.app.config.web_root,
             SetPosterSortByHandler),
            (r'%s/setPosterSortDir(/?)' % sickrage.app.config.web_root,
             SetPosterSortDirHandler),
            (r'%s/setHistoryLayout(/?)' % sickrage.app.config.web_root,
             SetHistoryLayoutHandler),
            (r'%s/toggleDisplayShowSpecials(/?)' %
             sickrage.app.config.web_root, ToggleDisplayShowSpecialsHandler),
            (r'%s/toggleScheduleDisplayPaused(/?)' %
             sickrage.app.config.web_root, ToggleScheduleDisplayPausedHandler),
            (r'%s/setScheduleSort(/?)' % sickrage.app.config.web_root,
             SetScheduleSortHandler),
            (r'%s/schedule(/?)' % sickrage.app.config.web_root,
             ScheduleHandler),
            (r'%s/unlink(/?)' % sickrage.app.config.web_root, UnlinkHandler),
            (r'%s/setScheduleLayout(/?)' % sickrage.app.config.web_root,
             SetScheduleLayoutHandler),
            (r'%s/calendar(/?)' % sickrage.app.config.web_root,
             CalendarHandler),
            (r'%s/changelog(/?)' % sickrage.app.config.web_root,
             ChangelogHandler),
            (r'%s/history(/?)' % sickrage.app.config.web_root, HistoryHandler),
            (r'%s/history/clear(/?)' % sickrage.app.config.web_root,
             HistoryClearHandler),
            (r'%s/history/trim(/?)' % sickrage.app.config.web_root,
             HistoryTrimHandler),
            (r'%s/irc(/?)' % sickrage.app.config.web_root, IRCHandler),
            (r'%s/logs(/?)' % sickrage.app.config.web_root, LogsHandler),
            (r'%s/logs/view(/?)' % sickrage.app.config.web_root,
             LogsViewHandler),
            (r'%s/logs/clearAll(/?)' % sickrage.app.config.web_root,
             LogsClearAllHanlder),
            (r'%s/logs/clearWarnings(/?)' % sickrage.app.config.web_root,
             LogsClearWarningsHanlder),
            (r'%s/logs/clearErrors(/?)' % sickrage.app.config.web_root,
             LogsClearErrorsHanlder),
            (r'%s/browser(/?)' % sickrage.app.config.web_root,
             WebFileBrowserHandler),
            (r'%s/browser/complete(/?)' % sickrage.app.config.web_root,
             WebFileBrowserCompleteHandler),
            (r'%s/home(/?)' % sickrage.app.config.web_root, HomeHandler),
            (r'%s/home/is_alive(/?)' % sickrage.app.config.web_root,
             IsAliveHandler),
            (r'%s/home/testSABnzbd(/?)' % sickrage.app.config.web_root,
             TestSABnzbdHandler),
            (r'%s/home/testTorrent(/?)' % sickrage.app.config.web_root,
             TestTorrentHandler),
            (r'%s/home/testFreeMobile(/?)' % sickrage.app.config.web_root,
             TestFreeMobileHandler),
            (r'%s/home/testTelegram(/?)' % sickrage.app.config.web_root,
             TestTelegramHandler),
            (r'%s/home/testJoin(/?)' % sickrage.app.config.web_root,
             TestJoinHandler),
            (r'%s/home/testGrowl(/?)' % sickrage.app.config.web_root,
             TestGrowlHandler),
            (r'%s/home/testProwl(/?)' % sickrage.app.config.web_root,
             TestProwlHandler),
            (r'%s/home/testBoxcar2(/?)' % sickrage.app.config.web_root,
             TestBoxcar2Handler),
            (r'%s/home/testPushover(/?)' % sickrage.app.config.web_root,
             TestPushoverHandler),
            (r'%s/home/twitterStep1(/?)' % sickrage.app.config.web_root,
             TwitterStep1Handler),
            (r'%s/home/twitterStep2(/?)' % sickrage.app.config.web_root,
             TwitterStep2Handler),
            (r'%s/home/testTwitter(/?)' % sickrage.app.config.web_root,
             TestTwitterHandler),
            (r'%s/home/testTwilio(/?)' % sickrage.app.config.web_root,
             TestTwilioHandler),
            (r'%s/home/testSlack(/?)' % sickrage.app.config.web_root,
             TestSlackHandler),
            (r'%s/home/testDiscord(/?)' % sickrage.app.config.web_root,
             TestDiscordHandler),
            (r'%s/home/testKODI(/?)' % sickrage.app.config.web_root,
             TestKODIHandler),
            (r'%s/home/testPMC(/?)' % sickrage.app.config.web_root,
             TestPMCHandler),
            (r'%s/home/testPMS(/?)' % sickrage.app.config.web_root,
             TestPMSHandler),
            (r'%s/home/testLibnotify(/?)' % sickrage.app.config.web_root,
             TestLibnotifyHandler),
            (r'%s/home/testEMBY(/?)' % sickrage.app.config.web_root,
             TestEMBYHandler),
            (r'%s/home/testNMJ(/?)' % sickrage.app.config.web_root,
             TestNMJHandler),
            (r'%s/home/settingsNMJ(/?)' % sickrage.app.config.web_root,
             SettingsNMJHandler),
            (r'%s/home/testNMJv2(/?)' % sickrage.app.config.web_root,
             TestNMJv2Handler),
            (r'%s/home/settingsNMJv2(/?)' % sickrage.app.config.web_root,
             SettingsNMJv2Handler),
            (r'%s/home/getTraktToken(/?)' % sickrage.app.config.web_root,
             GetTraktTokenHandler),
            (r'%s/home/testTrakt(/?)' % sickrage.app.config.web_root,
             TestTraktHandler),
            (r'%s/home/loadShowNotifyLists(/?)' % sickrage.app.config.web_root,
             LoadShowNotifyListsHandler),
            (r'%s/home/saveShowNotifyList(/?)' % sickrage.app.config.web_root,
             SaveShowNotifyListHandler),
            (r'%s/home/testEmail(/?)' % sickrage.app.config.web_root,
             TestEmailHandler),
            (r'%s/home/testNMA(/?)' % sickrage.app.config.web_root,
             TestNMAHandler),
            (r'%s/home/testPushalot(/?)' % sickrage.app.config.web_root,
             TestPushalotHandler),
            (r'%s/home/testPushbullet(/?)' % sickrage.app.config.web_root,
             TestPushbulletHandler),
            (r'%s/home/getPushbulletDevices(/?)' %
             sickrage.app.config.web_root, GetPushbulletDevicesHandler),
            (r'%s/home/status(/?)' % sickrage.app.config.web_root,
             StatusHandler),
            (r'%s/home/shutdown(/?)' % sickrage.app.config.web_root,
             ShutdownHandler),
            (r'%s/home/restart(/?)' % sickrage.app.config.web_root,
             RestartHandler),
            (r'%s/home/updateCheck(/?)' % sickrage.app.config.web_root,
             UpdateCheckHandler),
            (r'%s/home/update(/?)' % sickrage.app.config.web_root,
             UpdateHandler),
            (r'%s/home/verifyPath(/?)' % sickrage.app.config.web_root,
             VerifyPathHandler),
            (r'%s/home/installRequirements(/?)' % sickrage.app.config.web_root,
             InstallRequirementsHandler),
            (r'%s/home/branchCheckout(/?)' % sickrage.app.config.web_root,
             BranchCheckoutHandler),
            (r'%s/home/displayShow(/?)' % sickrage.app.config.web_root,
             DisplayShowHandler),
            (r'%s/home/editShow(/?)' % sickrage.app.config.web_root,
             EditShowHandler),
            (r'%s/home/togglePause(/?)' % sickrage.app.config.web_root,
             TogglePauseHandler),
            (r'%s/home/deleteShow' % sickrage.app.config.web_root,
             DeleteShowHandler),
            (r'%s/home/refreshShow(/?)' % sickrage.app.config.web_root,
             RefreshShowHandler),
            (r'%s/home/updateShow(/?)' % sickrage.app.config.web_root,
             UpdateShowHandler),
            (r'%s/home/subtitleShow(/?)' % sickrage.app.config.web_root,
             SubtitleShowHandler),
            (r'%s/home/updateKODI(/?)' % sickrage.app.config.web_root,
             UpdateKODIHandler),
            (r'%s/home/updatePLEX(/?)' % sickrage.app.config.web_root,
             UpdatePLEXHandler),
            (r'%s/home/updateEMBY(/?)' % sickrage.app.config.web_root,
             UpdateEMBYHandler),
            (r'%s/home/syncTrakt(/?)' % sickrage.app.config.web_root,
             SyncTraktHandler),
            (r'%s/home/deleteEpisode(/?)' % sickrage.app.config.web_root,
             DeleteEpisodeHandler),
            (r'%s/home/setStatus(/?)' % sickrage.app.config.web_root,
             SetStatusHandler),
            (r'%s/home/testRename(/?)' % sickrage.app.config.web_root,
             TestRenameHandler),
            (r'%s/home/doRename(/?)' % sickrage.app.config.web_root,
             DoRenameHandler),
            (r'%s/home/searchEpisode(/?)' % sickrage.app.config.web_root,
             SearchEpisodeHandler),
            (r'%s/home/getManualSearchStatus(/?)' %
             sickrage.app.config.web_root, GetManualSearchStatusHandler),
            (r'%s/home/searchEpisodeSubtitles(/?)' %
             sickrage.app.config.web_root, SearchEpisodeSubtitlesHandler),
            (r'%s/home/setSceneNumbering(/?)' % sickrage.app.config.web_root,
             SetSceneNumberingHandler),
            (r'%s/home/retryEpisode(/?)' % sickrage.app.config.web_root,
             RetryEpisodeHandler),
            (r'%s/home/fetch_releasegroups(/?)' % sickrage.app.config.web_root,
             FetchReleasegroupsHandler),
            (r'%s/home/postprocess(/?)' % sickrage.app.config.web_root,
             HomePostProcessHandler),
            (r'%s/home/postprocess/processEpisode(/?)' %
             sickrage.app.config.web_root, HomeProcessEpisodeHandler),
            (r'%s/home/addShows(/?)' % sickrage.app.config.web_root,
             HomeAddShowsHandler),
            (r'%s/home/addShows/searchIndexersForShowName(/?)' %
             sickrage.app.config.web_root, SearchIndexersForShowNameHandler),
            (r'%s/home/addShows/massAddTable(/?)' %
             sickrage.app.config.web_root, MassAddTableHandler),
            (r'%s/home/addShows/newShow(/?)' % sickrage.app.config.web_root,
             NewShowHandler),
            (r'%s/home/addShows/traktShows(/?)' % sickrage.app.config.web_root,
             TraktShowsHandler),
            (r'%s/home/addShows/popularShows(/?)' %
             sickrage.app.config.web_root, PopularShowsHandler),
            (r'%s/home/addShows/addShowToBlacklist(/?)' %
             sickrage.app.config.web_root, AddShowToBlacklistHandler),
            (r'%s/home/addShows/existingShows(/?)' %
             sickrage.app.config.web_root, ExistingShowsHandler),
            (r'%s/home/addShows/addShowByID(/?)' %
             sickrage.app.config.web_root, AddShowByIDHandler),
            (r'%s/home/addShows/addNewShow(/?)' % sickrage.app.config.web_root,
             AddNewShowHandler),
            (r'%s/home/addShows/addExistingShows(/?)' %
             sickrage.app.config.web_root, AddExistingShowsHandler),
            (r'%s/manage(/?)' % sickrage.app.config.web_root, ManageHandler),
            (r'%s/manage/showEpisodeStatuses(/?)' %
             sickrage.app.config.web_root, ShowEpisodeStatusesHandler),
            (r'%s/manage/episodeStatuses(/?)' % sickrage.app.config.web_root,
             EpisodeStatusesHandler),
            (r'%s/manage/changeEpisodeStatuses(/?)' %
             sickrage.app.config.web_root, ChangeEpisodeStatusesHandler),
            (r'%s/manage/showSubtitleMissed(/?)' %
             sickrage.app.config.web_root, ShowSubtitleMissedHandler),
            (r'%s/manage/subtitleMissed(/?)' % sickrage.app.config.web_root,
             SubtitleMissedHandler),
            (r'%s/manage/downloadSubtitleMissed(/?)' %
             sickrage.app.config.web_root, DownloadSubtitleMissedHandler),
            (r'%s/manage/backlogShow(/?)' % sickrage.app.config.web_root,
             BacklogShowHandler),
            (r'%s/manage/backlogOverview(/?)' % sickrage.app.config.web_root,
             BacklogOverviewHandler),
            (r'%s/manage/massEdit(/?)' % sickrage.app.config.web_root,
             MassEditHandler),
            (r'%s/manage/massUpdate(/?)' % sickrage.app.config.web_root,
             MassUpdateHandler),
            (r'%s/manage/failedDownloads(/?)' % sickrage.app.config.web_root,
             FailedDownloadsHandler),
            (r'%s/manage/manageQueues(/?)' % sickrage.app.config.web_root,
             ManageQueuesHandler),
            (r'%s/manage/manageQueues/forceBacklogSearch(/?)' %
             sickrage.app.config.web_root, ForceBacklogSearchHandler),
            (r'%s/manage/manageQueues/forceDailySearch(/?)' %
             sickrage.app.config.web_root, ForceDailySearchHandler),
            (r'%s/manage/manageQueues/forceFindPropers(/?)' %
             sickrage.app.config.web_root, ForceFindPropersHandler),
            (r'%s/manage/manageQueues/pauseDailySearcher(/?)' %
             sickrage.app.config.web_root, PauseDailySearcherHandler),
            (r'%s/manage/manageQueues/pauseBacklogSearcher(/?)' %
             sickrage.app.config.web_root, PauseBacklogSearcherHandler),
            (r'%s/manage/manageQueues/pausePostProcessor(/?)' %
             sickrage.app.config.web_root, PausePostProcessorHandler),
            (r'%s/config(/?)' % sickrage.app.config.web_root, ConfigHandler),
            (r'%s/config/reset(/?)' % sickrage.app.config.web_root,
             ConfigResetHandler),
            (r'%s/config/anime(/?)' % sickrage.app.config.web_root,
             ConfigAnimeHandler),
            (r'%s/config/anime/saveAnime(/?)' % sickrage.app.config.web_root,
             ConfigSaveAnimeHandler),
            (r'%s/config/backuprestore(/?)' % sickrage.app.config.web_root,
             ConfigBackupRestoreHandler),
            (r'%s/config/backuprestore/backup(/?)' %
             sickrage.app.config.web_root, ConfigBackupHandler),
            (r'%s/config/backuprestore/restore(/?)' %
             sickrage.app.config.web_root, ConfigRestoreHandler),
            (r'%s/config/backuprestore/saveBackupRestore(/?)' %
             sickrage.app.config.web_root, SaveBackupRestoreHandler),
            (r'%s/config/general(/?)' % sickrage.app.config.web_root,
             ConfigGeneralHandler),
            (r'%s/config/general/generateApiKey(/?)' %
             sickrage.app.config.web_root, GenerateApiKeyHandler),
            (r'%s/config/general/saveRootDirs(/?)' %
             sickrage.app.config.web_root, SaveRootDirsHandler),
            (r'%s/config/general/saveAddShowDefaults(/?)' %
             sickrage.app.config.web_root, SaveAddShowDefaultsHandler),
            (r'%s/config/general/saveGeneral(/?)' %
             sickrage.app.config.web_root, SaveGeneralHandler),
            (r'%s/config/notifications(/?)' % sickrage.app.config.web_root,
             ConfigNotificationsHandler),
            (r'%s/config/notifications/saveNotifications(/?)' %
             sickrage.app.config.web_root, SaveNotificationsHandler),
            (r'%s/config/postProcessing(/?)' % sickrage.app.config.web_root,
             ConfigPostProcessingHandler),
            (r'%s/config/postProcessing/savePostProcessing(/?)' %
             sickrage.app.config.web_root, SavePostProcessingHandler),
            (r'%s/config/postProcessing/testNaming(/?)' %
             sickrage.app.config.web_root, TestNamingHandler),
            (r'%s/config/postProcessing/isNamingValid(/?)' %
             sickrage.app.config.web_root, IsNamingValidHandler),
            (r'%s/config/postProcessing/isRarSupported(/?)' %
             sickrage.app.config.web_root, IsRarSupportedHandler),
            (r'%s/config/providers(/?)' % sickrage.app.config.web_root,
             ConfigProvidersHandler),
            (r'%s/config/providers/canAddNewznabProvider(/?)' %
             sickrage.app.config.web_root, CanAddNewznabProviderHandler),
            (r'%s/config/providers/canAddTorrentRssProvider(/?)' %
             sickrage.app.config.web_root, CanAddTorrentRssProviderHandler),
            (r'%s/config/providers/getNewznabCategories(/?)' %
             sickrage.app.config.web_root, GetNewznabCategoriesHandler),
            (r'%s/config/providers/saveProviders(/?)' %
             sickrage.app.config.web_root, SaveProvidersHandler),
            (r'%s/config/qualitySettings(/?)' % sickrage.app.config.web_root,
             ConfigQualitySettingsHandler),
            (r'%s/config/qualitySettings/saveQualities(/?)' %
             sickrage.app.config.web_root, SaveQualitiesHandler),
            (r'%s/config/search(/?)' % sickrage.app.config.web_root,
             ConfigSearchHandler),
            (r'%s/config/search/saveSearch(/?)' % sickrage.app.config.web_root,
             SaveSearchHandler),
            (r'%s/config/subtitles(/?)' % sickrage.app.config.web_root,
             ConfigSubtitlesHandler),
            (r'%s/config/subtitles/get_code(/?)' %
             sickrage.app.config.web_root, ConfigSubtitleGetCodeHandler),
            (r'%s/config/subtitles/wanted_languages(/?)' %
             sickrage.app.config.web_root,
             ConfigSubtitlesWantedLanguagesHandler),
            (r'%s/config/subtitles/saveSubtitles(/?)' %
             sickrage.app.config.web_root, SaveSubtitlesHandler),
        ])

        # HTTPS Cert/Key object
        ssl_ctx = None
        if sickrage.app.config.enable_https:
            ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
            ssl_ctx.load_cert_chain(sickrage.app.config.https_cert,
                                    sickrage.app.config.https_key)

        # Web Server
        self.server = HTTPServer(
            self.app,
            ssl_options=ssl_ctx,
            xheaders=sickrage.app.config.handle_reverse_proxy)

        try:
            self.server.listen(sickrage.app.config.web_port)
        except socket.error as e:
            sickrage.app.log.warning(e.strerror)
            raise SystemExit