show.py 24.2 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, EpisodeNotFoundException
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.indexers import IndexerApi
39
from sickrage.indexers.exceptions import indexer_attributenotfound, indexer_exception
40
41


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

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

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

        return False
56

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

        return False
63

64
    def is_being_removed(self, indexer_id):
65
        return self._is_being(indexer_id, [ShowTaskActions.REMOVE])
66

67
    def is_being_added(self, indexer_id):
68
        return self._is_being(indexer_id, [ShowTaskActions.ADD])
69

70
    def is_being_updated(self, indexer_id):
71
        return self._is_being(indexer_id, [ShowTaskActions.UPDATE, ShowTaskActions.FORCEUPDATE])
72

73
    def is_being_refreshed(self, indexer_id):
74
        return self._is_being(indexer_id, [ShowTaskActions.REFRESH])
75

76
    def is_being_renamed(self, indexer_id):
77
        return self._is_being(indexer_id, [ShowTaskActions.RENAME])
echel0n's avatar
echel0n committed
78

79
    def is_being_subtitled(self, indexer_id):
80
        return self._is_being(indexer_id, [ShowTaskActions.SUBTITLE])
81

82
83
    def update_show(self, indexer_id, indexer_update_only=False, force=False):
        show_obj = find_show(indexer_id)
84

85
86
        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))
87

88
89
90
        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))

91
        if force:
92
            task_id = self.put(ShowTaskForceUpdate(indexer_id, indexer_update_only))
93
        else:
94
            task_id = self.put(ShowTaskUpdate(indexer_id, indexer_update_only))
95
96

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

99
100
    def refresh_show(self, indexer_id, force=False):
        show_obj = find_show(indexer_id)
101

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

105
106
        if show_obj.paused and not force:
            sickrage.app.log.debug('Skipping show [{}] because it is paused.'.format(show_obj.name))
107
            return
108

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

111
        self.put(ShowTaskRefresh(indexer_id, force=force))
112

113
    def rename_show_episodes(self, indexer_id):
114
        self.put(ShowTaskRename(indexer_id))
echel0n's avatar
echel0n committed
115

116
    def download_subtitles(self, indexer_id):
117
        self.put(ShowTaskSubtitle(indexer_id))
118

119
    def add_show(self, indexer, indexer_id, showDir, default_status=None, quality=None, flatten_folders=None, lang=None, subtitles=None,
120
                 sub_use_sr_metadata=None, anime=None, search_format=None, dvdorder=None, paused=None, blacklist=None, whitelist=None,
121
                 default_status_after=None, scene=None, skip_downloaded=None):
122

123
        if lang is None:
124
            lang = sickrage.app.config.indexer_default_language
125

126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
        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,
142
                             scene=scene,
143
                             skip_downloaded=skip_downloaded))
144

145
146
147
148
    def remove_show(self, indexer_id, full=False):
        show_obj = find_show(indexer_id)

        if not show_obj:
echel0n's avatar
echel0n committed
149
            raise CantRemoveShowException('Failed removing show: Show does not exist')
150
        elif not hasattr(show_obj, 'indexer_id'):
echel0n's avatar
echel0n committed
151
            raise CantRemoveShowException('Failed removing show: Show does not have an indexer id')
152
        elif self._is_being(show_obj.indexer_id, (ShowTaskActions.REMOVE,)):
153
            raise CantRemoveShowException("{} is already queued to be removed".format(show_obj))
Dustyn Gibson's avatar
Dustyn Gibson committed
154
155

        # remove other queued actions for this show.
156
157
158
        self.remove_task(indexer_id)

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

160

161
162
163
164
165
166
167
168
class ShowTaskActions(Enum):
    REFRESH = 'Refresh'
    ADD = 'Add'
    UPDATE = 'Update'
    FORCEUPDATE = 'Force Update'
    RENAME = 'Rename'
    SUBTITLE = 'Subtitle'
    REMOVE = 'Remove Show'
echel0n's avatar
echel0n committed
169

170

171
class ShowTask(Task):
172
173
174
175
176
177
178
179
180
181
    """
    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
182

183
    def __init__(self, indexer_id, action_id):
184
        super(ShowTask, self).__init__(action_id.value, action_id)
185
        self.indexer_id = indexer_id
echel0n's avatar
echel0n committed
186

echel0n's avatar
echel0n committed
187
    def is_in_queue(self):
188
        return self in sickrage.app.show_queue.queue
189

190
191
    @property
    def show_name(self):
echel0n's avatar
echel0n committed
192
193
        show_obj = find_show(self.indexer_id)
        return show_obj.name if show_obj else str(self.indexer_id)
194

195
    @property
echel0n's avatar
echel0n committed
196
    def is_loading(self):
197
198
        return False

199

200
class ShowTaskAdd(ShowTask):
201
    def __init__(self, indexer, indexer_id, showDir, default_status, quality, flatten_folders, lang, subtitles, sub_use_sr_metadata, anime,
202
                 dvdorder, search_format, paused, blacklist, whitelist, default_status_after, scene, skip_downloaded):
203
        super(ShowTaskAdd, self).__init__(None, ShowTaskActions.ADD)
204

205
206
207
208
209
210
211
212
        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
213
        self.sub_use_sr_metadata = sub_use_sr_metadata
214
        self.anime = anime
215
        self.search_format = search_format
216
        self.dvdorder = dvdorder
217
        self.paused = paused
218
219
        self.blacklist = blacklist
        self.whitelist = whitelist
220
        self.default_status_after = default_status_after
221
        self.scene = scene
222
        self.skip_downloaded = skip_downloaded
223
        self.priority = TaskPriority.HIGH
224

225
226
    @property
    def show_name(self):
227
228
229
230
        """
        Returns the show name if there is a show object created, if not returns
        the dir that the show is being added to.
        """
231
232
233

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

235
    @property
echel0n's avatar
echel0n committed
236
    def is_loading(self):
237
238
239
240
        """
        Returns True if we've gotten far enough to have a show object, or False
        if we still only know the folder name.
        """
241
        if find_show(self.indexer_id):
242
243
            return True

244
    def run(self):
echel0n's avatar
echel0n committed
245
246
        start_time = time.time()

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

249
        index_name = IndexerApi(self.indexer).name
250

echel0n's avatar
echel0n committed
251
        # make sure the Indexer IDs are valid
252
253
254
255
256
        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
257

258
        t = IndexerApi(self.indexer).indexer(**lINDEXER_API_PARMS)
259

260
261
        s = t[self.indexer_id]
        if not s:
262
            sickrage.app.alerts.error(
echel0n's avatar
echel0n committed
263
264
265
                _("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)
266
            )
267

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

271
272
273
274
                data = {
                    'shows': [
                        {
                            'title': title,
275
                            'ids': {IndexerApi(self.indexer).trakt_id: self.indexer_id}
276
277
278
279
                        }
                    ]
                }

280
                TraktAPI()["sync/watchlist"].remove(data)
281

echel0n's avatar
echel0n committed
282
            return self._finish_early()
283

284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
        # 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()

304
        try:
305
306
307
            # add show to database
            show_obj = TVShow(self.indexer_id, self.indexer, lang=self.lang, location=self.showDir)

308
            # set up initial values
309
310
311
312
313
            show_obj.subtitles = self.subtitles or sickrage.app.config.subtitles_default
            show_obj.sub_use_sr_metadata = self.sub_use_sr_metadata
            show_obj.quality = self.quality or sickrage.app.config.quality_default
            show_obj.flatten_folders = self.flatten_folders or sickrage.app.config.flatten_folders_default
            show_obj.anime = self.anime or sickrage.app.config.anime_default
314
            show_obj.dvdorder = self.dvdorder
315
            show_obj.search_format = self.search_format or sickrage.app.config.search_format_default
316
            show_obj.scene = self.scene or sickrage.app.config.scene_default
317
            show_obj.skip_downloaded = self.skip_downloaded or sickrage.app.config.skip_downloaded_default
318
            show_obj.paused = self.paused
echel0n's avatar
echel0n committed
319

320
            # set up default new/missing episode status
321
            sickrage.app.log.info("Setting all current episodes to the specified default status: " + str(self.default_status))
echel0n's avatar
echel0n committed
322

323
            show_obj.default_ep_status = self.default_status
324

325
            # save to database
326
            show_obj.save()
327

328
            if show_obj.anime:
329
                if self.blacklist:
330
                    show_obj.release_groups.set_black_keywords(self.blacklist)
331
                if self.whitelist:
332
                    show_obj.release_groups.set_white_keywords(self.whitelist)
333
        except indexer_exception as e:
334
            sickrage.app.log.warning(_("Unable to add show due to an error with ") + IndexerApi(self.indexer).name + ": {}".format(e))
335
            sickrage.app.alerts.error(_("Unable to add show due to an error with ") + IndexerApi(self.indexer).name + "")
echel0n's avatar
echel0n committed
336
            return self._finish_early()
337
        except MultipleShowObjectsException:
338
            sickrage.app.log.warning(_("The show in ") + self.showDir + _(" is already in your show list, skipping"))
339
            sickrage.app.alerts.error(_('Show skipped'),
340
                                      _("The show in ") + self.showDir + _(" is already in your show list"))
echel0n's avatar
echel0n committed
341
            return self._finish_early()
342
        except Exception as e:
343
            sickrage.app.log.error(_("Error trying to add show: {}").format(e))
344
            sickrage.app.log.debug(traceback.format_exc())
echel0n's avatar
echel0n committed
345
            raise self._finish_early()
346
347

        try:
348
            sickrage.app.log.debug(_("Attempting to retrieve show info from IMDb"))
349
            show_obj.load_imdb_info()
350
        except Exception as e:
echel0n's avatar
echel0n committed
351
352
            sickrage.app.log.debug(_("Error loading IMDb info: {}").format(e))
            sickrage.app.log.debug(traceback.format_exc())
echel0n's avatar
echel0n committed
353

354
        try:
355
            show_obj.load_episodes_from_indexer()
356
        except Exception as e:
echel0n's avatar
echel0n committed
357
            sickrage.app.log.debug(_("Error with ") + IndexerApi(show_obj.indexer).name + _(", not creating episode list: {}").format(e))
358
            sickrage.app.log.debug(traceback.format_exc())
359
360

        try:
361
            show_obj.load_episodes_from_dir()
362
        except Exception as e:
echel0n's avatar
echel0n committed
363
            sickrage.app.log.debug("Error searching dir for episodes: {}".format(e))
364
            sickrage.app.log.debug(traceback.format_exc())
365

366
367
        show_obj.write_metadata(force=True)
        show_obj.populate_cache()
368

369
        if sickrage.app.config.use_trakt:
echel0n's avatar
echel0n committed
370
            # if there are specific episodes that need to be added by trakt
371
            sickrage.app.trakt_searcher.manage_new_show(show_obj)
echel0n's avatar
echel0n committed
372
373

            # add show to trakt.tv library
374
            if sickrage.app.config.trakt_sync:
375
                sickrage.app.trakt_searcher.add_show_to_trakt_library(show_obj)
376

377
            if sickrage.app.config.trakt_sync_watchlist:
378
                sickrage.app.log.info("update watchlist")
379
                sickrage.app.notifier_providers['trakt'].update_watchlist(show_obj)
Giovanni's avatar
Giovanni committed
380

381
382
383
        # Retrieve scene exceptions
        show_obj.retrieve_scene_exceptions()

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

387
        # check if show has XEM mapping so we can determine if searches should go by scene numbering or indexer
echel0n's avatar
echel0n committed
388
        # numbering.
389
390
        # 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
391

392
393
394
        # 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"))
395
            sickrage.app.backlog_searcher.search_backlog(show_obj.indexer_id)
396

397
        show_obj.default_ep_status = self.default_status_after
398
399

        show_obj.save()
400

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

echel0n's avatar
echel0n committed
403
    def _finish_early(self):
404
405
406
407
        try:
            sickrage.app.show_queue.remove_show(self.indexer_id)
        except CantRemoveShowException:
            pass
408

409

410
class ShowTaskRefresh(ShowTask):
411
    def __init__(self, indexer_id=None, force=False):
412
        super(ShowTaskRefresh, self).__init__(indexer_id, ShowTaskActions.REFRESH)
413

414
415
416
        # force refresh certain items
        self.force = force

417
    def run(self):
echel0n's avatar
echel0n committed
418
419
        start_time = time.time()

420
        tv_show = find_show(self.indexer_id)
421

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

424
425
426
        tv_show.refresh_dir()
        tv_show.write_metadata(force=self.force)
        tv_show.populate_cache(force=self.force)
427

428
        # Load XEM data to DB for show
429
        # xem_refresh(show.indexer_id, show.indexer)
430

431
432
        tv_show.last_refresh = datetime.date.today().toordinal()

433
        tv_show.save()
434

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

437

438
class ShowTaskRename(ShowTask):
439
    def __init__(self, indexer_id=None):
440
        super(ShowTaskRename, self).__init__(indexer_id, ShowTaskActions.RENAME)
441

442
443
    def run(self):
        show_obj = find_show(self.indexer_id)
444

445
446
447
        sickrage.app.log.info("Performing renames for show: {}".format(show_obj.name))

        if not os.path.isdir(show_obj.location):
448
            sickrage.app.log.warning("Can't perform rename on " + show_obj.name + " when the show dir is missing.")
449
450
451
452
            return

        ep_obj_rename_list = []

453
        for cur_ep_obj in (x for x in show_obj.episodes if x.location):
454
455
            # Only want to rename if we have a location
            if cur_ep_obj.location:
456
                if cur_ep_obj.related_episodes:
457
458
                    # do we have one of multi-episodes in the rename list already
                    have_already = False
459
                    for cur_related_ep in cur_ep_obj.related_episodes + [cur_ep_obj]:
460
461
462
463
464
465
466
467
468
469
470
                        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()

471
        sickrage.app.log.info("Finished renames for show: {}".format(show_obj.name))
472

473

474
class ShowTaskSubtitle(ShowTask):
475
    def __init__(self, indexer_id=None):
476
        super(ShowTaskSubtitle, self).__init__(indexer_id, ShowTaskActions.SUBTITLE)
477

478
479
    def run(self):
        show_obj = find_show(self.indexer_id)
480
481

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

483
        show_obj.download_subtitles()
484

485
        sickrage.app.log.info("Finished downloading subtitles for show: {}".format(show_obj.name))
486

487

488
489
490
class ShowTaskUpdate(ShowTask):
    def __init__(self, indexer_id=None, indexer_update_only=False, action_id=ShowTaskActions.UPDATE):
        super(ShowTaskUpdate, self).__init__(indexer_id, action_id)
491
        self.indexer_update_only = indexer_update_only
492
493
        self.force = False

494
495
    def run(self):
        show_obj = find_show(self.indexer_id)
496

echel0n's avatar
echel0n committed
497
498
        start_time = time.time()

499
        sickrage.app.log.info("Performing updates for show: {}".format(show_obj.name))
500
501

        try:
502
503
            sickrage.app.log.debug("Retrieving show info from " + IndexerApi(show_obj.indexer).name + "")
            show_obj.load_from_indexer(cache=False)
504
        except indexer_attributenotfound as e:
505
            sickrage.app.log.warning("Data retrieved from " + IndexerApi(show_obj.indexer).name + " was incomplete, aborting: {}".format(e))
506
            return
507
508
509
        except indexer_exception as e:
            sickrage.app.log.warning("Unable to contact " + IndexerApi(show_obj.indexer).name + ", aborting: {}".format(e))
            return
510
511

        try:
512
513
            if not self.indexer_update_only:
                sickrage.app.log.debug("Attempting to retrieve show info from IMDb")
514
                show_obj.load_imdb_info()
515
        except Exception as e:
516
            sickrage.app.log.warning("Error loading IMDb info for {}: {}".format(IndexerApi(show_obj.indexer).name, e))
echel0n's avatar
echel0n committed
517

518
519
        # get episodes from database
        db_episodes = {}
520
        for data in show_obj.episodes:
521
522
523
524
525
            if data.season not in db_episodes:
                db_episodes[data.season] = {}
            db_episodes[data.season].update({data.episode: True})

        # get episodes from indexers
526
        try:
527
            indexer_episodes = show_obj.load_episodes_from_indexer()
528
        except indexer_exception as e:
echel0n's avatar
echel0n committed
529
            sickrage.app.log.warning("Unable to get info from " + IndexerApi(show_obj.indexer).name + ", the show info will not be refreshed: {}".format(e))
530
            indexer_episodes = None
531

532
        if not indexer_episodes:
echel0n's avatar
echel0n committed
533
            sickrage.app.log.warning("No data returned from " + IndexerApi(show_obj.indexer).name + ", unable to update this show")
534
        else:
echel0n's avatar
echel0n committed
535
            # for each ep we found on indexer delete it from the DB list
536
537
538
539
            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]
540

541
            # remaining episodes in the DB list are not on the indexer, just delete them from the DB
542
543
            for curSeason in db_episodes:
                for curEpisode in db_episodes[curSeason]:
544
                    sickrage.app.log.info("Permanently deleting episode " + str(curSeason) + "x" + str(curEpisode) + " from the database")
545
546
547
548
549
550
                    episode_obj = show_obj.get_episode(curSeason, curEpisode, no_create=True)
                    if episode_obj:
                        try:
                            episode_obj.delete_episode()
                        except EpisodeDeletedException:
                            continue
551

552
553
        show_obj.retrieve_scene_exceptions()

554
        sickrage.app.log.info("Finished updates in {}s for show: {}".format(round(time.time() - start_time, 2), show_obj.name))
555

556

557
class ShowTaskForceUpdate(ShowTaskUpdate):
558
    def __init__(self, indexer_id=None, indexer_update_only=False):
559
        super(ShowTaskForceUpdate, self).__init__(indexer_id, indexer_update_only, ShowTaskActions.FORCEUPDATE)
560
        self.indexer_update_only = indexer_update_only
561
        self.force = True
Dustyn Gibson's avatar
Dustyn Gibson committed
562

563

564
class ShowTaskForceRemove(ShowTask):
565
    def __init__(self, indexer_id=None, full=False):
566
        super(ShowTaskForceRemove, self).__init__(indexer_id, ShowTaskActions.REMOVE)
567
568

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

572
    @property
echel0n's avatar
echel0n committed
573
    def is_loading(self):
574
575
576
577
578
        """
        Returns false cause we are removing the show.
        """
        return False

579
580
    def run(self):
        show_obj = find_show(self.indexer_id)
581
582

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

584
        show_obj.delete_show(full=self.full)
585

586
        if sickrage.app.config.use_trakt:
587
            try:
588
                sickrage.app.trakt_searcher.remove_show_from_trakt_library(show_obj)
589
            except Exception as e:
590
                sickrage.app.log.warning("Unable to delete show from Trakt: %s. Error: %s" % (show_obj.name, e))
591

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