1
0
mirror of https://github.com/moparisthebest/SickRage synced 2024-08-13 16:53:54 -04:00
SickRage/sickbeard/show_queue.py
echel0n 0d9fbc1ad7 Welcome to our SickBeard-TVRage Edition ...
This version of SickBeard uses both TVDB and TVRage to search and gather it's series data from allowing you to now have access to and download shows that you couldn't before because of being locked into only what TheTVDB had to offer.

Also this edition is based off the code we used in our XEM editon so it does come with scene numbering support as well as all the other features our XEM edition has to offer.

Please before using this with your existing database (sickbeard.db) please make a backup copy of it and delete any other database files such as cache.db and failed.db if present, we HIGHLY recommend starting out with no database files at all to make this a fresh start but the choice is at your own risk!

Enjoy!
2014-03-09 22:39:12 -07:00

518 lines
20 KiB
Python

# Author: Nic Wolfe <nic@wolfeden.ca>
# URL: http://code.google.com/p/sickbeard/
#
# This file is part of Sick Beard.
#
# Sick Beard 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.
#
# Sick Beard 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 Sick Beard. If not, see <http://www.gnu.org/licenses/>.
from __future__ import with_statement
import traceback
import sickbeard
from lib.imdb import _exceptions as imdb_exceptions
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.indexers import indexer_api, indexer_exceptions
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 refres at the end anyway I'm skipping this request.", logger.DEBUG)
return
queueItemObj = QueueItemRefresh(show)
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, subtitles=None, lang="en", refresh=False):
queueItemObj = QueueItemAdd(indexer, indexer_id, showDir, default_status, quality, flatten_folders, lang, subtitles, refresh)
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, refresh):
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
self.refresh = refresh
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 execute(self):
ShowQueueItem.execute(self)
logger.log(u"Starting to add show " + self.showDir)
try:
# make sure the indexer ids are valid
try:
sickbeard.INDEXER_API_PARMS['indexer'] = self.indexer
lindexer_api_parms = sickbeard.INDEXER_API_PARMS.copy()
if self.lang:
lindexer_api_parms['language'] = self.lang
logger.log(u"" + self.indexer + ": " + repr(lindexer_api_parms))
t = indexer_api.indexerApi(**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 not s['seriesname']:
logger.log(u"Show in " + self.showDir + " has no name on " + self.indexer + ", 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 " + self.indexer + ", 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 " + self.indexer + " but contains no season/episode data.", logger.ERROR)
ui.notifications.error("Unable to add show", "Show " + str(s['seriesname']) + " is on " + self.indexer + " but contains no season/episode data.")
self._finishEarly()
return
except indexer_exceptions.indexer_exception, e:
logger.log(u"Error contacting " + self.indexer + ": " + ex(e), logger.ERROR)
ui.notifications.error("Unable to add show", "Unable to look up the show in " + self.showDir + " on " + self.indexer + ", not using the NFO. Delete .nfo and add manually in the correct language or try a different Indexer.")
self._finishEarly()
return
# clear the name cache
name_cache.clearCache()
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.paused = False
# be smartish about this
if self.show.genre and "talk show" in self.show.genre.lower() or "sports" in self.show.classification.lower():
self.show.air_by_date = 1
except indexer_exceptions.indexer_exception, e:
logger.log(u"Unable to add show due to an error with " + self.indexer + ": " + ex(e), logger.ERROR)
if self.show:
ui.notifications.error("Unable to add " + str(self.show.name) + " due to an error with " + self.indexer + "")
else:
ui.notifications.error("Unable to add show due to an error with " + self.indexer + "")
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
logger.log(u"Retrieving show info from IMDb", logger.DEBUG)
try:
self.show.loadIMDbInfo()
except imdb_exceptions.IMDbError, e:
#todo Insert UI notification
logger.log(u" Something wrong on IMDb api: " + ex(e), logger.WARNING)
except imdb_exceptions.IMDbParserError, e:
logger.log(u" IMDb_api parser error: " + ex(e), logger.WARNING)
except Exception, e:
logger.log(u"Error loading IMDb info: " + ex(e), logger.ERROR)
logger.log(traceback.format_exc(), logger.DEBUG)
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 " + self.show.indexer + ", not creating episode list: " + ex(e), logger.ERROR)
logger.log(traceback.format_exc(), logger.DEBUG)
try:
self.show.loadEpisodesFromDir()
except Exception, e:
logger.log(u"Error searching dir 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 started with WANTED eps then run the backlog
if self.default_status == WANTED:
logger.log(u"Launching backlog for this show since its episodes are WANTED")
sickbeard.backlogSearchScheduler.action.searchBacklog([self.show]) #@UndefinedVariable
self.show.writeMetadata(force=self.refresh)
self.show.populateCache()
self.show.flushEpisodes()
# if there are specific episodes that need to be added by trakt
sickbeard.traktWatchListCheckerSchedular.action.manageNewShow(self.show)
self.finish()
def _finishEarly(self):
if self.show != None:
self.show.deleteShow()
self.finish()
class QueueItemRefresh(ShowQueueItem):
def __init__(self, show=None):
ShowQueueItem.__init__(self, ShowQueueActions.REFRESH, show)
# do refreshes first because they're quick
self.priority = generic_queue.QueuePriorities.HIGH
def execute(self):
ShowQueueItem.execute(self)
logger.log(u"Performing refresh on " + self.show.name)
self.show.refreshDir()
self.show.writeMetadata(force=True)
self.show.populateCache()
self.inProgress = False
class QueueItemRename(ShowQueueItem):
def __init__(self, show=None):
ShowQueueItem.__init__(self, ShowQueueActions.RENAME, show)
def execute(self):
ShowQueueItem.execute(self)
logger.log(u"Performing rename on " + self.show.name)
try:
show_loc = self.show.location
except exceptions.ShowDirNotFoundException:
logger.log(u"Can't perform rename on " + self.show.name + " when the show dir is missing.", logger.WARNING)
return
ep_obj_rename_list = []
ep_obj_list = self.show.getAllEpisodes(has_location=True)
for cur_ep_obj in ep_obj_list:
# Only want to rename if we have a location
if cur_ep_obj.location:
if cur_ep_obj.relatedEps:
# do we have one of multi-episodes in the rename list already
have_already = False
for cur_related_ep in cur_ep_obj.relatedEps + [cur_ep_obj]:
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()
self.inProgress = False
class QueueItemSubtitle(ShowQueueItem):
def __init__(self, show=None):
ShowQueueItem.__init__(self, ShowQueueActions.SUBTITLE, show)
def execute(self):
ShowQueueItem.execute(self)
logger.log(u"Downloading subtitles for "+self.show.name)
self.show.downloadSubtitles()
self.inProgress = False
class QueueItemUpdate(ShowQueueItem):
def __init__(self, show=None):
ShowQueueItem.__init__(self, ShowQueueActions.UPDATE, show)
self.force = False
def execute(self):
ShowQueueItem.execute(self)
logger.log(u"Beginning update of " + self.show.name)
logger.log(u"Retrieving show info from " + self.show.indexer + "", logger.DEBUG)
try:
self.show.loadFromIndexer(cache=not self.force)
except indexer_exceptions.indexer_error, e:
logger.log(u"Unable to contact " + self.show.indexer + ", aborting: " + ex(e), logger.WARNING)
return
except indexer_exceptions.indexer_attributenotfound, e:
logger.log(u"Data retrieved from " + self.show.indexer + " was incomplete, aborting: " + ex(e), logger.ERROR)
return
logger.log(u"Retrieving show info from IMDb", logger.DEBUG)
try:
self.show.loadIMDbInfo()
except imdb_exceptions.IMDbError, e:
logger.log(u" Something wrong on IMDb api: " + ex(e), logger.WARNING)
except imdb_exceptions.IMDbParserError, e:
logger.log(u" IMDb api parser error: " + ex(e), logger.WARNING)
except Exception, e:
logger.log(u"Error loading IMDb info: " + ex(e), logger.ERROR)
logger.log(traceback.format_exc(), logger.DEBUG)
try:
self.show.saveToDB()
except Exception, e:
logger.log(u"Error saving the episode to the database: " + ex(e), logger.ERROR)
logger.log(traceback.format_exc(), logger.DEBUG)
# get episode list from DB
logger.log(u"Loading all episodes from the database", logger.DEBUG)
DBEpList = self.show.loadEpisodesFromDB()
# get episode list from TVDB
logger.log(u"Loading all episodes from " + self.show.indexer + "", logger.DEBUG)
try:
IndexerEpList = self.show.loadEpisodesFromIndexer(cache=not self.force)
except indexer_exceptions.indexer_exception, e:
logger.log(u"Unable to get info from " + self.show.indexer + ", the show info will not be refreshed: " + ex(e), logger.ERROR)
IndexerEpList = None
if IndexerEpList == None:
logger.log(u"No data returned from " + self.show.indexer + ", unable to update this show", logger.ERROR)
else:
# for each ep we found on TVDB delete it from the DB list
for curSeason in IndexerEpList:
for curEpisode in IndexerEpList[curSeason]:
logger.log(u"Removing " + str(curSeason) + "x" + str(curEpisode) + " from the DB list", logger.DEBUG)
if curSeason in DBEpList and curEpisode in DBEpList[curSeason]:
del DBEpList[curSeason][curEpisode]
# for the remaining episodes in the DB list just delete them from the DB
for curSeason in DBEpList:
for curEpisode in DBEpList[curSeason]:
logger.log(u"Permanently deleting episode " + str(curSeason) + "x" + str(curEpisode) + " from the database", logger.MESSAGE)
curEp = self.show.getEpisode(curSeason, curEpisode)
try:
curEp.deleteEpisode()
except exceptions.EpisodeDeletedException:
pass
sickbeard.showQueueScheduler.action.refreshShow(self.show, True) #@UndefinedVariable
class QueueItemForceUpdate(QueueItemUpdate):
def __init__(self, show=None):
ShowQueueItem.__init__(self, ShowQueueActions.FORCEUPDATE, show)
self.force = True