show.py 26.9 KB
Newer Older
echel0n's avatar
echel0n committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# ##############################################################################
#  Author: echel0n <[email protected]>
#  URL: https://sickrage.ca/
#  Git: https://git.sickrage.ca/SiCKRAGE/sickrage.git
#  -
#  This file is part of SiCKRAGE.
#  -
#  SiCKRAGE is free software: you can redistribute it and/or modify
#  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.
#  -
#  SiCKRAGE is distributed in the hope that it will be useful,
#  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
#  along with SiCKRAGE.  If not, see <http://www.gnu.org/licenses/>.
# ##############################################################################
21

22

23
import datetime
24
import os
echel0n's avatar
echel0n committed
25
import time
26
import traceback
27
from enum import Enum
28

29
import sickrage
30
from sickrage.core.common import WANTED
31
from sickrage.core.exceptions import CantRefreshShowException, CantRemoveShowException, CantUpdateShowException, EpisodeDeletedException, \
32
    MultipleShowObjectsException
33
from sickrage.core.queues import Queue, Task, TaskPriority
34
from sickrage.core.scene_numbering import xem_refresh
35
from sickrage.core.traktapi import TraktAPI
36
from sickrage.core.tv.show import TVShow
37
from sickrage.core.tv.show.helpers import find_show
38
from sickrage.core.websocket import WebSocketMessage
39
from sickrage.indexers import IndexerApi
40
from sickrage.indexers.exceptions import indexer_attributenotfound, indexer_exception
41
42


43
class ShowQueue(Queue):
44
    def __init__(self):
45
        Queue.__init__(self, "SHOWQUEUE")
46
47

    @property
echel0n's avatar
echel0n committed
48
    def loading_show_list(self):
49
        return [x.indexer_id for x in self.tasks.copy().values() if x.is_loading]
50

51
    def _is_in_queue(self, indexer_id, actions):
52
        for task in self.tasks.copy().values():
53
            if task.is_queued() and task.indexer_id == indexer_id and task.action in actions:
54
55
56
                return True

        return False
57

58
    def _is_being(self, indexer_id, actions):
59
        for task in self.tasks.copy().values():
60
            if task.is_started() and task.indexer_id == indexer_id and task.action in actions:
61
62
63
                return True

        return False
64

65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
    def is_queued_to_remove(self, indexer_id):
        return self._is_in_queue(indexer_id, [ShowTaskActions.REMOVE])

    def is_queued_to_add(self, indexer_id):
        return self._is_in_queue(indexer_id, [ShowTaskActions.ADD])

    def is_queued_to_update(self, indexer_id):
        return self._is_in_queue(indexer_id, [ShowTaskActions.UPDATE, ShowTaskActions.FORCEUPDATE])

    def is_queued_to_refresh(self, indexer_id):
        return self._is_in_queue(indexer_id, [ShowTaskActions.REFRESH])

    def is_queued_to_rename(self, indexer_id):
        return self._is_in_queue(indexer_id, [ShowTaskActions.RENAME])

    def is_queued_to_subtitle(self, indexer_id):
        return self._is_in_queue(indexer_id, [ShowTaskActions.SUBTITLE])

83
    def is_being_removed(self, indexer_id):
84
        return self._is_being(indexer_id, [ShowTaskActions.REMOVE])
85

86
    def is_being_added(self, indexer_id):
87
        return self._is_being(indexer_id, [ShowTaskActions.ADD])
88

89
    def is_being_updated(self, indexer_id):
90
        return self._is_being(indexer_id, [ShowTaskActions.UPDATE, ShowTaskActions.FORCEUPDATE])
91

92
    def is_being_refreshed(self, indexer_id):
93
        return self._is_being(indexer_id, [ShowTaskActions.REFRESH])
94

95
    def is_being_renamed(self, indexer_id):
96
        return self._is_being(indexer_id, [ShowTaskActions.RENAME])
echel0n's avatar
echel0n committed
97

98
    def is_being_subtitled(self, indexer_id):
99
        return self._is_being(indexer_id, [ShowTaskActions.SUBTITLE])
100

101
102
    def update_show(self, indexer_id, indexer_update_only=False, force=False):
        show_obj = find_show(indexer_id)
103

104
105
        if self.is_being_added(indexer_id):
            raise CantUpdateShowException("{} is still being added, please wait until it is finished before trying to update.".format(show_obj.name))
106

107
108
109
        if self.is_being_updated(indexer_id):
            raise CantUpdateShowException("{} is already being updated, can't update again until it's done.".format(show_obj.name))

110
        task_id = self.put(ShowTaskUpdate(indexer_id, indexer_update_only, force))
111
112

        if not indexer_update_only:
113
            self.put(ShowTaskRefresh(indexer_id, force=force), depend=[task_id])
114

115
116
    def refresh_show(self, indexer_id, force=False):
        show_obj = find_show(indexer_id)
117

118
        if (self.is_being_refreshed(indexer_id) or self.is_being_refreshed(indexer_id)) and not force:
119
            raise CantRefreshShowException("This show is already being refreshed or queued to be refreshed, skipping this request.")
120

121
122
        if show_obj.paused and not force:
            sickrage.app.log.debug('Skipping show [{}] because it is paused.'.format(show_obj.name))
123
            return
124

125
        sickrage.app.log.debug("Queueing show refresh for {}".format(show_obj.name))
126

127
        self.put(ShowTaskRefresh(indexer_id, force=force))
128

129
    def rename_show_episodes(self, indexer_id):
130
        self.put(ShowTaskRename(indexer_id))
echel0n's avatar
echel0n committed
131

132
    def download_subtitles(self, indexer_id):
133
        self.put(ShowTaskSubtitle(indexer_id))
134

135
    def add_show(self, indexer, indexer_id, showDir, default_status=None, quality=None, flatten_folders=None, lang=None, subtitles=None,
136
                 sub_use_sr_metadata=None, anime=None, search_format=None, dvdorder=None, paused=None, blacklist=None, whitelist=None,
137
                 default_status_after=None, scene=None, skip_downloaded=None):
138

139
        if lang is None:
140
            lang = sickrage.app.config.indexer_default_language
141

142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
        self.put(ShowTaskAdd(indexer=indexer,
                             indexer_id=indexer_id,
                             showDir=showDir,
                             default_status=default_status,
                             quality=quality,
                             flatten_folders=not flatten_folders,
                             lang=lang,
                             subtitles=subtitles,
                             sub_use_sr_metadata=sub_use_sr_metadata,
                             anime=anime,
                             dvdorder=dvdorder,
                             search_format=search_format,
                             paused=paused,
                             blacklist=blacklist,
                             whitelist=whitelist,
                             default_status_after=default_status_after,
158
                             scene=scene,
159
                             skip_downloaded=skip_downloaded))
160

161
162
163
164
    def remove_show(self, indexer_id, full=False):
        show_obj = find_show(indexer_id)

        if not show_obj:
echel0n's avatar
echel0n committed
165
            raise CantRemoveShowException('Failed removing show: Show does not exist')
166
        elif not hasattr(show_obj, 'indexer_id'):
echel0n's avatar
echel0n committed
167
            raise CantRemoveShowException('Failed removing show: Show does not have an indexer id')
168
        elif self._is_being(show_obj.indexer_id, (ShowTaskActions.REMOVE,)):
169
            raise CantRemoveShowException("{} is already queued to be removed".format(show_obj))
Dustyn Gibson's avatar
Dustyn Gibson committed
170
171

        # remove other queued actions for this show.
172
173
174
        self.remove_task(indexer_id)

        self.put(ShowTaskForceRemove(indexer_id=indexer_id, full=full))
echel0n's avatar
echel0n committed
175

176

177
178
179
180
181
182
183
184
class ShowTaskActions(Enum):
    REFRESH = 'Refresh'
    ADD = 'Add'
    UPDATE = 'Update'
    FORCEUPDATE = 'Force Update'
    RENAME = 'Rename'
    SUBTITLE = 'Subtitle'
    REMOVE = 'Remove Show'
echel0n's avatar
echel0n committed
185

186

187
class ShowTask(Task):
188
189
190
191
192
193
194
195
196
197
    """
    Represents an item in the queue waiting to be executed

    Can be either:
    - show being added (may or may not be associated with a show object)
    - show being refreshed
    - show being updated
    - show being force updated
    - show being subtitled
    """
echel0n's avatar
echel0n committed
198

199
200
    def __init__(self, indexer_id, action):
        super(ShowTask, self).__init__(action.value, action)
201
        self.indexer_id = indexer_id
echel0n's avatar
echel0n committed
202

echel0n's avatar
echel0n committed
203
    def is_in_queue(self):
204
        return self in sickrage.app.show_queue.queue
205

206
207
    @property
    def show_name(self):
echel0n's avatar
echel0n committed
208
209
        show_obj = find_show(self.indexer_id)
        return show_obj.name if show_obj else str(self.indexer_id)
210

211
    @property
echel0n's avatar
echel0n committed
212
    def is_loading(self):
213
214
        return False

215
216
217
218
    def run(self):
        show_obj = find_show(self.indexer_id)
        if show_obj:
            WebSocketMessage('SHOW_QUEUE_STATUS_UPDATED', {'series_id': show_obj.indexer_id, 'show_queue_status': show_obj.show_queue_status}).push()
219
220
        else:
            WebSocketMessage('SHOW_QUEUE_STATUS_UPDATED', {'series_id': self.indexer_id, 'action': self.action.name}).push()
221
222
223
224
225

    def finish(self):
        show_obj = find_show(self.indexer_id)
        if show_obj:
            WebSocketMessage('SHOW_QUEUE_STATUS_UPDATED', {'series_id': show_obj.indexer_id, 'show_queue_status': show_obj.show_queue_status}).push()
226
227
        else:
            WebSocketMessage('SHOW_QUEUE_STATUS_UPDATED', {'series_id': self.indexer_id, 'action': self.action.name}).push()
228

229

230
class ShowTaskAdd(ShowTask):
231
    def __init__(self, indexer, indexer_id, showDir, default_status, quality, flatten_folders, lang, subtitles, sub_use_sr_metadata, anime,
232
                 dvdorder, search_format, paused, blacklist, whitelist, default_status_after, scene, skip_downloaded):
233
        super(ShowTaskAdd, self).__init__(None, ShowTaskActions.ADD)
234

235
236
237
238
239
240
241
242
        self.indexer = indexer
        self.indexer_id = indexer_id
        self.showDir = showDir
        self.default_status = default_status
        self.quality = quality
        self.flatten_folders = flatten_folders
        self.lang = lang
        self.subtitles = subtitles
243
        self.sub_use_sr_metadata = sub_use_sr_metadata
244
        self.anime = anime
245
        self.search_format = search_format
246
        self.dvdorder = dvdorder
247
        self.paused = paused
248
249
        self.blacklist = blacklist
        self.whitelist = whitelist
250
        self.default_status_after = default_status_after
251
        self.scene = scene
252
        self.skip_downloaded = skip_downloaded
253
        self.priority = TaskPriority.HIGH
254

255
256
    @property
    def show_name(self):
257
258
259
260
        """
        Returns the show name if there is a show object created, if not returns
        the dir that the show is being added to.
        """
261
262
263

        show_obj = find_show(self.indexer_id)
        return show_obj.name if show_obj else os.path.basename(self.showDir)
264

265
    @property
echel0n's avatar
echel0n committed
266
    def is_loading(self):
267
268
269
270
        """
        Returns True if we've gotten far enough to have a show object, or False
        if we still only know the folder name.
        """
271
        if find_show(self.indexer_id):
272
273
            return True

274
    def run(self):
275
276
        super(ShowTaskAdd, self).run()

echel0n's avatar
echel0n committed
277
278
        start_time = time.time()

279
        sickrage.app.log.info("Started adding show {} from show dir: {}".format(self.show_name, self.showDir))
280

281
        index_name = IndexerApi(self.indexer).name
282

echel0n's avatar
echel0n committed
283
        # make sure the Indexer IDs are valid
284
285
286
287
288
        lINDEXER_API_PARMS = IndexerApi(self.indexer).api_params.copy()
        lINDEXER_API_PARMS['cache'] = False
        lINDEXER_API_PARMS['language'] = self.lang or sickrage.app.config.indexer_default_language

        sickrage.app.log.info("{}: {}".format(index_name, repr(lINDEXER_API_PARMS)))
echel0n's avatar
echel0n committed
289

290
        t = IndexerApi(self.indexer).indexer(**lINDEXER_API_PARMS)
291

292
293
        s = t[self.indexer_id]
        if not s:
294
            sickrage.app.alerts.error(
echel0n's avatar
echel0n committed
295
296
297
                _("Unable to add show"),
                _("Unable to look up the show in {} on {} using ID {}, not using the NFO. Delete .nfo and try adding "
                  "manually again.").format(self.showDir, index_name, self.indexer_id)
298
            )
299

300
            if sickrage.app.config.use_trakt:
301
                title = self.showDir.split("/")[-1]
echel0n's avatar
echel0n committed
302

303
304
305
306
                data = {
                    'shows': [
                        {
                            'title': title,
307
                            'ids': {IndexerApi(self.indexer).trakt_id: self.indexer_id}
308
309
310
311
                        }
                    ]
                }

312
                TraktAPI()["sync/watchlist"].remove(data)
313

echel0n's avatar
echel0n committed
314
            return self._finish_early()
315

316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
        # this usually only happens if they have an NFO in their show dir which gave us a Indexer ID that has no
        # proper english version of the show
        try:
            s.seriesname
        except AttributeError:
            sickrage.app.log.warning("Show in {} has no name on {}, probably the wrong language used to search with".format(self.showDir, index_name))
            sickrage.app.alerts.error(_("Unable to add show"),
                                      _("Show in {} has no name on {}, probably the wrong language. Delete .nfo "
                                        "and add manually in the correct language").format(self.showDir, index_name))
            return self._finish_early()

        # if the show has no episodes/seasons
        if not len(s):
            sickrage.app.log.warning("Show " + str(s['seriesname']) + " is on " + str(IndexerApi(self.indexer).name) + "but contains no season/episode "
                                                                                                                       "data.")
            sickrage.app.alerts.error(_("Unable to add show"),
                                      _("Show ") + str(s['seriesname']) + _(" is on ") + str(IndexerApi(self.indexer).name) + _(
                                          " but contains no season/episode data."))
            return self._finish_early()

336
        try:
337
338
339
            # add show to database
            show_obj = TVShow(self.indexer_id, self.indexer, lang=self.lang, location=self.showDir)

340
            # set up initial values
341
342
343
344
345
346
347
348
349
350
            show_obj.subtitles = self.subtitles if self.subtitles is not None else sickrage.app.config.subtitles_default
            show_obj.sub_use_sr_metadata = self.sub_use_sr_metadata if self.sub_use_sr_metadata is not None else False
            show_obj.quality = self.quality if self.quality is not None else sickrage.app.config.quality_default
            show_obj.flatten_folders = self.flatten_folders if self.flatten_folders is not None else sickrage.app.config.flatten_folders_default
            show_obj.scene = self.scene if self.scene is not None else sickrage.app.config.scene_default
            show_obj.anime = self.anime if self.anime is not None else sickrage.app.config.anime_default
            show_obj.dvdorder = self.dvdorder if self.dvdorder is not None else False
            show_obj.search_format = self.search_format if self.search_format is not None else sickrage.app.config.search_format_default
            show_obj.skip_downloaded = self.skip_downloaded if self.skip_downloaded is not None else sickrage.app.config.skip_downloaded_default
            show_obj.paused = self.paused if self.paused is not None else False
echel0n's avatar
echel0n committed
351

352
            # set up default new/missing episode status
353
            sickrage.app.log.info("Setting all current episodes to the specified default status: " + str(self.default_status))
354
            show_obj.default_ep_status = self.default_status
355

356
            # save to database
357
            show_obj.save()
358

359
            if show_obj.anime:
360
                if self.blacklist:
361
                    show_obj.release_groups.set_black_keywords(self.blacklist)
362
                if self.whitelist:
363
                    show_obj.release_groups.set_white_keywords(self.whitelist)
364
        except indexer_exception as e:
365
            sickrage.app.log.warning(_("Unable to add show due to an error with ") + IndexerApi(self.indexer).name + ": {}".format(e))
366
            sickrage.app.alerts.error(_("Unable to add show due to an error with ") + IndexerApi(self.indexer).name + "")
echel0n's avatar
echel0n committed
367
            return self._finish_early()
368
        except MultipleShowObjectsException:
369
            sickrage.app.log.warning(_("The show in ") + self.showDir + _(" is already in your show list, skipping"))
370
            sickrage.app.alerts.error(_('Show skipped'),
371
                                      _("The show in ") + self.showDir + _(" is already in your show list"))
echel0n's avatar
echel0n committed
372
            return self._finish_early()
373
        except Exception as e:
374
            sickrage.app.log.error(_("Error trying to add show: {}").format(e))
375
            sickrage.app.log.debug(traceback.format_exc())
echel0n's avatar
echel0n committed
376
            raise self._finish_early()
377
378

        try:
379
            sickrage.app.log.debug(_("Attempting to retrieve show info from IMDb"))
380
            show_obj.load_imdb_info()
381
        except Exception as e:
echel0n's avatar
echel0n committed
382
383
            sickrage.app.log.debug(_("Error loading IMDb info: {}").format(e))
            sickrage.app.log.debug(traceback.format_exc())
echel0n's avatar
echel0n committed
384

385
        try:
386
            show_obj.load_episodes_from_indexer()
387
        except Exception as e:
echel0n's avatar
echel0n committed
388
            sickrage.app.log.debug(_("Error with ") + IndexerApi(show_obj.indexer).name + _(", not creating episode list: {}").format(e))
389
            sickrage.app.log.debug(traceback.format_exc())
390
391

        try:
392
            show_obj.load_episodes_from_dir()
393
        except Exception as e:
echel0n's avatar
echel0n committed
394
            sickrage.app.log.debug("Error searching dir for episodes: {}".format(e))
395
            sickrage.app.log.debug(traceback.format_exc())
396

397
398
        show_obj.write_metadata(force=True)
        show_obj.populate_cache()
399

400
        if sickrage.app.config.use_trakt:
echel0n's avatar
echel0n committed
401
            # if there are specific episodes that need to be added by trakt
402
            sickrage.app.trakt_searcher.manage_new_show(show_obj)
echel0n's avatar
echel0n committed
403
404

            # add show to trakt.tv library
405
            if sickrage.app.config.trakt_sync:
406
                sickrage.app.trakt_searcher.add_show_to_trakt_library(show_obj)
407

408
            if sickrage.app.config.trakt_sync_watchlist:
409
                sickrage.app.log.info("update watchlist")
410
                sickrage.app.notifier_providers['trakt'].update_watchlist(show_obj)
Giovanni's avatar
Giovanni committed
411

412
413
414
        # Retrieve scene exceptions
        show_obj.retrieve_scene_exceptions()

echel0n's avatar
echel0n committed
415
        # Load XEM data to DB for show
416
        xem_refresh(show_obj.indexer_id, show_obj.indexer, force=True)
echel0n's avatar
echel0n committed
417

418
        # check if show has XEM mapping so we can determine if searches should go by scene numbering or indexer
echel0n's avatar
echel0n committed
419
        # numbering.
420
421
        # if not self.scene and get_xem_numbering_for_show(show_obj.indexer_id, show_obj.indexer):
        #     show_obj.scene = 1
echel0n's avatar
echel0n committed
422

423
424
425
        # if they set default ep status to WANTED then run the backlog to search for episodes
        if show_obj.default_ep_status == WANTED:
            sickrage.app.log.info(_("Launching backlog for this show since it has episodes that are WANTED"))
426
            sickrage.app.backlog_searcher.search_backlog(show_obj.indexer_id)
427

428
        show_obj.default_ep_status = self.default_status_after
429
430

        show_obj.save()
431

432
433
434
435
        WebSocketMessage('SHOW_ADDED',
                         {'series_id': show_obj.indexer_id,
                          'series': show_obj.to_json(progress=True)}).push()

436
        sickrage.app.log.info("Finished adding show {} in {}s from show dir: {}".format(self.show_name, round(time.time() - start_time, 2), self.showDir))
437

echel0n's avatar
echel0n committed
438
    def _finish_early(self):
439
440
441
442
        try:
            sickrage.app.show_queue.remove_show(self.indexer_id)
        except CantRemoveShowException:
            pass
443

444

445
class ShowTaskRefresh(ShowTask):
446
    def __init__(self, indexer_id=None, force=False):
447
        super(ShowTaskRefresh, self).__init__(indexer_id, ShowTaskActions.REFRESH)
448

449
450
451
        # force refresh certain items
        self.force = force

452
    def run(self):
453
454
        super(ShowTaskRefresh, self).run()

echel0n's avatar
echel0n committed
455
456
        start_time = time.time()

457
        tv_show = find_show(self.indexer_id)
458

459
        sickrage.app.log.info("Performing refresh for show: {}".format(tv_show.name))
460

461
462
463
        tv_show.refresh_dir()
        tv_show.write_metadata(force=self.force)
        tv_show.populate_cache(force=self.force)
464

465
        # Load XEM data to DB for show
466
        # xem_refresh(show.indexer_id, show.indexer)
467

468
469
        tv_show.last_refresh = datetime.date.today().toordinal()

470
        tv_show.save()
471

472
473
474
475
        WebSocketMessage('SHOW_REFRESHED',
                         {'series_id': tv_show.indexer_id,
                          'series': tv_show.to_json(episodes=True, details=True)}).push()

476
        sickrage.app.log.info("Finished refresh in {}s for show: {}".format(round(time.time() - start_time, 2), tv_show.name))
477

478

479
class ShowTaskRename(ShowTask):
480
    def __init__(self, indexer_id=None):
481
        super(ShowTaskRename, self).__init__(indexer_id, ShowTaskActions.RENAME)
482

483
    def run(self):
484
485
486
        super(ShowTaskRename, self).run()

        tv_show = find_show(self.indexer_id)
487

488
        sickrage.app.log.info("Performing renames for show: {}".format(tv_show.name))
489

490
491
        if not os.path.isdir(tv_show.location):
            sickrage.app.log.warning("Can't perform rename on " + tv_show.name + " when the show dir is missing.")
492
493
494
495
            return

        ep_obj_rename_list = []

496
        for cur_ep_obj in (x for x in tv_show.episodes if x.location):
497
498
            # Only want to rename if we have a location
            if cur_ep_obj.location:
499
                if cur_ep_obj.related_episodes:
500
501
                    # do we have one of multi-episodes in the rename list already
                    have_already = False
502
                    for cur_related_ep in cur_ep_obj.related_episodes + [cur_ep_obj]:
503
504
505
506
507
508
509
510
511
512
513
                        if cur_related_ep in ep_obj_rename_list:
                            have_already = True
                            break
                    if not have_already:
                        ep_obj_rename_list.append(cur_ep_obj)
                else:
                    ep_obj_rename_list.append(cur_ep_obj)

        for cur_ep_obj in ep_obj_rename_list:
            cur_ep_obj.rename()

514
515
516
517
518
        WebSocketMessage('SHOW_RENAMED',
                         {'series_id': tv_show.indexer_id,
                          'series': tv_show.to_json(episodes=True, details=True)}).push()

        sickrage.app.log.info("Finished renames for show: {}".format(tv_show.name))
519

520

521
class ShowTaskSubtitle(ShowTask):
522
    def __init__(self, indexer_id=None):
523
        super(ShowTaskSubtitle, self).__init__(indexer_id, ShowTaskActions.SUBTITLE)
524

525
    def run(self):
526
527
528
529
530
        super(ShowTaskSubtitle, self).run()

        tv_show = find_show(self.indexer_id)

        sickrage.app.log.info("Started downloading subtitles for show: {}".format(tv_show.name))
531

532
        tv_show.download_subtitles()
533

534
535
536
        WebSocketMessage('SHOW_SUBTITLED',
                         {'series_id': tv_show.indexer_id,
                          'series': tv_show.to_json(episodes=True, details=True)}).push()
537

538
        sickrage.app.log.info("Finished downloading subtitles for show: {}".format(tv_show.name))
539

540

541
class ShowTaskUpdate(ShowTask):
542
543
    def __init__(self, indexer_id=None, indexer_update_only=False, force=False, action=ShowTaskActions.UPDATE):
        super(ShowTaskUpdate, self).__init__(indexer_id, action if not force else ShowTaskActions.FORCEUPDATE)
544
        self.indexer_update_only = indexer_update_only
545
        self.force = force
546

547
    def run(self):
548
549
        super(ShowTaskUpdate, self).run()

550
        show_obj = find_show(self.indexer_id)
551

echel0n's avatar
echel0n committed
552
553
        start_time = time.time()

554
        sickrage.app.log.info("Performing updates for show: {}".format(show_obj.name))
555
556

        try:
557
558
            sickrage.app.log.debug("Retrieving show info from " + IndexerApi(show_obj.indexer).name + "")
            show_obj.load_from_indexer(cache=False)
559
        except indexer_attributenotfound as e:
560
            sickrage.app.log.warning("Data retrieved from " + IndexerApi(show_obj.indexer).name + " was incomplete, aborting: {}".format(e))
561
            return
562
563
564
        except indexer_exception as e:
            sickrage.app.log.warning("Unable to contact " + IndexerApi(show_obj.indexer).name + ", aborting: {}".format(e))
            return
565
566

        try:
567
568
            if not self.indexer_update_only:
                sickrage.app.log.debug("Attempting to retrieve show info from IMDb")
569
                show_obj.load_imdb_info()
570
        except Exception as e:
571
            sickrage.app.log.warning("Error loading IMDb info for {}: {}".format(IndexerApi(show_obj.indexer).name, e))
echel0n's avatar
echel0n committed
572

573
574
        # get episodes from database
        db_episodes = {}
575
        for data in show_obj.episodes:
576
577
578
579
580
            if data.season not in db_episodes:
                db_episodes[data.season] = {}
            db_episodes[data.season].update({data.episode: True})

        # get episodes from indexers
581
        try:
582
            indexer_episodes = show_obj.load_episodes_from_indexer()
583
        except indexer_exception as e:
echel0n's avatar
echel0n committed
584
            sickrage.app.log.warning("Unable to get info from " + IndexerApi(show_obj.indexer).name + ", the show info will not be refreshed: {}".format(e))
585
            indexer_episodes = None
586

587
        if not indexer_episodes:
echel0n's avatar
echel0n committed
588
            sickrage.app.log.warning("No data returned from " + IndexerApi(show_obj.indexer).name + ", unable to update this show")
589
        else:
echel0n's avatar
echel0n committed
590
            # for each ep we found on indexer delete it from the DB list
591
592
593
594
            for curSeason in indexer_episodes:
                for curEpisode in indexer_episodes[curSeason]:
                    if curSeason in db_episodes and curEpisode in db_episodes[curSeason]:
                        del db_episodes[curSeason][curEpisode]
595

596
            # remaining episodes in the DB list are not on the indexer, just delete them from the DB
597
598
            for curSeason in db_episodes:
                for curEpisode in db_episodes[curSeason]:
599
                    sickrage.app.log.info("Permanently deleting episode " + str(curSeason) + "x" + str(curEpisode) + " from the database")
600
601
602
603
                    try:
                        show_obj.delete_episode(curSeason, curEpisode)
                    except EpisodeDeletedException:
                        continue
604

605
606
        show_obj.retrieve_scene_exceptions()

607
608
609
        WebSocketMessage('SHOW_UPDATED',
                         {'series_id': show_obj.indexer_id,
                          'series': show_obj.to_json(episodes=True, details=True)}).push()
610

611
        sickrage.app.log.info("Finished updates in {}s for show: {}".format(round(time.time() - start_time, 2), show_obj.name))
Dustyn Gibson's avatar
Dustyn Gibson committed
612

613

614
class ShowTaskForceRemove(ShowTask):
615
    def __init__(self, indexer_id=None, full=False):
616
        super(ShowTaskForceRemove, self).__init__(indexer_id, ShowTaskActions.REMOVE)
617
618

        # lets make sure this happens before any other high priority actions
619
        self.priority = TaskPriority.EXTREME
Dustyn Gibson's avatar
Dustyn Gibson committed
620
621
        self.full = full

622
    @property
echel0n's avatar
echel0n committed
623
    def is_loading(self):
624
625
626
627
628
        """
        Returns false cause we are removing the show.
        """
        return False

629
    def run(self):
630
631
        super(ShowTaskForceRemove, self).run()

632
        show_obj = find_show(self.indexer_id)
633
634

        sickrage.app.log.info("Removing show: {}".format(show_obj.name))
635

636
        show_obj.delete_show(full=self.full)
637

638
        if sickrage.app.config.use_trakt:
639
            try:
640
                sickrage.app.trakt_searcher.remove_show_from_trakt_library(show_obj)
641
            except Exception as e:
642
                sickrage.app.log.warning("Unable to delete show from Trakt: %s. Error: %s" % (show_obj.name, e))
643

644
645
646
        WebSocketMessage('SHOW_REMOVED',
                         {'series_id': show_obj.indexer_id}).push()

647
        sickrage.app.log.info("Finished removing show: {}".format(show_obj.name))