__init__.py 20.5 KB
Newer Older
echel0n's avatar
echel0n committed
1
# Author: echel0n <[email protected]>
echel0n's avatar
echel0n committed
2
# URL: https://sickrage.ca
echel0n's avatar
echel0n committed
3
#
echel0n's avatar
echel0n committed
4
# This file is part of SickRage.
echel0n's avatar
echel0n committed
5
#
echel0n's avatar
echel0n committed
6
# SickRage is free software: you can redistribute it and/or modify
echel0n's avatar
echel0n committed
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,
echel0n's avatar
echel0n committed
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/>.
echel0n's avatar
echel0n committed
18
19
20

from __future__ import unicode_literals

echel0n's avatar
echel0n committed
21
import datetime
echel0n's avatar
echel0n committed
22
import os
23
import platform
echel0n's avatar
echel0n committed
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
58
59
60
from sickrage.core.processors.auto_postprocessor import AutoPostProcessor
from sickrage.core.queues.postprocessor import PostProcessorQueue
from sickrage.core.queues.search import SearchQueue
from sickrage.core.queues.show import ShowQueue
61
from sickrage.core.searchers import get_backlog_cycle_time
62
63
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 update_network_dict
echel0n's avatar
echel0n committed
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
echel0n's avatar
echel0n committed
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
        self.tz = tz.tzwinlocal() if tz.tzwinlocal else tz.tzlocal()
89

90
91
92
93
94
95
96
97
98
99
100
        self.config_file = None
        self.data_dir = None
        self.cache_dir = None
        self.quite = None
        self.no_launch = None
        self.web_port = None
        self.developer = None
        self.debug = None
        self.newest_version = None
        self.newest_version_string = None

echel0n's avatar
echel0n committed
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
        self.naming_ep_type = ("%(seasonnumber)dx%(episodenumber)02d",
                               "s%(seasonnumber)02de%(episodenumber)02d",
                               "S%(seasonnumber)02dE%(episodenumber)02d",
                               "%(seasonnumber)02dx%(episodenumber)02d")
        self.sports_ep_type = ("%(seasonnumber)dx%(episodenumber)02d",
                               "s%(seasonnumber)02de%(episodenumber)02d",
                               "S%(seasonnumber)02dE%(episodenumber)02d",
                               "%(seasonnumber)02dx%(episodenumber)02d")
        self.naming_ep_type_text = ("1x02", "s01e02", "S01E02", "01x02")
        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")

117
118
        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]
119
        self.sys_encoding = get_sys_encoding()
120
        self.client_web_urls = {'torrent': '', 'newznab': ''}
echel0n's avatar
echel0n committed
121

122
123
        self.adba_connection = None
        self.notifier_providers = None
echel0n's avatar
echel0n committed
124
        self.metadata_providers = {}
125
        self.search_providers = None
126
127
        self.log = None
        self.config = None
128
129
130
131
132
        self.alerts = None
        self.main_db = None
        self.cache_db = None
        self.scheduler = None
        self.wserver = None
133
        self.google_auth = None
134
135
136
137
138
139
140
141
142
143
144
145
        self.name_cache = None
        self.show_queue = None
        self.search_queue = None
        self.postprocessor_queue = None
        self.version_updater = None
        self.show_updater = None
        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
146
        self.upnp_client = None
echel0n's avatar
echel0n committed
147
        self.oidc_client = None
148
        self.quicksearch_cache = None
echel0n's avatar
echel0n committed
149

150
    def start(self):
151
        self.started = True
152

153
154
        # thread name
        threading.currentThread().setName('CORE')
155

echel0n's avatar
echel0n committed
156
157
158
        # patch modules with encoding kludge
        patch_modules()

159
        # init core classes
160
161
        self.notifier_providers = NotifierProviders()
        self.metadata_providers = MetadataProviders()
162
163
164
165
166
167
        self.search_providers = SearchProviders()
        self.log = Logger()
        self.config = Config()
        self.alerts = Notifications()
        self.main_db = MainDB()
        self.cache_db = CacheDB()
168
        self.scheduler = TornadoScheduler()
169
170
171
172
173
174
175
176
        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()
echel0n's avatar
echel0n committed
177
        self.failed_snatch_searcher = FailedSnatchSearcher()
178
179
180
181
182
        self.backlog_searcher = BacklogSearcher()
        self.proper_searcher = ProperSearcher()
        self.trakt_searcher = TraktSearcher()
        self.subtitle_searcher = SubtitleSearcher()
        self.auto_postprocessor = AutoPostProcessor()
183
        self.upnp_client = UPNPClient()
184
        self.quicksearch_cache = QuicksearchCache()
185

echel0n's avatar
echel0n committed
186
187
188
189
190
        # 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')

191
        # Check if we need to perform a restore first
192
193
        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)
194
195
            print("Restoring SiCKRAGE backup: %s!\n" % ("FAILED", "SUCCESSFUL")[success])
            if success:
196
                shutil.rmtree(os.path.abspath(os.path.join(self.data_dir, 'restore')), ignore_errors=True)
197
198

        # migrate old database file names to new ones
199
200
201
202
        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-{}'
203
204
205
206
                                              .format('sickrage.db',
                                                      datetime.datetime.now().strftime(
                                                          '%Y%m%d_%H%M%S'))))

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

210
        # load config
211
        self.config.load()
212

213
        # set language
214
        self.config.change_gui_lang(self.config.gui_lang)
215

216
        # set socket timeout
217
        socket.setdefaulttimeout(self.config.socket_timeout)
218

219
        # setup logger settings
220
221
        self.log.logSize = self.config.log_size
        self.log.logNr = self.config.log_nr
222
        self.log.logFile = os.path.join(self.data_dir, 'logs', 'sickrage.log')
223
        self.log.debugLogging = self.config.debug
224
        self.log.consoleLogging = not self.quite
225
226

        # start logger
227
        self.log.start()
echel0n's avatar
echel0n committed
228

229
        # user agent
230
        if self.config.random_user_agent:
231
            self.user_agent = UserAgent().random
232
233

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

236
237
238
        # set torrent client web url
        torrent_webui_url(True)

echel0n's avatar
echel0n committed
239
240
        # Check available space
        try:
241
            total_space, available_space = getFreeSpace(self.data_dir)
echel0n's avatar
echel0n committed
242
            if available_space < 100:
243
244
                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
245
                return
246
        except Exception:
echel0n's avatar
echel0n committed
247
            self.log.error('Failed getting disk space: %s', traceback.format_exc())
echel0n's avatar
echel0n committed
248

249
        # perform database startup actions
250
        for db in [self.main_db, self.cache_db]:
251
252
253
254
255
256
257
258
259
            # initialize database
            db.initialize()

            # check integrity of database
            db.check_integrity()

            # migrate database
            db.migrate()

260
261
262
            # misc database cleanups
            db.cleanup()

263
264
265
            # upgrade database
            db.upgrade()

266
        # compact main database
267
        if not self.developer and self.config.last_db_compact < time.time() - 604800:  # 7 days
268
            self.main_db.compact()
269
            self.config.last_db_compact = int(time.time())
270

271
        # load name cache
272
        self.name_cache.load()
273

echel0n's avatar
echel0n committed
274
275
        # load data for shows from database
        self.load_shows()
276

277
        if self.config.default_page not in ('schedule', 'history', 'IRC'):
278
            self.config.default_page = 'home'
echel0n's avatar
echel0n committed
279

280
        # cleanup cache folder
echel0n's avatar
echel0n committed
281
        for folder in ['mako', 'sessions', 'indexers']:
282
            try:
283
                shutil.rmtree(os.path.join(sickrage.app.cache_dir, folder), ignore_errors=True)
284
285
            except Exception:
                continue
echel0n's avatar
echel0n committed
286

287
        # init anidb connection
288
        if self.config.use_anidb:
289
            def anidb_logger(msg):
290
                return self.log.debug("AniDB: {} ".format(msg))
291

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

298
299
        if self.config.web_port < 21 or self.config.web_port > 65535:
            self.config.web_port = 8081
echel0n's avatar
echel0n committed
300

301
        if not self.config.web_cookie_secret:
echel0n's avatar
echel0n committed
302
            self.config.web_cookie_secret = generate_secret()
echel0n's avatar
echel0n committed
303
304

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

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

311
        self.config.naming_force_folders = check_force_season_folders()
312

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

316
        if self.config.torrent_method not in ('blackhole', 'utorrent', 'transmission', 'deluge', 'deluged',
317
                                              'download_station', 'rtorrent', 'qbittorrent', 'mlnet', 'putio'):
318
319
320
321
322
323
            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
324
        self.config.min_backlog_searcher_freq = get_backlog_cycle_time()
325
326
327
328
329
330
        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
331
332
        if self.config.failed_snatch_age < self.config.min_failed_snatch_age:
            self.config.failed_snatch_age = self.config.min_failed_snatch_age
333
334
335
336
337
338
        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 = []
echel0n's avatar
echel0n committed
339

340
        # add version checker job
341
342
        self.scheduler.add_job(
            self.version_updater.run,
echel0n's avatar
echel0n committed
343
            IntervalTrigger(
344
                hours=self.config.version_updater_freq
echel0n's avatar
echel0n committed
345
            ),
346
347
            name=self.version_updater.name,
            id=self.version_updater.name
echel0n's avatar
echel0n committed
348
349
        )

350
        # add network timezones updater job
351
        self.scheduler.add_job(
echel0n's avatar
echel0n committed
352
            update_network_dict,
echel0n's avatar
echel0n committed
353
354
355
            IntervalTrigger(
                days=1
            ),
echel0n's avatar
echel0n committed
356
            name="TZUPDATER",
357
            id="TZUPDATER"
echel0n's avatar
echel0n committed
358
359
        )

360
        # add show updater job
361
362
        self.scheduler.add_job(
            self.show_updater.run,
echel0n's avatar
echel0n committed
363
364
            IntervalTrigger(
                days=1,
365
                start_date=datetime.datetime.now().replace(hour=self.config.showupdate_hour)
echel0n's avatar
echel0n committed
366
            ),
367
368
            name=self.show_updater.name,
            id=self.show_updater.name
echel0n's avatar
echel0n committed
369
370
        )

371
        # add daily search job
372
373
        self.scheduler.add_job(
            self.daily_searcher.run,
echel0n's avatar
echel0n committed
374
            IntervalTrigger(
375
                minutes=self.config.daily_searcher_freq,
376
                start_date=datetime.datetime.now() + datetime.timedelta(minutes=4)
echel0n's avatar
echel0n committed
377
            ),
378
379
380
381
            name=self.daily_searcher.name,
            id=self.daily_searcher.name
        )

echel0n's avatar
echel0n committed
382
        # add failed snatch search job
383
        self.scheduler.add_job(
echel0n's avatar
echel0n committed
384
            self.failed_snatch_searcher.run,
385
386
387
388
            IntervalTrigger(
                hours=1,
                start_date=datetime.datetime.now() + datetime.timedelta(minutes=4)
            ),
echel0n's avatar
echel0n committed
389
390
            name=self.failed_snatch_searcher.name,
            id=self.failed_snatch_searcher.name
echel0n's avatar
echel0n committed
391
392
        )

393
        # add backlog search job
394
395
        self.scheduler.add_job(
            self.backlog_searcher.run,
echel0n's avatar
echel0n committed
396
            IntervalTrigger(
397
                minutes=self.config.backlog_searcher_freq,
398
                start_date=datetime.datetime.now() + datetime.timedelta(minutes=30)
echel0n's avatar
echel0n committed
399
            ),
400
401
            name=self.backlog_searcher.name,
            id=self.backlog_searcher.name
echel0n's avatar
echel0n committed
402
403
        )

404
        # add auto-postprocessing job
405
406
        self.scheduler.add_job(
            self.auto_postprocessor.run,
echel0n's avatar
echel0n committed
407
            IntervalTrigger(
408
                minutes=self.config.autopostprocessor_freq
echel0n's avatar
echel0n committed
409
            ),
410
411
            name=self.auto_postprocessor.name,
            id=self.auto_postprocessor.name
echel0n's avatar
echel0n committed
412
413
        )

414
        # add find proper job
415
416
        self.scheduler.add_job(
            self.proper_searcher.run,
echel0n's avatar
echel0n committed
417
418
            IntervalTrigger(
                minutes={'15m': 15, '45m': 45, '90m': 90, '4h': 4 * 60, 'daily': 24 * 60}[
419
                    self.config.proper_searcher_interval]
echel0n's avatar
echel0n committed
420
            ),
421
422
            name=self.proper_searcher.name,
            id=self.proper_searcher.name
echel0n's avatar
echel0n committed
423
424
        )

425
        # add trakt.tv checker job
426
427
        self.scheduler.add_job(
            self.trakt_searcher.run,
echel0n's avatar
echel0n committed
428
429
430
            IntervalTrigger(
                hours=1
            ),
431
432
            name=self.trakt_searcher.name,
            id=self.trakt_searcher.name
echel0n's avatar
echel0n committed
433
434
        )

435
        # add subtitles finder job
436
437
        self.scheduler.add_job(
            self.subtitle_searcher.run,
echel0n's avatar
echel0n committed
438
            IntervalTrigger(
439
                hours=self.config.subtitle_searcher_freq
echel0n's avatar
echel0n committed
440
            ),
441
442
            name=self.subtitle_searcher.name,
            id=self.subtitle_searcher.name
echel0n's avatar
echel0n committed
443
444
        )

445
446
447
448
449
450
451
452
453
454
        # 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
        )

455
        # start scheduler service
456
        self.scheduler.start()
457

echel0n's avatar
echel0n committed
458
        # start queue's
459
460
461
        self.search_queue.start()
        self.show_queue.start()
        self.postprocessor_queue.start()
echel0n's avatar
echel0n committed
462

463
        # start webserver
464
        self.wserver.start()
465

echel0n's avatar
echel0n committed
466
467
468
        # start ioloop
        self.io_loop.start()

469
    def shutdown(self, restart=False):
470
        if self.started:
471
            self.log.info('SiCKRAGE IS SHUTTING DOWN!!!')
472

echel0n's avatar
echel0n committed
473
            # shutdown webserver
echel0n's avatar
echel0n committed
474
475
            if self.wserver:
                self.wserver.shutdown()
echel0n's avatar
echel0n committed
476

echel0n's avatar
echel0n committed
477
            # shutdown show queue
478
479
480
481
            if self.show_queue:
                self.log.debug("Shutting down show queue")
                self.show_queue.shutdown()
                del self.show_queue
echel0n's avatar
echel0n committed
482

echel0n's avatar
echel0n committed
483
            # shutdown search queue
484
485
486
487
            if self.search_queue:
                self.log.debug("Shutting down search queue")
                self.search_queue.shutdown()
                del self.search_queue
488

echel0n's avatar
echel0n committed
489
            # shutdown post-processor queue
490
491
492
493
            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
494

echel0n's avatar
echel0n committed
495
            # log out of ADBA
496
497
498
            if self.adba_connection:
                self.log.debug("Shutting down ANIDB connection")
                self.adba_connection.stop()
echel0n's avatar
echel0n committed
499

echel0n's avatar
echel0n committed
500
501
            # save all show and config settings
            self.save_all()
502

503
504
505
506
507
            # 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()
508

echel0n's avatar
echel0n committed
509
            # shutdown logging
510
511
            if self.log:
                self.log.close()
512

echel0n's avatar
echel0n committed
513
514
        if restart:
            os.execl(sys.executable, sys.executable, *sys.argv)
515
516

        if sickrage.app.daemon:
517
            sickrage.app.daemon.stop()
echel0n's avatar
echel0n committed
518

519
        self.started = False
520

echel0n's avatar
echel0n committed
521
522
        self.io_loop.stop()

echel0n's avatar
echel0n committed
523
524
    def save_all(self):
        # write all shows
525
        self.log.info("Saving all shows to the database")
526
        for show in self.showlist:
527
            try:
528
                show.saveToDB()
529
            except Exception:
530
                continue
echel0n's avatar
echel0n committed
531
532

        # save config
533
        self.config.save()
534

echel0n's avatar
echel0n committed
535
536
    def load_shows(self):
        """
537
        Populates the showlist and quicksearch cache with shows and episodes from the database
echel0n's avatar
echel0n committed
538
539
        """

540
541
        self.quicksearch_cache.load()

542
        for dbData in self.main_db.all('tv_shows'):
echel0n's avatar
echel0n committed
543
            try:
544
                self.log.debug("Loading data for show: [{}]".format(dbData['show_name']))
545
546
                self.showlist.append(TVShow(int(dbData['indexer']), int(dbData['indexer_id'])))
                self.quicksearch_cache.add_show(dbData['indexer_id'])
echel0n's avatar
echel0n committed
547
            except Exception as e:
548
                self.log.debug("Show error in [%s]: %s" % (dbData['location'], str(e)))