__init__.py 21.1 KB
Newer Older
echel0n's avatar
echel0n committed
1
# Author: echel0n <[email protected]>
echel0n's avatar
echel0n committed
2
# URL: https://sickrage.ca
3
#
echel0n's avatar
echel0n committed
4
# This file is part of SickRage.
5
#
echel0n's avatar
echel0n committed
6
# SickRage is free software: you can redistribute it and/or modify
7 8 9 10
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
echel0n's avatar
echel0n committed
11
# SickRage is distributed in the hope that it will be useful,
12 13 14 15 16
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
echel0n's avatar
echel0n committed
17
# along with SickRage.  If not, see <http://www.gnu.org/licenses/>.
18 19 20

from __future__ import unicode_literals

21
import datetime
22
import os
23
import platform
24 25 26
import re
import shutil
import socket
27
import sys
28
import threading
29
import time
echel0n's avatar
echel0n committed
30
import traceback
31 32 33
import urllib
import urlparse
import uuid
34

35
from apscheduler.schedulers.tornado import TornadoScheduler
echel0n's avatar
echel0n committed
36
from apscheduler.triggers.interval import IntervalTrigger
37
from dateutil import tz
38
from fake_useragent import UserAgent
echel0n's avatar
echel0n committed
39
from keycloak.realm import KeycloakRealm
40
from tornado.ioloop import IOLoop
echel0n's avatar
echel0n committed
41

echel0n's avatar
echel0n committed
42
import adba
echel0n's avatar
echel0n committed
43
import sickrage
44
from sickrage.core.api import API
45
from sickrage.core.caches.name_cache import NameCache
46
from sickrage.core.caches.quicksearch_cache import QuicksearchCache
47
from sickrage.core.common import SD, SKIPPED, WANTED
48
from sickrage.core.config import Config
49 50
from sickrage.core.databases.cache import CacheDB
from sickrage.core.databases.main import MainDB
echel0n's avatar
echel0n committed
51
from sickrage.core.helpers import findCertainShow, generate_secret, makeDir, get_lan_ip, restoreSR, \
52
    getDiskSpaceUsage, getFreeSpace, launch_browser, torrent_webui_url
53
from sickrage.core.helpers.encoding import get_sys_encoding, ek, patch_modules
54
from sickrage.core.logger import Logger
55
from sickrage.core.nameparser.validator import check_force_season_folders
56
from sickrage.core.processors import auto_postprocessor
57
from sickrage.core.processors.auto_postprocessor import AutoPostProcessor
echel0n's avatar
echel0n committed
58
from sickrage.core.queues.event import EventQueue
59 60 61 62 63
from sickrage.core.queues.postprocessor import PostProcessorQueue
from sickrage.core.queues.search import SearchQueue
from sickrage.core.queues.show import ShowQueue
from sickrage.core.searchers.backlog_searcher import BacklogSearcher
from sickrage.core.searchers.daily_searcher import DailySearcher
echel0n's avatar
echel0n committed
64
from sickrage.core.searchers.failed_snatch_searcher import FailedSnatchSearcher
65 66 67
from sickrage.core.searchers.proper_searcher import ProperSearcher
from sickrage.core.searchers.subtitle_searcher import SubtitleSearcher
from sickrage.core.searchers.trakt_searcher import TraktSearcher
68
from sickrage.core.tv.show import TVShow
69
from sickrage.core.ui import Notifications
70
from sickrage.core.updaters.show_updater import ShowUpdater
71
from sickrage.core.updaters.tz_updater import TimeZoneUpdater
72
from sickrage.core.upnp import UPNPClient
73 74
from sickrage.core.version_updater import VersionUpdater
from sickrage.core.webserver import WebServer
75 76
from sickrage.metadata import MetadataProviders
from sickrage.notifiers import NotifierProviders
77
from sickrage.providers import SearchProviders
78

79

80 81
class Core(object):
    def __init__(self):
82 83
        self.started = False
        self.daemon = None
84
        self.io_loop = IOLoop()
85
        self.pid = os.getpid()
86
        self.showlist = []
87

88 89 90 91
        try:
            self.tz = tz.tzwinlocal() if tz.tzwinlocal else tz.tzlocal()
        except Exception:
            self.tz = tz.tzlocal()
92

93 94 95
        self.config_file = None
        self.data_dir = None
        self.cache_dir = None
echel0n's avatar
echel0n committed
96
        self.quiet = None
97 98 99 100 101 102
        self.no_launch = None
        self.web_port = None
        self.developer = None
        self.debug = None
        self.newest_version_string = None

echel0n's avatar
echel0n committed
103 104 105
        self.naming_ep_type = ("%(seasonnumber)dx%(episodenumber)02d",
                               "s%(seasonnumber)02de%(episodenumber)02d",
                               "S%(seasonnumber)02dE%(episodenumber)02d",
106 107
                               "%(seasonnumber)02dx%(episodenumber)02d",
                               "S%(seasonnumber)02d E%(episodenumber)02d")
echel0n's avatar
echel0n committed
108 109 110
        self.sports_ep_type = ("%(seasonnumber)dx%(episodenumber)02d",
                               "s%(seasonnumber)02de%(episodenumber)02d",
                               "S%(seasonnumber)02dE%(episodenumber)02d",
111 112 113
                               "%(seasonnumber)02dx%(episodenumber)02d",
                               "S%(seasonnumber)02 dE%(episodenumber)02d")
        self.naming_ep_type_text = ("1x02", "s01e02", "S01E02", "01x02", "S01 E02",)
echel0n's avatar
echel0n committed
114 115 116 117 118 119 120
        self.naming_multi_ep_type = {0: ["-%(episodenumber)02d"] * len(self.naming_ep_type),
                                     1: [" - " + x for x in self.naming_ep_type],
                                     2: [x + "%(episodenumber)02d" for x in ("x", "e", "E", "x")]}
        self.naming_multi_ep_type_text = ("extend", "duplicate", "repeat")
        self.naming_sep_type = (" - ", " ")
        self.naming_sep_type_text = (" - ", "space")

121 122
        self.user_agent = 'SiCKRAGE.CE.1/({};{};{})'.format(platform.system(), platform.release(), str(uuid.uuid1()))
        self.languages = [language for language in os.listdir(sickrage.LOCALE_DIR) if '_' in language]
123
        self.sys_encoding = get_sys_encoding()
124
        self.client_web_urls = {'torrent': '', 'newznab': ''}
125

126 127
        self.adba_connection = None
        self.notifier_providers = None
echel0n's avatar
echel0n committed
128
        self.metadata_providers = {}
129
        self.search_providers = None
130 131
        self.log = None
        self.config = None
132 133 134 135 136
        self.alerts = None
        self.main_db = None
        self.cache_db = None
        self.scheduler = None
        self.wserver = None
137
        self.google_auth = None
138 139 140 141
        self.name_cache = None
        self.show_queue = None
        self.search_queue = None
        self.postprocessor_queue = None
echel0n's avatar
echel0n committed
142
        self.event_queue = None
143 144
        self.version_updater = None
        self.show_updater = None
145
        self.tz_updater = None
146 147 148 149 150 151
        self.daily_searcher = None
        self.backlog_searcher = None
        self.proper_searcher = None
        self.trakt_searcher = None
        self.subtitle_searcher = None
        self.auto_postprocessor = None
echel0n's avatar
echel0n committed
152
        self.upnp_client = None
echel0n's avatar
echel0n committed
153
        self.oidc_client = None
154
        self.quicksearch_cache = None
echel0n's avatar
echel0n committed
155

156
    def start(self):
157
        self.started = True
158

159 160
        # thread name
        threading.currentThread().setName('CORE')
161

162 163 164
        # patch modules with encoding kludge
        patch_modules()

165
        # init core classes
166 167
        self.notifier_providers = NotifierProviders()
        self.metadata_providers = MetadataProviders()
168 169 170 171 172 173
        self.search_providers = SearchProviders()
        self.log = Logger()
        self.config = Config()
        self.alerts = Notifications()
        self.main_db = MainDB()
        self.cache_db = CacheDB()
174
        self.scheduler = TornadoScheduler()
175 176 177 178 179
        self.wserver = WebServer()
        self.name_cache = NameCache()
        self.show_queue = ShowQueue()
        self.search_queue = SearchQueue()
        self.postprocessor_queue = PostProcessorQueue()
echel0n's avatar
echel0n committed
180
        self.event_queue = EventQueue()
181 182
        self.version_updater = VersionUpdater()
        self.show_updater = ShowUpdater()
183
        self.tz_updater = TimeZoneUpdater()
184
        self.daily_searcher = DailySearcher()
echel0n's avatar
echel0n committed
185
        self.failed_snatch_searcher = FailedSnatchSearcher()
186 187 188 189 190
        self.backlog_searcher = BacklogSearcher()
        self.proper_searcher = ProperSearcher()
        self.trakt_searcher = TraktSearcher()
        self.subtitle_searcher = SubtitleSearcher()
        self.auto_postprocessor = AutoPostProcessor()
191
        self.upnp_client = UPNPClient()
192
        self.quicksearch_cache = QuicksearchCache()
193

echel0n's avatar
echel0n committed
194 195 196 197 198
        # 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')

199
        # Check if we need to perform a restore first
200 201
        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)
202 203
            print("Restoring SiCKRAGE backup: %s!\n" % ("FAILED", "SUCCESSFUL")[success])
            if success:
204
                shutil.rmtree(os.path.abspath(os.path.join(self.data_dir, 'restore')), ignore_errors=True)
205 206

        # migrate old database file names to new ones
207 208
        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')):
209 210
                helpers.move_file(os.path.join(self.data_dir, 'sickrage.db'),
                                  os.path.join(self.data_dir, '{}.bak-{}'
211 212 213 214
                                              .format('sickrage.db',
                                                      datetime.datetime.now().strftime(
                                                          '%Y%m%d_%H%M%S'))))

215 216
            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')))
217

218
        # load config
219
        self.config.load()
220

221
        # set language
222
        self.config.change_gui_lang(self.config.gui_lang)
223

224
        # set socket timeout
225
        socket.setdefaulttimeout(self.config.socket_timeout)
226

227
        # setup logger settings
228 229
        self.log.logSize = self.config.log_size
        self.log.logNr = self.config.log_nr
230
        self.log.logFile = os.path.join(self.data_dir, 'logs', 'sickrage.log')
231
        self.log.debugLogging = self.config.debug
echel0n's avatar
echel0n committed
232
        self.log.consoleLogging = not self.quiet
233 234

        # start logger
235
        self.log.start()
echel0n's avatar
echel0n committed
236

237
        # user agent
238
        if self.config.random_user_agent:
239
            self.user_agent = UserAgent().random
240 241

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

244 245 246
        # set torrent client web url
        torrent_webui_url(True)

echel0n's avatar
echel0n committed
247 248
        # Check available space
        try:
249
            total_space, available_space = getFreeSpace(self.data_dir)
echel0n's avatar
echel0n committed
250
            if available_space < 100:
251 252
                self.log.error('Shutting down as SiCKRAGE needs some space to work. You\'ll get corrupted data '
                               'otherwise. Only %sMB left', available_space)
echel0n's avatar
echel0n committed
253
                return
254
        except Exception:
echel0n's avatar
echel0n committed
255
            self.log.error('Failed getting disk space: %s', traceback.format_exc())
echel0n's avatar
echel0n committed
256

257
        # perform database startup actions
258
        for db in [self.main_db, self.cache_db]:
259 260 261 262 263 264 265 266 267
            # initialize database
            db.initialize()

            # check integrity of database
            db.check_integrity()

            # migrate database
            db.migrate()

268 269 270
            # misc database cleanups
            db.cleanup()

271 272 273
            # upgrade database
            db.upgrade()

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

279
        # load name cache
280
        self.name_cache.load()
281

echel0n's avatar
echel0n committed
282 283
        # load data for shows from database
        self.load_shows()
284

285
        if self.config.default_page not in ('schedule', 'history', 'IRC'):
286
            self.config.default_page = 'home'
echel0n's avatar
echel0n committed
287

288
        # cleanup cache folder
echel0n's avatar
echel0n committed
289
        for folder in ['mako', 'sessions', 'indexers']:
290
            try:
291
                shutil.rmtree(os.path.join(sickrage.app.cache_dir, folder), ignore_errors=True)
292 293
            except Exception:
                continue
echel0n's avatar
echel0n committed
294

295
        # init anidb connection
296
        if self.config.use_anidb:
297
            def anidb_logger(msg):
298
                return self.log.debug("AniDB: {} ".format(msg))
299

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

306 307
        if self.config.web_port < 21 or self.config.web_port > 65535:
            self.config.web_port = 8081
echel0n's avatar
echel0n committed
308

309
        if not self.config.web_cookie_secret:
echel0n's avatar
echel0n committed
310
            self.config.web_cookie_secret = generate_secret()
echel0n's avatar
echel0n committed
311 312

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

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

319
        self.config.naming_force_folders = check_force_season_folders()
320

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

324
        if self.config.torrent_method not in ('blackhole', 'utorrent', 'transmission', 'deluge', 'deluged',
325
                                              'download_station', 'rtorrent', 'qbittorrent', 'mlnet', 'putio'):
326 327 328 329 330 331 332 333 334 335 336 337
            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
echel0n's avatar
echel0n committed
338 339
        if self.config.failed_snatch_age < self.config.min_failed_snatch_age:
            self.config.failed_snatch_age = self.config.min_failed_snatch_age
340 341 342 343
        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
echel0n's avatar
echel0n committed
344

345
        # add version checker job
346 347
        self.scheduler.add_job(
            self.version_updater.run,
echel0n's avatar
echel0n committed
348
            IntervalTrigger(
echel0n's avatar
echel0n committed
349
                hours=self.config.version_updater_freq,
echel0n's avatar
echel0n committed
350
            ),
351 352
            name=self.version_updater.name,
            id=self.version_updater.name
353
        )
echel0n's avatar
echel0n committed
354

355
        # add network timezones updater job
356
        self.scheduler.add_job(
357
            self.tz_updater.run,
echel0n's avatar
echel0n committed
358
            IntervalTrigger(
echel0n's avatar
echel0n committed
359
                days=1,
echel0n's avatar
echel0n committed
360
            ),
361 362
            name=self.tz_updater.name,
            id=self.tz_updater.name
363
        )
echel0n's avatar
echel0n committed
364

365
        # add show updater job
366 367
        self.scheduler.add_job(
            self.show_updater.run,
echel0n's avatar
echel0n committed
368 369
            IntervalTrigger(
                days=1,
370
                start_date=datetime.datetime.now().replace(hour=self.config.showupdate_hour)
echel0n's avatar
echel0n committed
371
            ),
372 373
            name=self.show_updater.name,
            id=self.show_updater.name
echel0n's avatar
echel0n committed
374 375
        )

376
        # add daily search job
377 378
        self.scheduler.add_job(
            self.daily_searcher.run,
echel0n's avatar
echel0n committed
379
            IntervalTrigger(
380
                minutes=self.config.daily_searcher_freq,
381
                start_date=datetime.datetime.now() + datetime.timedelta(minutes=4)
echel0n's avatar
echel0n committed
382
            ),
383 384 385 386
            name=self.daily_searcher.name,
            id=self.daily_searcher.name
        )

echel0n's avatar
echel0n committed
387
        # add failed snatch search job
388
        self.scheduler.add_job(
echel0n's avatar
echel0n committed
389
            self.failed_snatch_searcher.run,
390 391 392 393
            IntervalTrigger(
                hours=1,
                start_date=datetime.datetime.now() + datetime.timedelta(minutes=4)
            ),
echel0n's avatar
echel0n committed
394 395
            name=self.failed_snatch_searcher.name,
            id=self.failed_snatch_searcher.name
echel0n's avatar
echel0n committed
396 397
        )

398
        # add backlog search job
399 400
        self.scheduler.add_job(
            self.backlog_searcher.run,
echel0n's avatar
echel0n committed
401
            IntervalTrigger(
402
                minutes=self.config.backlog_searcher_freq,
403
                start_date=datetime.datetime.now() + datetime.timedelta(minutes=30)
echel0n's avatar
echel0n committed
404
            ),
405 406
            name=self.backlog_searcher.name,
            id=self.backlog_searcher.name
echel0n's avatar
echel0n committed
407 408
        )

409
        # add auto-postprocessing job
410 411
        self.scheduler.add_job(
            self.auto_postprocessor.run,
echel0n's avatar
echel0n committed
412
            IntervalTrigger(
413
                minutes=self.config.autopostprocessor_freq
echel0n's avatar
echel0n committed
414
            ),
415 416
            name=self.auto_postprocessor.name,
            id=self.auto_postprocessor.name
echel0n's avatar
echel0n committed
417 418
        )

419
        # add find proper job
420 421
        self.scheduler.add_job(
            self.proper_searcher.run,
echel0n's avatar
echel0n committed
422
            IntervalTrigger(
echel0n's avatar
echel0n committed
423 424 425 426 427 428 429
                minutes={
                    '15m': 15,
                    '45m': 45,
                    '90m': 90,
                    '4h': 4 * 60,
                    'daily': 24 * 60
                }[self.config.proper_searcher_interval]
echel0n's avatar
echel0n committed
430
            ),
431 432
            name=self.proper_searcher.name,
            id=self.proper_searcher.name
echel0n's avatar
echel0n committed
433 434
        )

435
        # add trakt.tv checker job
436 437
        self.scheduler.add_job(
            self.trakt_searcher.run,
echel0n's avatar
echel0n committed
438 439 440
            IntervalTrigger(
                hours=1
            ),
441 442
            name=self.trakt_searcher.name,
            id=self.trakt_searcher.name
echel0n's avatar
echel0n committed
443 444
        )

445
        # add subtitles finder job
446 447
        self.scheduler.add_job(
            self.subtitle_searcher.run,
echel0n's avatar
echel0n committed
448
            IntervalTrigger(
449
                hours=self.config.subtitle_searcher_freq
echel0n's avatar
echel0n committed
450
            ),
451 452
            name=self.subtitle_searcher.name,
            id=self.subtitle_searcher.name
echel0n's avatar
echel0n committed
453 454
        )

455 456 457 458 459 460 461 462 463 464
        # 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
        )

465
        # start scheduler service
466
        self.scheduler.start()
467

echel0n's avatar
echel0n committed
468
        # start queue's
469 470 471
        self.search_queue.start()
        self.show_queue.start()
        self.postprocessor_queue.start()
echel0n's avatar
echel0n committed
472
        self.event_queue.start()
echel0n's avatar
echel0n committed
473

474
        # start webserver
475
        self.wserver.start()
476

echel0n's avatar
echel0n committed
477
        # fire off startup events
echel0n's avatar
echel0n committed
478
        # self.event_queue.fire_event(self.version_updater.run)
echel0n's avatar
echel0n committed
479 480
        self.event_queue.fire_event(self.tz_updater.run)

echel0n's avatar
echel0n committed
481 482 483
        # start ioloop
        self.io_loop.start()

484
    def shutdown(self, restart=False):
485
        if self.started:
486
            self.log.info('SiCKRAGE IS SHUTTING DOWN!!!')
487

echel0n's avatar
echel0n committed
488
            # shutdown webserver
echel0n's avatar
echel0n committed
489 490
            if self.wserver:
                self.wserver.shutdown()
echel0n's avatar
echel0n committed
491

492
            # shutdown show queue
493 494 495 496
            if self.show_queue:
                self.log.debug("Shutting down show queue")
                self.show_queue.shutdown()
                del self.show_queue
echel0n's avatar
echel0n committed
497

498
            # shutdown search queue
499 500 501 502
            if self.search_queue:
                self.log.debug("Shutting down search queue")
                self.search_queue.shutdown()
                del self.search_queue
503

echel0n's avatar
echel0n committed
504
            # shutdown post-processor queue
505 506 507 508
            if self.postprocessor_queue:
                self.log.debug("Shutting down post-processor queue")
                self.postprocessor_queue.shutdown()
                del self.postprocessor_queue
echel0n's avatar
echel0n committed
509

echel0n's avatar
echel0n committed
510 511 512 513 514 515
            # shutdown event queue
            if self.event_queue:
                self.log.debug("Shutting down event queue")
                self.event_queue.shutdown()
                del self.event_queue

516
            # log out of ADBA
517 518 519
            if self.adba_connection:
                self.log.debug("Shutting down ANIDB connection")
                self.adba_connection.stop()
echel0n's avatar
echel0n committed
520

521 522
            # save all show and config settings
            self.save_all()
523

524 525 526 527 528
            # close databases
            for db in [self.main_db, self.cache_db]:
                if db.opened:
                    self.log.debug("Shutting down {} database connection".format(db.name))
                    db.close()
529

530
            # shutdown logging
531 532
            if self.log:
                self.log.close()
533

echel0n's avatar
echel0n committed
534 535
        if restart:
            os.execl(sys.executable, sys.executable, *sys.argv)
536 537

        if sickrage.app.daemon:
538
            sickrage.app.daemon.stop()
echel0n's avatar
echel0n committed
539

540
        self.started = False
541

echel0n's avatar
echel0n committed
542 543
        self.io_loop.stop()

echel0n's avatar
echel0n committed
544 545
    def save_all(self):
        # write all shows
546
        self.log.info("Saving all shows to the database")
547
        for show in self.showlist:
548
            try:
549
                show.save_to_db()
550
            except Exception:
551
                continue
echel0n's avatar
echel0n committed
552 553

        # save config
554
        self.config.save()
555

echel0n's avatar
echel0n committed
556 557
    def load_shows(self):
        """
558
        Populates the showlist and quicksearch cache with shows and episodes from the database
echel0n's avatar
echel0n committed
559 560
        """

561 562
        self.quicksearch_cache.load()

563
        for dbData in self.main_db.all('tv_shows'):
564 565
            show = TVShow(int(dbData['indexer']), int(dbData['indexer_id']))

echel0n's avatar
echel0n committed
566
            try:
567 568 569
                self.log.debug("Loading data for show: [{}]".format(show.name))
                self.showlist.append(show)
                self.quicksearch_cache.add_show(show.indexerid)
echel0n's avatar
echel0n committed
570
            except Exception as e:
echel0n's avatar
echel0n committed
571
                self.log.debug("Show error in [%s]: %s" % (show.location, str(e)))