show.py 28.1 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 EpisodeStatus
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.series_providers.exceptions import SeriesProviderAttributeNotFound, SeriesProviderException
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.series_id for x in self.tasks.copy().values() if x.is_loading]
49

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

        return False
56

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

        return False
63

64
65
    def is_queued_to_remove(self, series_id):
        return self._is_in_queue(series_id, [ShowTaskActions.REMOVE])
66

67
68
    def is_queued_to_add(self, series_id):
        return self._is_in_queue(series_id, [ShowTaskActions.ADD])
69

70
71
    def is_queued_to_update(self, series_id):
        return self._is_in_queue(series_id, [ShowTaskActions.UPDATE, ShowTaskActions.FORCEUPDATE])
72

73
74
    def is_queued_to_refresh(self, series_id):
        return self._is_in_queue(series_id, [ShowTaskActions.REFRESH])
75

76
77
    def is_queued_to_rename(self, series_id):
        return self._is_in_queue(series_id, [ShowTaskActions.RENAME])
78

79
80
    def is_queued_to_subtitle(self, series_id):
        return self._is_in_queue(series_id, [ShowTaskActions.SUBTITLE])
81

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

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

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

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

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

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

100
101
    def update_show(self, series_id, series_provider_id, force=False):
        show_obj = find_show(series_id, series_provider_id)
102

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

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

109
        task_id = self.put(ShowTaskUpdate(series_id, series_provider_id, force))
110

111
        self.put(ShowTaskRefresh(series_id, series_provider_id, force=force), depend=[task_id])
112

113
114
    def refresh_show(self, series_id, series_provider_id, force=False):
        show_obj = find_show(series_id, series_provider_id)
115

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

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

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

125
        self.put(ShowTaskRefresh(series_id, series_provider_id, force=force))
126

127
128
    def rename_show_episodes(self, series_id, series_provider_id):
        self.put(ShowTaskRename(series_id, series_provider_id))
echel0n's avatar
echel0n committed
129

130
131
    def download_subtitles(self, series_id, series_provider_id):
        self.put(ShowTaskSubtitle(series_id, series_provider_id))
132

133
134
    def add_show(self, series_provider_id, series_id, showDir, default_status=None, quality=None, flatten_folders=None, lang=None, subtitles=None,
                 sub_use_sr_metadata=None, anime=None, search_format=None, dvd_order=None, paused=None, blacklist=None, whitelist=None,
135
                 default_status_after=None, scene=None, skip_downloaded=None):
136

137
        if lang is None:
138
            lang = sickrage.app.config.general.series_provider_default_language
139

140
141
142
        self.put(ShowTaskAdd(series_provider_id=series_provider_id,
                             series_id=series_id,
                             show_dir=showDir,
143
144
145
146
147
148
149
                             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,
150
                             dvd_order=dvd_order,
151
152
153
154
155
                             search_format=search_format,
                             paused=paused,
                             blacklist=blacklist,
                             whitelist=whitelist,
                             default_status_after=default_status_after,
156
                             scene=scene,
157
                             skip_downloaded=skip_downloaded))
158

159
160
    def remove_show(self, series_id, series_provider_id, full=False):
        show_obj = find_show(series_id, series_provider_id)
161
162

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

        # remove other queued actions for this show.
170
        self.remove_task(series_id)
171

172
        self.put(ShowTaskForceRemove(series_id=series_id, series_provider_id=series_provider_id, full=full))
echel0n's avatar
echel0n committed
173

174

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

184

185
class ShowTask(Task):
186
187
188
189
190
191
192
193
194
195
    """
    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
196

197
    def __init__(self, series_id, series_provider_id, action):
198
        super(ShowTask, self).__init__(action.value, action)
199
200
        self.series_id = series_id
        self.series_provider_id = series_provider_id
echel0n's avatar
echel0n committed
201

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

205
206
    @property
    def show_name(self):
207
208
        show_obj = find_show(self.series_id, self.series_provider_id)
        return show_obj.name if show_obj else str(self.series_id)
209

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

214
    def run(self):
215
        show_obj = find_show(self.series_id, self.series_provider_id)
216

217
        if show_obj:
218
            WebSocketMessage('SHOW_QUEUE_STATUS_UPDATED', {'seriesSlug': show_obj.slug, 'showQueueStatus': show_obj.show_queue_status}).push()
219
        else:
220
            WebSocketMessage('SHOW_QUEUE_STATUS_UPDATED',
221
                             {'seriesSlug': f'{self.series_id}-{self.series_provider_id.value}', 'action': self.action.name}).push()
222
223

    def finish(self):
224
        show_obj = find_show(self.series_id, self.series_provider_id)
225

226
        if show_obj:
227
            WebSocketMessage('SHOW_QUEUE_STATUS_UPDATED', {'seriesSlug': show_obj.slug, 'showQueueStatus': show_obj.show_queue_status}).push()
228
        else:
229
            WebSocketMessage('SHOW_QUEUE_STATUS_UPDATED',
230
                             {'seriesSlug': f'{self.series_id}-{self.series_provider_id.value}', 'action': self.action.name}).push()
231

232

233
class ShowTaskAdd(ShowTask):
234
235
236
    def __init__(self, series_provider_id, series_id, show_dir, default_status, quality, flatten_folders, lang, subtitles, sub_use_sr_metadata, anime,
                 dvd_order, search_format, paused, blacklist, whitelist, default_status_after, scene, skip_downloaded):
        super(ShowTaskAdd, self).__init__(series_id, series_provider_id, ShowTaskActions.ADD)
237

238
        self.show_dir = show_dir
239
240
241
242
243
        self.default_status = default_status
        self.quality = quality
        self.flatten_folders = flatten_folders
        self.lang = lang
        self.subtitles = subtitles
244
        self.sub_use_sr_metadata = sub_use_sr_metadata
245
        self.anime = anime
246
        self.search_format = search_format
247
        self.dvd_order = dvd_order
248
        self.paused = paused
249
250
        self.blacklist = blacklist
        self.whitelist = whitelist
251
        self.default_status_after = default_status_after
252
        self.scene = scene
253
        self.skip_downloaded = skip_downloaded
254
        self.priority = TaskPriority.HIGH
255

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

263
264
        show_obj = find_show(self.series_id, self.series_provider_id)
        return show_obj.name if show_obj else os.path.basename(self.show_dir)
265

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

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

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

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

282
283
284
285
        series_provider_language = self.lang or sickrage.app.config.general.series_provider_default_language
        series_info = sickrage.app.series_providers[self.series_provider_id].get_series_info(self.series_id, language=series_provider_language,
                                                                                             enable_cache=False)
        if not series_info:
286
            sickrage.app.alerts.error(
echel0n's avatar
echel0n committed
287
288
                _("Unable to add show"),
                _("Unable to look up the show in {} on {} using ID {}, not using the NFO. Delete .nfo and try adding "
289
                  "manually again.").format(self.show_dir, sickrage.app.series_providers[self.series_provider_id].name, self.series_id)
290
            )
291

292
293
            if sickrage.app.config.trakt.enable:
                title = self.show_dir.split("/")[-1]
echel0n's avatar
echel0n committed
294

295
296
297
298
                data = {
                    'shows': [
                        {
                            'title': title,
299
                            'ids': {sickrage.app.series_providers[self.series_provider_id].trakt_id: self.series_id}
300
301
302
303
                        }
                    ]
                }

304
                TraktAPI()["sync/watchlist"].remove(data)
305

echel0n's avatar
echel0n committed
306
            return self._finish_early()
307

308
        # this usually only happens if they have an NFO in their show dir which gave us a series id that has no
309
310
        # proper english version of the show
        try:
311
            series_info.name
312
        except AttributeError:
313
314
315
316
            sickrage.app.log.warning(
                f"Show in {self.show_dir} has no name on {sickrage.app.series_providers[self.series_provider_id].name}, "
                f"probably the wrong language used to search with")

317
            sickrage.app.alerts.error(_("Unable to add show"),
echel0n's avatar
echel0n committed
318
319
                                      f"Show in {self.show_dir} has no name on {sickrage.app.series_providers[self.series_provider_id].name}, "
                                      f"probably the wrong language. Delete .nfo and add manually in the correct language")
320

321
322
323
            return self._finish_early()

        # if the show has no episodes/seasons
324
325
326
327
328
329
330
331
332
333
334
335
        if not len(series_info):
            sickrage.app.log.warning(
                "Show " + str(series_info['name']) + " is on " + str(
                    sickrage.app.series_providers[self.series_provider_id].name) + "but contains no season/episode data."
            )

            sickrage.app.alerts.error(
                _("Unable to add show"),
                _("Show ") + str(series_info['name']) + _(" is on ") + str(sickrage.app.series_providers[self.series_provider_id].name) + _(
                    " but contains no season/episode data.")
            )

336
337
            return self._finish_early()

338
        try:
339
            # add show to database
340
            show_obj = TVShow(self.series_id, self.series_provider_id, lang=self.lang, location=self.show_dir)
341

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

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

358
            # save to database
359
            show_obj.save()
360

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

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

388
        try:
389
            show_obj.load_episodes_from_series_provider()
390
        except Exception as e:
391
            sickrage.app.log.debug(_("Error with ") + show_obj.series_provider.name + _(", not creating episode list: {}").format(e))
392
            sickrage.app.log.debug(traceback.format_exc())
393
394

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

400
401
        show_obj.write_metadata(force=True)
        show_obj.populate_cache()
402

403
        if sickrage.app.config.trakt.enable:
echel0n's avatar
echel0n committed
404
            # if there are specific episodes that need to be added by trakt
405
            sickrage.app.trakt_searcher.manage_new_show(show_obj)
echel0n's avatar
echel0n committed
406
407

            # add show to trakt.tv library
408
            if sickrage.app.config.trakt.sync:
409
                sickrage.app.trakt_searcher.add_show_to_trakt_library(show_obj)
410

411
            if sickrage.app.config.trakt.sync_watchlist:
412
                sickrage.app.log.info("update watchlist")
413
                sickrage.app.notification_providers['trakt'].update_watchlist(show_obj)
Giovanni's avatar
Giovanni committed
414

415
416
417
        # Retrieve scene exceptions
        show_obj.retrieve_scene_exceptions()

echel0n's avatar
echel0n committed
418
        # Load XEM data to DB for show
419
        xem_refresh(show_obj.series_id, show_obj.series_provider_id, force=True)
echel0n's avatar
echel0n committed
420

421
        # check if show has XEM mapping so we can determine if searches should go by scene numbering or series_provider_id
echel0n's avatar
echel0n committed
422
        # numbering.
423
        # if not self.scene and get_xem_numbering_for_show(show_obj.series_id, show_obj.series_provider_id):
424
        #     show_obj.scene = 1
echel0n's avatar
echel0n committed
425

426
        # if they set default ep status to WANTED then run the backlog to search for episodes
427
        if show_obj.default_ep_status == EpisodeStatus.WANTED:
428
            sickrage.app.log.info(_("Launching backlog for this show since it has episodes that are WANTED"))
429
            sickrage.app.backlog_searcher.search_backlog(show_obj.series_id, show_obj.series_provider_id)
430

431
        show_obj.default_ep_status = self.default_status_after
432
433

        show_obj.save()
434

435
        WebSocketMessage('SHOW_ADDED',
436
                         {'seriesSlug': show_obj.slug,
437
438
                          'series': show_obj.to_json(progress=True)}).push()

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

echel0n's avatar
echel0n committed
441
    def _finish_early(self):
442
        try:
443
            sickrage.app.show_queue.remove_show(self.series_id, self.series_provider_id)
444
        except CantRemoveShowException:
445
            WebSocketMessage('SHOW_REMOVED',
446
                             {'seriesSlug': f'{self.series_id}-{self.series_provider_id.value}'}).push()
447

448

449
class ShowTaskRefresh(ShowTask):
450
451
    def __init__(self, series_id=None, series_provider_id=None, force=False):
        super(ShowTaskRefresh, self).__init__(series_id, series_provider_id, ShowTaskActions.REFRESH)
452

453
454
455
        # force refresh certain items
        self.force = force

456
    def run(self):
457
458
        super(ShowTaskRefresh, self).run()

echel0n's avatar
echel0n committed
459
460
        start_time = time.time()

461
        tv_show = find_show(self.series_id, self.series_provider_id)
462

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

465
466
467
        tv_show.refresh_dir()
        tv_show.write_metadata(force=self.force)
        tv_show.populate_cache(force=self.force)
468

469
        # Load XEM data to DB for show
470
        # xem_refresh(show.series_id, show.series_provider_id)
471

472
        tv_show.last_refresh = datetime.datetime.now()
473

474
        tv_show.save()
475

476
        WebSocketMessage('SHOW_REFRESHED',
477
                         {'seriesSlug': tv_show.slug,
478
479
                          'series': tv_show.to_json(episodes=True, details=True)}).push()

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

482

483
class ShowTaskRename(ShowTask):
484
485
    def __init__(self, series_id=None, series_provider_id=None):
        super(ShowTaskRename, self).__init__(series_id, series_provider_id, ShowTaskActions.RENAME)
486

487
    def run(self):
488
489
        super(ShowTaskRename, self).run()

490
        tv_show = find_show(self.series_id, self.series_provider_id)
491

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

494
495
        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.")
496
497
498
499
            return

        ep_obj_rename_list = []

500
        for cur_ep_obj in (x for x in tv_show.episodes if x.location):
501
502
            # Only want to rename if we have a location
            if cur_ep_obj.location:
503
                if cur_ep_obj.related_episodes:
504
505
                    # do we have one of multi-episodes in the rename list already
                    have_already = False
506
                    for cur_related_ep in cur_ep_obj.related_episodes + [cur_ep_obj]:
507
508
509
510
511
512
513
514
515
516
517
                        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()

518
        WebSocketMessage('SHOW_RENAMED',
519
                         {'seriesSlug': tv_show.slug,
520
521
522
                          'series': tv_show.to_json(episodes=True, details=True)}).push()

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

524

525
class ShowTaskSubtitle(ShowTask):
526
527
    def __init__(self, series_id=None, series_provider_id=None):
        super(ShowTaskSubtitle, self).__init__(series_id, series_provider_id, ShowTaskActions.SUBTITLE)
528

529
    def run(self):
530
531
        super(ShowTaskSubtitle, self).run()

532
        tv_show = find_show(self.series_id, self.series_provider_id)
533
534

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

536
        tv_show.download_subtitles()
537

538
        WebSocketMessage('SHOW_SUBTITLED',
539
                         {'seriesSlug': tv_show.slug,
540
                          'series': tv_show.to_json(episodes=True, details=True)}).push()
541

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

544

545
class ShowTaskUpdate(ShowTask):
546
547
    def __init__(self, series_id=None, series_provider_id=None, force=False, action=ShowTaskActions.UPDATE):
        super(ShowTaskUpdate, self).__init__(series_id, series_provider_id, action if not force else ShowTaskActions.FORCEUPDATE)
548
        self.force = force
549

550
    def run(self):
551
552
        super(ShowTaskUpdate, self).run()

553
        show_obj = find_show(self.series_id, self.series_provider_id)
554

echel0n's avatar
echel0n committed
555
556
        start_time = time.time()

557
        sickrage.app.log.info("Performing updates for show: {}".format(show_obj.name))
558
559

        try:
560
561
562
563
            sickrage.app.log.debug("Retrieving show info from " + show_obj.series_provider.name + "")
            show_obj.load_from_series_provider(cache=False)
        except SeriesProviderAttributeNotFound as e:
            sickrage.app.log.warning("Data retrieved from " + show_obj.series_provider.name + " was incomplete, aborting: {}".format(e))
564
            return
565
566
        except SeriesProviderException as e:
            sickrage.app.log.warning("Unable to contact " + show_obj.series_provider.name + ", aborting: {}".format(e))
567
            return
568
569

        try:
570
571
            sickrage.app.log.debug("Attempting to retrieve show info from IMDb")
            show_obj.load_imdb_info()
572
        except Exception as e:
573
            sickrage.app.log.warning("Error loading IMDb info for {}: {}".format(show_obj.series_provider.name, e))
echel0n's avatar
echel0n committed
574

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

582
        # get episodes from a series provider
583
        try:
584
585
586
587
588
589
590
591
            series_provider_episodes = show_obj.load_episodes_from_series_provider()
        except SeriesProviderException as e:
            sickrage.app.log.warning(
                "Unable to get info from " + show_obj.series_provider.name + ", the show info will not be refreshed: {}".format(e))
            series_provider_episodes = None

        if not series_provider_episodes:
            sickrage.app.log.warning("No data returned from " + show_obj.series_provider.name + ", unable to update this show")
592
        else:
593
594
595
            # for each ep we found on series_provider_id delete it from the DB list
            for curSeason in series_provider_episodes:
                for curEpisode in series_provider_episodes[curSeason]:
596
597
                    if curSeason in db_episodes and curEpisode in db_episodes[curSeason]:
                        del db_episodes[curSeason][curEpisode]
598

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

608
609
        show_obj.retrieve_scene_exceptions()

610
        WebSocketMessage('SHOW_UPDATED',
611
                         {'seriesSlug': show_obj.slug,
612
                          'series': show_obj.to_json(episodes=True, details=True)}).push()
613

614
        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
615

616

617
class ShowTaskForceRemove(ShowTask):
618
619
    def __init__(self, series_id=None, series_provider_id=None, full=False):
        super(ShowTaskForceRemove, self).__init__(series_id, series_provider_id, ShowTaskActions.REMOVE)
620
621

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

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

632
    def run(self):
633
634
        super(ShowTaskForceRemove, self).run()

635
        show_obj = find_show(self.series_id, self.series_provider_id)
636
637
        if not show_obj:
            return
638
639

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

641
        show_obj.delete_show(full=self.full)
642

643
        if sickrage.app.config.trakt.enable:
644
            try:
645
                sickrage.app.trakt_searcher.remove_show_from_trakt_library(show_obj)
646
            except Exception as e:
647
                sickrage.app.log.warning("Unable to delete show from Trakt: %s. Error: %s" % (show_obj.name, e))
648

649
        WebSocketMessage('SHOW_REMOVED',
650
                         {'seriesSlug': show_obj.slug}).push()
651

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