# Author: Nic Wolfe # URL: http://code.google.com/p/sickbeard/ # # This file is part of SickGear. # # SickGear 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. # # SickGear 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 SickGear. If not, see . from __future__ import with_statement import traceback import sickbeard from sickbeard.common import SKIPPED, WANTED from sickbeard.tv import TVShow from sickbeard import exceptions, logger, ui, db from sickbeard import generic_queue from sickbeard import name_cache from sickbeard.exceptions import ex from sickbeard.blackandwhitelist import BlackAndWhiteList class ShowQueue(generic_queue.GenericQueue): def __init__(self): generic_queue.GenericQueue.__init__(self) self.queue_name = "SHOWQUEUE" def _isInQueue(self, show, actions): return show in [x.show for x in self.queue if x.action_id in actions] def _isBeingSomethinged(self, show, actions): return self.currentItem != None and show == self.currentItem.show and \ self.currentItem.action_id in actions def isInUpdateQueue(self, show): return self._isInQueue(show, (ShowQueueActions.UPDATE, ShowQueueActions.FORCEUPDATE)) def isInRefreshQueue(self, show): return self._isInQueue(show, (ShowQueueActions.REFRESH,)) def isInRenameQueue(self, show): return self._isInQueue(show, (ShowQueueActions.RENAME,)) def isInSubtitleQueue(self, show): return self._isInQueue(show, (ShowQueueActions.SUBTITLE,)) def isBeingAdded(self, show): return self._isBeingSomethinged(show, (ShowQueueActions.ADD,)) def isBeingUpdated(self, show): return self._isBeingSomethinged(show, (ShowQueueActions.UPDATE, ShowQueueActions.FORCEUPDATE)) def isBeingRefreshed(self, show): return self._isBeingSomethinged(show, (ShowQueueActions.REFRESH,)) def isBeingRenamed(self, show): return self._isBeingSomethinged(show, (ShowQueueActions.RENAME,)) def isBeingSubtitled(self, show): return self._isBeingSomethinged(show, (ShowQueueActions.SUBTITLE,)) def _getLoadingShowList(self): return [x for x in self.queue + [self.currentItem] if x != None and x.isLoading] loadingShowList = property(_getLoadingShowList) def updateShow(self, show, force=False): if self.isBeingAdded(show): raise exceptions.CantUpdateException( "Show is still being added, wait until it is finished before you update.") if self.isBeingUpdated(show): raise exceptions.CantUpdateException( "This show is already being updated, can't update again until it's done.") if self.isInUpdateQueue(show): raise exceptions.CantUpdateException( "This show is already being updated, can't update again until it's done.") if not force: queueItemObj = QueueItemUpdate(show) else: queueItemObj = QueueItemForceUpdate(show) self.add_item(queueItemObj) return queueItemObj def refreshShow(self, show, force=False): if self.isBeingRefreshed(show) and not force: raise exceptions.CantRefreshException("This show is already being refreshed, not refreshing again.") if (self.isBeingUpdated(show) or self.isInUpdateQueue(show)) and not force: logger.log( u"A refresh was attempted but there is already an update queued or in progress. Since updates do a refresh at the end anyway I'm skipping this request.", logger.DEBUG) return queueItemObj = QueueItemRefresh(show, force=force) self.add_item(queueItemObj) return queueItemObj def renameShowEpisodes(self, show, force=False): queueItemObj = QueueItemRename(show) self.add_item(queueItemObj) return queueItemObj def downloadSubtitles(self, show, force=False): queueItemObj = QueueItemSubtitle(show) self.add_item(queueItemObj) return queueItemObj def addShow(self, indexer, indexer_id, showDir, default_status=None, quality=None, flatten_folders=None, lang="en", subtitles=None, anime=None, scene=None, paused=None, blacklist=None, whitelist=None, default_wanted_begin=None, default_wanted_latest=None): queueItemObj = QueueItemAdd(indexer, indexer_id, showDir, default_status, quality, flatten_folders, lang, subtitles, anime, scene, paused, blacklist, whitelist, default_wanted_begin, default_wanted_latest) self.add_item(queueItemObj) return queueItemObj class ShowQueueActions: REFRESH = 1 ADD = 2 UPDATE = 3 FORCEUPDATE = 4 RENAME = 5 SUBTITLE = 6 names = {REFRESH: 'Refresh', ADD: 'Add', UPDATE: 'Update', FORCEUPDATE: 'Force Update', RENAME: 'Rename', SUBTITLE: 'Subtitle'} class ShowQueueItem(generic_queue.QueueItem): """ 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 """ def __init__(self, action_id, show): generic_queue.QueueItem.__init__(self, ShowQueueActions.names[action_id], action_id) self.show = show def isInQueue(self): return self in sickbeard.showQueueScheduler.action.queue + [ sickbeard.showQueueScheduler.action.currentItem] #@UndefinedVariable def _getName(self): return str(self.show.indexerid) def _isLoading(self): return False show_name = property(_getName) isLoading = property(_isLoading) class QueueItemAdd(ShowQueueItem): def __init__(self, indexer, indexer_id, showDir, default_status, quality, flatten_folders, lang, subtitles, anime, scene, paused, blacklist, whitelist, default_wanted_begin, default_wanted_latest): self.indexer = indexer self.indexer_id = indexer_id self.showDir = showDir self.default_status = default_status self.default_wanted_begin = default_wanted_begin self.default_wanted_latest = default_wanted_latest self.quality = quality self.flatten_folders = flatten_folders self.lang = lang self.subtitles = subtitles self.anime = anime self.scene = scene self.paused = paused self.blacklist = blacklist self.whitelist = whitelist self.show = None # this will initialize self.show to None ShowQueueItem.__init__(self, ShowQueueActions.ADD, self.show) def _getName(self): """ Returns the show name if there is a show object created, if not returns the dir that the show is being added to. """ if self.show == None: return self.showDir return self.show.name show_name = property(_getName) def _isLoading(self): """ Returns True if we've gotten far enough to have a show object, or False if we still only know the folder name. """ if self.show == None: return True return False isLoading = property(_isLoading) def run(self): ShowQueueItem.run(self) logger.log(u"Starting to add show " + self.showDir) # make sure the Indexer IDs are valid try: lINDEXER_API_PARMS = sickbeard.indexerApi(self.indexer).api_params.copy() if self.lang: lINDEXER_API_PARMS['language'] = self.lang logger.log(u"" + str(sickbeard.indexerApi(self.indexer).name) + ": " + repr(lINDEXER_API_PARMS)) t = sickbeard.indexerApi(self.indexer).indexer(**lINDEXER_API_PARMS) s = t[self.indexer_id] # 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 if getattr(s, 'seriesname', None) is None: logger.log(u"Show in " + self.showDir + " has no name on " + str( sickbeard.indexerApi(self.indexer).name) + ", probably the wrong language used to search with.", logger.ERROR) ui.notifications.error("Unable to add show", "Show in " + self.showDir + " has no name on " + str(sickbeard.indexerApi( self.indexer).name) + ", probably the wrong language. Delete .nfo and add manually in the correct language.") self._finishEarly() return # if the show has no episodes/seasons if not s: logger.log(u"Show " + str(s['seriesname']) + " is on " + str( sickbeard.indexerApi(self.indexer).name) + " but contains no season/episode data.", logger.ERROR) ui.notifications.error("Unable to add show", "Show " + str(s['seriesname']) + " is on " + str(sickbeard.indexerApi( self.indexer).name) + " but contains no season/episode data.") self._finishEarly() return except Exception, e: logger.log(u"Unable to find show ID:" + str(self.indexer_id) + " on Indexer: " + str( sickbeard.indexerApi(self.indexer).name), logger.ERROR) ui.notifications.error("Unable to add show", "Unable to look up the show in " + self.showDir + " on " + str(sickbeard.indexerApi( self.indexer).name) + " using ID " + str( self.indexer_id) + ", not using the NFO. Delete .nfo and try adding manually again.") self._finishEarly() return try: newShow = TVShow(self.indexer, self.indexer_id, self.lang) newShow.loadFromIndexer() self.show = newShow # set up initial values self.show.location = self.showDir self.show.subtitles = self.subtitles if self.subtitles != None else sickbeard.SUBTITLES_DEFAULT self.show.quality = self.quality if self.quality else sickbeard.QUALITY_DEFAULT self.show.flatten_folders = self.flatten_folders if self.flatten_folders != None else sickbeard.FLATTEN_FOLDERS_DEFAULT self.show.anime = self.anime if self.anime != None else sickbeard.ANIME_DEFAULT self.show.scene = self.scene if self.scene != None else sickbeard.SCENE_DEFAULT self.show.paused = self.paused if self.paused != None else False if self.show.anime: self.show.release_groups = BlackAndWhiteList(self.show.indexerid) if self.blacklist: self.show.release_groups.set_black_keywords(self.blacklist) if self.whitelist: self.show.release_groups.set_white_keywords(self.whitelist) # be smartish about this if self.show.genre and "talk show" in self.show.genre.lower(): self.show.air_by_date = 1 if self.show.genre and "documentary" in self.show.genre.lower(): self.show.air_by_date = 0 if self.show.classification and "sports" in self.show.classification.lower(): self.show.sports = 1 except sickbeard.indexer_exception, e: logger.log( u"Unable to add show due to an error with " + sickbeard.indexerApi(self.indexer).name + ": " + ex(e), logger.ERROR) if self.show: ui.notifications.error( "Unable to add " + str(self.show.name) + " due to an error with " + sickbeard.indexerApi( self.indexer).name + "") else: ui.notifications.error( "Unable to add show due to an error with " + sickbeard.indexerApi(self.indexer).name + "") self._finishEarly() return except exceptions.MultipleShowObjectsException: logger.log(u"The show in " + self.showDir + " is already in your show list, skipping", logger.ERROR) ui.notifications.error('Show skipped', "The show in " + self.showDir + " is already in your show list") self._finishEarly() return except Exception, e: logger.log(u"Error trying to add show: " + ex(e), logger.ERROR) logger.log(traceback.format_exc(), logger.DEBUG) self._finishEarly() raise self.show.load_imdb_info() try: self.show.saveToDB() except Exception, e: logger.log(u"Error saving the show to the database: " + ex(e), logger.ERROR) logger.log(traceback.format_exc(), logger.DEBUG) self._finishEarly() raise # add it to the show list sickbeard.showList.append(self.show) try: self.show.loadEpisodesFromIndexer() except Exception, e: logger.log( u"Error with " + sickbeard.indexerApi(self.show.indexer).name + ", not creating episode list: " + ex(e), logger.ERROR) logger.log(traceback.format_exc(), logger.DEBUG) # update internal name cache name_cache.buildNameCache() try: self.show.loadEpisodesFromDir() except Exception, e: logger.log(u"Error searching directory for episodes: " + ex(e), logger.ERROR) logger.log(traceback.format_exc(), logger.DEBUG) # if they gave a custom status then change all the eps to it if self.default_status != SKIPPED: logger.log(u"Setting all episodes to the specified default status: " + str(self.default_status)) myDB = db.DBConnection() myDB.action("UPDATE tv_episodes SET status = ? WHERE status = ? AND showid = ? AND season != 0", [self.default_status, SKIPPED, self.show.indexerid]) # if they gave a number to start or number to end as wanted, then change those eps to it def get_wanted_sql(wanted_max, sql_vars): actual = 0 if wanted_max: my_db = db.DBConnection() sql = "UPDATE `tv_episodes` SET status=%s" % WANTED\ + " WHERE indexerid IN "\ + "(SELECT t1.indexerid FROM `tv_episodes` t1 JOIN "\ + "(SELECT %s(season),showid,season FROM `tv_episodes` WHERE '%s'=showid" % (sql_vars[0], self.show.indexerid)\ + " AND '0'