2014-03-10 01:18:05 -04:00
# Author: Nic Wolfe <nic@wolfeden.ca>
# URL: http://code.google.com/p/sickbeard/
#
2014-05-23 08:37:22 -04:00
# This file is part of SickRage.
2014-03-10 01:18:05 -04:00
#
2014-05-23 08:37:22 -04:00
# SickRage is free software: you can redistribute it and/or modify
2014-03-10 01:18:05 -04:00
# 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.
#
2014-05-23 08:37:22 -04:00
# SickRage is distributed in the hope that it will be useful,
2014-03-10 01:18:05 -04:00
# 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
2014-05-23 08:37:22 -04:00
# along with SickRage. If not, see <http://www.gnu.org/licenses/>.
2014-03-10 01:18:05 -04:00
from __future__ import with_statement
import os . path
import datetime
import threading
import re
import glob
2014-08-29 06:29:56 -04:00
import stat
2014-03-10 01:18:05 -04:00
import traceback
2014-10-02 07:20:03 -04:00
import shutil
2014-03-10 01:18:05 -04:00
import sickbeard
import xml . etree . cElementTree as etree
2014-07-06 09:11:04 -04:00
from name_parser . parser import NameParser , InvalidNameException , InvalidShowException
2014-03-10 01:18:05 -04:00
from lib import subliminal
2014-11-05 00:36:16 -05:00
try :
from lib . send2trash import send2trash
except ImportError :
pass
2014-03-10 01:18:05 -04:00
from lib . imdb import imdb
from sickbeard import db
from sickbeard import helpers , exceptions , logger
from sickbeard . exceptions import ex
from sickbeard import image_cache
from sickbeard import notifiers
from sickbeard import postProcessor
from sickbeard import subtitles
from sickbeard import history
from sickbeard import encodingKludge as ek
2014-03-20 04:15:22 -04:00
from common import Quality , Overview , statusStrings
2014-03-25 01:57:24 -04:00
from common import DOWNLOADED , SNATCHED , SNATCHED_PROPER , SNATCHED_BEST , ARCHIVED , IGNORED , UNAIRED , WANTED , SKIPPED , \
UNKNOWN , FAILED
from common import NAMING_DUPLICATE , NAMING_EXTEND , NAMING_LIMITED_EXTEND , NAMING_SEPARATED_REPEAT , \
NAMING_LIMITED_EXTEND_E_PREFIXED
2014-05-27 03:44:23 -04:00
2014-06-11 10:16:24 -04:00
2014-06-23 23:25:20 -04:00
def dirty_setter ( attr_name ) :
def wrapper ( self , val ) :
if getattr ( self , attr_name ) != val :
setattr ( self , attr_name , val )
self . dirty = True
return wrapper
2014-07-14 22:00:53 -04:00
2014-03-10 01:18:05 -04:00
class TVShow ( object ) :
def __init__ ( self , indexer , indexerid , lang = " " ) :
2014-06-23 23:25:20 -04:00
self . _indexerid = int ( indexerid )
self . _indexer = int ( indexer )
self . _name = " "
self . _imdbid = " "
self . _network = " "
self . _genre = " "
self . _classification = " "
self . _runtime = 0
self . _imdb_info = { }
self . _quality = int ( sickbeard . QUALITY_DEFAULT )
self . _flatten_folders = int ( sickbeard . FLATTEN_FOLDERS_DEFAULT )
self . _status = " "
self . _airs = " "
self . _startyear = 0
self . _paused = 0
self . _air_by_date = 0
2014-12-05 21:03:20 -05:00
self . _subtitles = int ( sickbeard . SUBTITLES_DEFAULT )
2014-06-23 23:25:20 -04:00
self . _dvdorder = 0
self . _archive_firstmatch = 0
self . _lang = lang
self . _last_update_indexer = 1
self . _sports = 0
self . _anime = 0
self . _scene = 0
self . _rls_ignore_words = " "
self . _rls_require_words = " "
2014-12-05 21:03:20 -05:00
self . _default_ep_status = SKIPPED
2014-06-23 23:25:20 -04:00
self . dirty = True
2014-04-24 01:18:16 -04:00
2014-07-07 23:27:24 -04:00
self . _location = " "
2014-03-10 01:18:05 -04:00
self . lock = threading . Lock ( )
2014-06-23 23:25:20 -04:00
self . isDirGood = False
2014-06-11 10:16:24 -04:00
self . episodes = { }
2014-07-09 12:01:12 -04:00
self . nextaired = " "
2014-03-10 01:18:05 -04:00
otherShow = helpers . findCertainShow ( sickbeard . showList , self . indexerid )
2014-03-20 14:03:22 -04:00
if otherShow != None :
2014-03-10 01:18:05 -04:00
raise exceptions . MultipleShowObjectsException ( " Can ' t create a show if it already exists " )
self . loadFromDB ( )
2014-06-23 23:25:20 -04:00
name = property ( lambda self : self . _name , dirty_setter ( " _name " ) )
indexerid = property ( lambda self : self . _indexerid , dirty_setter ( " _indexerid " ) )
indexer = property ( lambda self : self . _indexer , dirty_setter ( " _indexer " ) )
2014-07-14 22:00:53 -04:00
# location = property(lambda self: self._location, dirty_setter("_location"))
2014-06-23 23:25:20 -04:00
imdbid = property ( lambda self : self . _imdbid , dirty_setter ( " _imdbid " ) )
network = property ( lambda self : self . _network , dirty_setter ( " _network " ) )
genre = property ( lambda self : self . _genre , dirty_setter ( " _genre " ) )
classification = property ( lambda self : self . _classification , dirty_setter ( " _classification " ) )
runtime = property ( lambda self : self . _runtime , dirty_setter ( " _runtime " ) )
imdb_info = property ( lambda self : self . _imdb_info , dirty_setter ( " _imdb_info " ) )
quality = property ( lambda self : self . _quality , dirty_setter ( " _quality " ) )
flatten_folders = property ( lambda self : self . _flatten_folders , dirty_setter ( " _flatten_folders " ) )
status = property ( lambda self : self . _status , dirty_setter ( " _status " ) )
airs = property ( lambda self : self . _airs , dirty_setter ( " _airs " ) )
startyear = property ( lambda self : self . _startyear , dirty_setter ( " _startyear " ) )
paused = property ( lambda self : self . _paused , dirty_setter ( " _paused " ) )
air_by_date = property ( lambda self : self . _air_by_date , dirty_setter ( " _air_by_date " ) )
subtitles = property ( lambda self : self . _subtitles , dirty_setter ( " _subtitles " ) )
dvdorder = property ( lambda self : self . _dvdorder , dirty_setter ( " _dvdorder " ) )
archive_firstmatch = property ( lambda self : self . _archive_firstmatch , dirty_setter ( " _archive_firstmatch " ) )
lang = property ( lambda self : self . _lang , dirty_setter ( " _lang " ) )
last_update_indexer = property ( lambda self : self . _last_update_indexer , dirty_setter ( " _last_update_indexer " ) )
sports = property ( lambda self : self . _sports , dirty_setter ( " _sports " ) )
anime = property ( lambda self : self . _anime , dirty_setter ( " _anime " ) )
scene = property ( lambda self : self . _scene , dirty_setter ( " _scene " ) )
rls_ignore_words = property ( lambda self : self . _rls_ignore_words , dirty_setter ( " _rls_ignore_words " ) )
rls_require_words = property ( lambda self : self . _rls_require_words , dirty_setter ( " _rls_require_words " ) )
2014-11-23 04:17:37 -05:00
default_ep_status = property ( lambda self : self . _default_ep_status , dirty_setter ( " _default_ep_status " ) )
2014-07-14 22:00:53 -04:00
@property
def is_anime ( self ) :
if int ( self . anime ) > 0 :
2014-05-26 02:29:22 -04:00
return True
else :
return False
2014-05-27 03:44:23 -04:00
2014-07-14 22:00:53 -04:00
@property
def is_sports ( self ) :
if int ( self . sports ) > 0 :
2014-05-26 02:29:22 -04:00
return True
else :
return False
2014-05-27 03:44:23 -04:00
2014-07-14 22:00:53 -04:00
@property
def is_scene ( self ) :
if int ( self . scene ) > 0 :
2014-05-30 01:48:02 -04:00
return True
else :
return False
2014-03-10 01:18:05 -04:00
def _getLocation ( self ) :
# no dir check needed if missing show dirs are created during post-processing
if sickbeard . CREATE_MISSING_SHOW_DIRS :
return self . _location
if ek . ek ( os . path . isdir , self . _location ) :
return self . _location
else :
2014-03-26 15:28:46 -04:00
raise exceptions . ShowDirNotFoundException ( " Show folder doesn ' t exist, you shouldn ' t be using it " )
2014-03-10 01:18:05 -04:00
def _setLocation ( self , newLocation ) :
logger . log ( u " Setter sets location to " + newLocation , logger . DEBUG )
# Don't validate dir if user wants to add shows without creating a dir
if sickbeard . ADD_SHOWS_WO_DIR or ek . ek ( os . path . isdir , newLocation ) :
2014-07-07 23:27:24 -04:00
dirty_setter ( " _location " ) ( self , newLocation )
2014-03-10 01:18:05 -04:00
self . _isDirGood = True
else :
raise exceptions . NoNFOException ( " Invalid folder for the show! " )
2014-07-14 22:00:53 -04:00
2014-03-10 01:18:05 -04:00
location = property ( _getLocation , _setLocation )
# delete references to anything that's not in the internal lists
def flushEpisodes ( self ) :
2014-06-11 10:16:24 -04:00
for curSeason in self . episodes :
for curEp in self . episodes [ curSeason ] :
myEp = self . episodes [ curSeason ] [ curEp ]
self . episodes [ curSeason ] [ curEp ] = None
2014-03-10 01:18:05 -04:00
del myEp
def getAllEpisodes ( self , season = None , has_location = False ) :
sql_selection = " SELECT season, episode, "
# subselection to detect multi-episodes early, share_location > 0
2014-03-20 14:03:22 -04:00
sql_selection = sql_selection + " (SELECT COUNT (*) FROM tv_episodes WHERE showid = tve.showid AND season = tve.season AND location != ' ' AND location = tve.location AND episode != tve.episode) AS share_location "
2014-03-10 01:18:05 -04:00
sql_selection = sql_selection + " FROM tv_episodes tve WHERE showid = " + str ( self . indexerid )
if season is not None :
2014-05-15 23:39:46 -04:00
sql_selection = sql_selection + " AND season = " + str ( season )
2014-03-10 01:18:05 -04:00
if has_location :
2014-03-20 14:03:22 -04:00
sql_selection = sql_selection + " AND location != ' ' "
2014-03-10 01:18:05 -04:00
# need ORDER episode ASC to rename multi-episodes in order S01E01-02
2014-03-20 14:03:22 -04:00
sql_selection = sql_selection + " ORDER BY season ASC, episode ASC "
2014-03-10 01:18:05 -04:00
2014-06-21 18:46:59 -04:00
myDB = db . DBConnection ( )
results = myDB . select ( sql_selection )
ep_list = [ ]
for cur_result in results :
cur_ep = self . getEpisode ( int ( cur_result [ " season " ] ) , int ( cur_result [ " episode " ] ) )
2014-12-05 21:03:20 -05:00
if not cur_ep :
continue
cur_ep . relatedEps = [ ]
if cur_ep . location :
# if there is a location, check if it's a multi-episode (share_location > 0) and put them in relatedEps
if cur_result [ " share_location " ] > 0 :
related_eps_result = myDB . select (
" SELECT * FROM tv_episodes WHERE showid = ? AND season = ? AND location = ? AND episode != ? ORDER BY episode ASC " ,
[ self . indexerid , cur_ep . season , cur_ep . location , cur_ep . episode ] )
for cur_related_ep in related_eps_result :
related_ep = self . getEpisode ( int ( cur_related_ep [ " season " ] ) , int ( cur_related_ep [ " episode " ] ) )
if related_ep and related_ep not in cur_ep . relatedEps :
cur_ep . relatedEps . append ( related_ep )
ep_list . append ( cur_ep )
2014-03-10 01:18:05 -04:00
return ep_list
2014-05-30 03:36:47 -04:00
def getEpisode ( self , season = None , episode = None , file = None , noCreate = False , absolute_number = None , forceUpdate = False ) :
2014-03-10 01:18:05 -04:00
2014-05-26 02:29:22 -04:00
# if we get an anime get the real season and episode
2014-06-07 07:06:21 -04:00
if self . is_anime and absolute_number and not season and not episode :
2014-06-21 18:46:59 -04:00
myDB = db . DBConnection ( )
sql = " SELECT * FROM tv_episodes WHERE showid = ? and absolute_number = ? and season != 0 "
sqlResults = myDB . select ( sql , [ self . indexerid , absolute_number ] )
2014-05-26 02:29:22 -04:00
if len ( sqlResults ) == 1 :
episode = int ( sqlResults [ 0 ] [ " episode " ] )
season = int ( sqlResults [ 0 ] [ " season " ] )
2014-05-27 03:44:23 -04:00
logger . log (
" Found episode by absolute_number: " + str ( absolute_number ) + " which is " + str ( season ) + " x " + str (
episode ) , logger . DEBUG )
2014-05-26 02:29:22 -04:00
elif len ( sqlResults ) > 1 :
2014-05-27 03:44:23 -04:00
logger . log ( " Multiple entries for absolute number: " + str (
absolute_number ) + " in show: " + self . name + " found " , logger . ERROR )
2014-05-26 02:29:22 -04:00
return None
else :
2014-05-27 03:44:23 -04:00
logger . log (
" No entries for absolute number: " + str ( absolute_number ) + " in show: " + self . name + " found. " ,
logger . DEBUG )
2014-05-26 02:29:22 -04:00
return None
2014-06-11 10:16:24 -04:00
if not season in self . episodes :
self . episodes [ season ] = { }
2014-06-01 01:39:24 -04:00
2014-06-11 10:16:24 -04:00
if not episode in self . episodes [ season ] or self . episodes [ season ] [ episode ] is None :
2014-03-10 01:18:05 -04:00
if noCreate :
return None
2014-03-25 01:57:24 -04:00
logger . log ( str ( self . indexerid ) + u " : An object for episode " + str ( season ) + " x " + str (
episode ) + " didn ' t exist in the cache, trying to create it " , logger . DEBUG )
2014-03-10 01:18:05 -04:00
2014-06-04 21:28:59 -04:00
if file :
2014-05-04 23:04:46 -04:00
ep = TVEpisode ( self , season , episode , file )
2014-03-10 01:18:05 -04:00
else :
2014-05-05 01:50:28 -04:00
ep = TVEpisode ( self , season , episode )
2014-07-21 18:16:04 -04:00
if ep != None :
2014-06-11 10:16:24 -04:00
self . episodes [ season ] [ episode ] = ep
2014-03-10 01:18:05 -04:00
2014-07-21 18:16:04 -04:00
return self . episodes [ season ] [ episode ]
2014-03-10 01:18:05 -04:00
def should_update ( self , update_date = datetime . date . today ( ) ) :
2014-09-27 22:16:22 -04:00
2014-12-05 21:03:20 -05:00
# if show is not 'Ended' always update (status 'Continuing')
if not self . status or ' Ended ' not in self . status :
2014-03-10 01:18:05 -04:00
return True
# run logic against the current show latest aired and next unaired data to see if we should bypass 'Ended' status
graceperiod = datetime . timedelta ( days = 30 )
last_airdate = datetime . date . fromordinal ( 1 )
# get latest aired episode to compare against today - graceperiod and today + graceperiod
2014-06-21 18:46:59 -04:00
myDB = db . DBConnection ( )
sql_result = myDB . select (
" SELECT * FROM tv_episodes WHERE showid = ? AND season > ' 0 ' AND airdate > ' 1 ' AND status > ' 1 ' ORDER BY airdate DESC LIMIT 1 " ,
2014-12-05 21:03:20 -05:00
[ self . indexerid ] )
2014-06-21 18:46:59 -04:00
if sql_result :
last_airdate = datetime . date . fromordinal ( sql_result [ 0 ] [ ' airdate ' ] )
if last_airdate > = ( update_date - graceperiod ) and last_airdate < = ( update_date + graceperiod ) :
return True
2014-03-10 01:18:05 -04:00
2014-06-21 18:46:59 -04:00
# get next upcoming UNAIRED episode to compare against today + graceperiod
sql_result = myDB . select (
" SELECT * FROM tv_episodes WHERE showid = ? AND season > ' 0 ' AND airdate > ' 1 ' AND status = ' 1 ' ORDER BY airdate ASC LIMIT 1 " ,
2014-12-05 21:03:20 -05:00
[ self . indexerid ] )
2014-03-10 01:18:05 -04:00
2014-06-21 18:46:59 -04:00
if sql_result :
next_airdate = datetime . date . fromordinal ( sql_result [ 0 ] [ ' airdate ' ] )
if next_airdate < = ( update_date + graceperiod ) :
return True
2014-03-10 01:18:05 -04:00
last_update_indexer = datetime . date . fromordinal ( self . last_update_indexer )
# in the first year after ended (last airdate), update every 30 days
2014-03-25 01:57:24 -04:00
if ( update_date - last_airdate ) < datetime . timedelta ( days = 450 ) and (
2014-05-27 03:44:23 -04:00
update_date - last_update_indexer ) > datetime . timedelta ( days = 30 ) :
2014-03-10 01:18:05 -04:00
return True
return False
2014-05-14 05:42:08 -04:00
def writeShowNFO ( self ) :
2014-03-10 01:18:05 -04:00
result = False
if not ek . ek ( os . path . isdir , self . _location ) :
logger . log ( str ( self . indexerid ) + u " : Show dir doesn ' t exist, skipping NFO generation " )
return False
2014-03-25 01:57:24 -04:00
2014-03-10 01:18:05 -04:00
logger . log ( str ( self . indexerid ) + u " : Writing NFOs for show " )
for cur_provider in sickbeard . metadata_provider_dict . values ( ) :
2014-05-14 05:42:08 -04:00
result = cur_provider . create_show_metadata ( self ) or result
2014-03-10 01:18:05 -04:00
return result
2014-05-14 05:42:08 -04:00
def writeMetadata ( self , show_only = False ) :
2014-03-10 01:18:05 -04:00
if not ek . ek ( os . path . isdir , self . _location ) :
logger . log ( str ( self . indexerid ) + u " : Show dir doesn ' t exist, skipping NFO generation " )
return
self . getImages ( )
2014-05-14 05:42:08 -04:00
self . writeShowNFO ( )
2014-03-10 01:18:05 -04:00
if not show_only :
2014-05-14 05:42:08 -04:00
self . writeEpisodeNFOs ( )
2014-03-10 01:18:05 -04:00
2014-05-14 05:42:08 -04:00
def writeEpisodeNFOs ( self ) :
2014-03-10 01:18:05 -04:00
if not ek . ek ( os . path . isdir , self . _location ) :
logger . log ( str ( self . indexerid ) + u " : Show dir doesn ' t exist, skipping NFO generation " )
return
logger . log ( str ( self . indexerid ) + u " : Writing NFOs for all episodes " )
2014-06-21 18:46:59 -04:00
myDB = db . DBConnection ( )
sqlResults = myDB . select ( " SELECT * FROM tv_episodes WHERE showid = ? AND location != ' ' " , [ self . indexerid ] )
2014-03-10 01:18:05 -04:00
for epResult in sqlResults :
2014-03-25 01:57:24 -04:00
logger . log ( str ( self . indexerid ) + u " : Retrieving/creating episode " + str ( epResult [ " season " ] ) + " x " + str (
epResult [ " episode " ] ) , logger . DEBUG )
2014-03-10 01:18:05 -04:00
curEp = self . getEpisode ( epResult [ " season " ] , epResult [ " episode " ] )
2014-12-05 21:03:20 -05:00
if not curEp :
continue
2014-05-14 05:42:08 -04:00
curEp . createMetaFiles ( )
2014-03-10 01:18:05 -04:00
2014-05-14 05:42:08 -04:00
def updateMetadata ( self ) :
if not ek . ek ( os . path . isdir , self . _location ) :
logger . log ( str ( self . indexerid ) + u " : Show dir doesn ' t exist, skipping NFO generation " )
return
self . updateShowNFO ( )
def updateShowNFO ( self ) :
result = False
if not ek . ek ( os . path . isdir , self . _location ) :
logger . log ( str ( self . indexerid ) + u " : Show dir doesn ' t exist, skipping NFO generation " )
return False
logger . log ( str ( self . indexerid ) + u " : Updating NFOs for show with new indexer info " )
for cur_provider in sickbeard . metadata_provider_dict . values ( ) :
result = cur_provider . update_show_indexer_metadata ( self ) or result
return result
2014-03-10 01:18:05 -04:00
# find all media files in the show folder and create episodes for as many as possible
2014-05-03 23:16:26 -04:00
def loadEpisodesFromDir ( self ) :
2014-03-10 01:18:05 -04:00
if not ek . ek ( os . path . isdir , self . _location ) :
logger . log ( str ( self . indexerid ) + u " : Show dir doesn ' t exist, not loading episodes from disk " )
return
logger . log ( str ( self . indexerid ) + u " : Loading all episodes from the show directory " + self . _location )
# get file list
mediaFiles = helpers . listMediaFiles ( self . _location )
# create TVEpisodes from each media file (if possible)
2014-05-30 06:01:49 -04:00
sql_l = [ ]
2014-03-10 01:18:05 -04:00
for mediaFile in mediaFiles :
2014-04-27 06:31:54 -04:00
parse_result = None
2014-03-10 01:18:05 -04:00
curEpisode = None
2014-04-27 06:31:54 -04:00
2014-03-10 01:18:05 -04:00
logger . log ( str ( self . indexerid ) + u " : Creating episode from " + mediaFile , logger . DEBUG )
try :
curEpisode = self . makeEpFromFile ( ek . ek ( os . path . join , self . _location , mediaFile ) )
except ( exceptions . ShowNotFoundException , exceptions . EpisodeNotFoundException ) , e :
logger . log ( u " Episode " + mediaFile + " returned an exception: " + ex ( e ) , logger . ERROR )
continue
except exceptions . EpisodeDeletedException :
logger . log ( u " The episode deleted itself when I tried making an object for it " , logger . DEBUG )
if curEpisode is None :
continue
# see if we should save the release name in the db
ep_file_name = ek . ek ( os . path . basename , curEpisode . location )
ep_file_name = ek . ek ( os . path . splitext , ep_file_name ) [ 0 ]
try :
2014-04-27 07:46:21 -04:00
parse_result = None
2014-07-24 00:44:11 -04:00
np = NameParser ( False , showObj = self , tryIndexers = True )
2014-03-10 01:18:05 -04:00
parse_result = np . parse ( ep_file_name )
2014-07-06 09:11:04 -04:00
except ( InvalidNameException , InvalidShowException ) :
2014-03-10 01:18:05 -04:00
pass
if not ' ' in ep_file_name and parse_result and parse_result . release_group :
2014-03-25 01:57:24 -04:00
logger . log (
u " Name " + ep_file_name + u " gave release group of " + parse_result . release_group + " , seems valid " ,
logger . DEBUG )
2014-03-10 01:18:05 -04:00
curEpisode . release_name = ep_file_name
# store the reference in the show
2014-03-20 14:03:22 -04:00
if curEpisode != None :
2014-03-10 01:18:05 -04:00
if self . subtitles :
try :
curEpisode . refreshSubtitles ( )
except :
logger . log ( str ( self . indexerid ) + " : Could not refresh subtitles " , logger . ERROR )
logger . log ( traceback . format_exc ( ) , logger . DEBUG )
2014-05-30 06:01:49 -04:00
sql_l . append ( curEpisode . get_sql ( ) )
2014-07-14 22:00:53 -04:00
if len ( sql_l ) > 0 :
2014-06-21 18:46:59 -04:00
myDB = db . DBConnection ( )
myDB . mass_action ( sql_l )
2014-06-30 11:57:32 -04:00
2014-03-25 01:57:24 -04:00
2014-03-10 01:18:05 -04:00
def loadEpisodesFromDB ( self ) :
logger . log ( u " Loading all episodes from the DB " )
2014-06-21 18:46:59 -04:00
myDB = db . DBConnection ( )
sql = " SELECT * FROM tv_episodes WHERE showid = ? "
sqlResults = myDB . select ( sql , [ self . indexerid ] )
2014-03-10 01:18:05 -04:00
scannedEps = { }
2014-03-26 15:28:46 -04:00
lINDEXER_API_PARMS = sickbeard . indexerApi ( self . indexer ) . api_params . copy ( )
2014-03-10 01:18:05 -04:00
if self . lang :
2014-03-12 01:28:30 -04:00
lINDEXER_API_PARMS [ ' language ' ] = self . lang
2014-03-10 01:18:05 -04:00
if self . dvdorder != 0 :
2014-03-12 01:28:30 -04:00
lINDEXER_API_PARMS [ ' dvdorder ' ] = True
2014-03-10 01:18:05 -04:00
2014-03-26 15:28:46 -04:00
t = sickbeard . indexerApi ( self . indexer ) . indexer ( * * lINDEXER_API_PARMS )
2014-03-10 01:18:05 -04:00
cachedShow = t [ self . indexerid ]
cachedSeasons = { }
for curResult in sqlResults :
deleteEp = False
curSeason = int ( curResult [ " season " ] )
curEpisode = int ( curResult [ " episode " ] )
2014-05-03 20:22:33 -04:00
2014-03-10 01:18:05 -04:00
if curSeason not in cachedSeasons :
try :
cachedSeasons [ curSeason ] = cachedShow [ curSeason ]
2014-03-25 01:57:24 -04:00
except sickbeard . indexer_seasonnotfound , e :
logger . log ( u " Error when trying to load the episode from " + sickbeard . indexerApi (
self . indexer ) . name + " : " + e . message , logger . WARNING )
2014-03-10 01:18:05 -04:00
deleteEp = True
if not curSeason in scannedEps :
scannedEps [ curSeason ] = { }
logger . log ( u " Loading episode " + str ( curSeason ) + " x " + str ( curEpisode ) + " from the DB " , logger . DEBUG )
try :
curEp = self . getEpisode ( curSeason , curEpisode )
2014-12-05 21:03:20 -05:00
if not curEp :
raise exceptions . EpisodeNotFoundException
2014-03-10 01:18:05 -04:00
2014-03-20 14:03:22 -04:00
# if we found out that the ep is no longer on TVDB then delete it from our database too
2014-03-10 01:18:05 -04:00
if deleteEp :
curEp . deleteEpisode ( )
2014-03-25 01:57:24 -04:00
2014-03-10 01:18:05 -04:00
curEp . loadFromDB ( curSeason , curEpisode )
curEp . loadFromIndexer ( tvapi = t , cachedSeason = cachedSeasons [ curSeason ] )
scannedEps [ curSeason ] [ curEpisode ] = True
except exceptions . EpisodeDeletedException :
2014-03-25 01:57:24 -04:00
logger . log ( u " Tried loading an episode from the DB that should have been deleted, skipping it " ,
logger . DEBUG )
2014-03-10 01:18:05 -04:00
continue
return scannedEps
def loadEpisodesFromIndexer ( self , cache = True ) :
2014-03-26 15:28:46 -04:00
lINDEXER_API_PARMS = sickbeard . indexerApi ( self . indexer ) . api_params . copy ( )
2014-03-10 01:18:05 -04:00
if not cache :
2014-03-12 01:28:30 -04:00
lINDEXER_API_PARMS [ ' cache ' ] = False
2014-03-10 01:18:05 -04:00
if self . lang :
2014-03-12 01:28:30 -04:00
lINDEXER_API_PARMS [ ' language ' ] = self . lang
2014-03-10 01:18:05 -04:00
if self . dvdorder != 0 :
2014-03-12 01:28:30 -04:00
lINDEXER_API_PARMS [ ' dvdorder ' ] = True
2014-03-10 01:18:05 -04:00
try :
2014-03-26 15:28:46 -04:00
t = sickbeard . indexerApi ( self . indexer ) . indexer ( * * lINDEXER_API_PARMS )
2014-03-10 01:18:05 -04:00
showObj = t [ self . indexerid ]
2014-03-25 01:57:24 -04:00
except sickbeard . indexer_error :
logger . log ( u " " + sickbeard . indexerApi (
self . indexer ) . name + " timed out, unable to update episodes from " + sickbeard . indexerApi (
self . indexer ) . name , logger . ERROR )
2014-03-10 01:18:05 -04:00
return None
2014-03-25 01:57:24 -04:00
logger . log (
str ( self . indexerid ) + u " : Loading all episodes from " + sickbeard . indexerApi ( self . indexer ) . name + " .. " )
2014-03-10 01:18:05 -04:00
scannedEps = { }
2014-03-20 06:24:58 -04:00
sql_l = [ ]
2014-03-10 01:18:05 -04:00
for season in showObj :
scannedEps [ season ] = { }
for episode in showObj [ season ] :
# need some examples of wtf episode 0 means to decide if we want it or not
if episode == 0 :
continue
try :
ep = self . getEpisode ( season , episode )
2014-12-05 21:03:20 -05:00
if not ep :
raise exceptions . EpisodeNotFoundException
2014-03-10 01:18:05 -04:00
except exceptions . EpisodeNotFoundException :
2014-03-25 01:57:24 -04:00
logger . log (
str ( self . indexerid ) + " : " + sickbeard . indexerApi ( self . indexer ) . name + " object for " + str (
season ) + " x " + str ( episode ) + " is incomplete, skipping this episode " )
2014-03-10 01:18:05 -04:00
continue
else :
try :
ep . loadFromIndexer ( tvapi = t )
except exceptions . EpisodeDeletedException :
logger . log ( u " The episode was deleted, skipping the rest of the load " )
continue
with ep . lock :
2014-03-25 01:57:24 -04:00
logger . log ( str ( self . indexerid ) + u " : Loading info from " + sickbeard . indexerApi (
self . indexer ) . name + " for episode " + str ( season ) + " x " + str ( episode ) , logger . DEBUG )
2014-03-10 01:18:05 -04:00
ep . loadFromIndexer ( season , episode , tvapi = t )
2014-05-30 06:01:49 -04:00
sql_l . append ( ep . get_sql ( ) )
2014-03-10 01:18:05 -04:00
scannedEps [ season ] [ episode ] = True
2014-07-14 22:00:53 -04:00
if len ( sql_l ) > 0 :
2014-06-21 18:46:59 -04:00
myDB = db . DBConnection ( )
myDB . mass_action ( sql_l )
2014-06-30 11:57:32 -04:00
2014-03-10 01:18:05 -04:00
# Done updating save last update date
self . last_update_indexer = datetime . date . today ( ) . toordinal ( )
2014-12-02 07:59:26 -05:00
2014-03-10 01:18:05 -04:00
self . saveToDB ( )
return scannedEps
def getImages ( self , fanart = None , poster = None ) :
fanart_result = poster_result = banner_result = False
season_posters_result = season_banners_result = season_all_poster_result = season_all_banner_result = False
for cur_provider in sickbeard . metadata_provider_dict . values ( ) :
# FIXME: Needs to not show this message if the option is not enabled?
logger . log ( u " Running metadata routines for " + cur_provider . name , logger . DEBUG )
fanart_result = cur_provider . create_fanart ( self ) or fanart_result
poster_result = cur_provider . create_poster ( self ) or poster_result
banner_result = cur_provider . create_banner ( self ) or banner_result
season_posters_result = cur_provider . create_season_posters ( self ) or season_posters_result
season_banners_result = cur_provider . create_season_banners ( self ) or season_banners_result
season_all_poster_result = cur_provider . create_season_all_poster ( self ) or season_all_poster_result
season_all_banner_result = cur_provider . create_season_all_banner ( self ) or season_all_banner_result
return fanart_result or poster_result or banner_result or season_posters_result or season_banners_result or season_all_poster_result or season_all_banner_result
# make a TVEpisode object from a media file
def makeEpFromFile ( self , file ) :
if not ek . ek ( os . path . isfile , file ) :
logger . log ( str ( self . indexerid ) + u " : That isn ' t even a real file dude... " + file )
return None
logger . log ( str ( self . indexerid ) + u " : Creating episode object from " + file , logger . DEBUG )
try :
2014-07-24 14:16:59 -04:00
myParser = NameParser ( showObj = self , tryIndexers = True )
2014-03-10 01:18:05 -04:00
parse_result = myParser . parse ( file )
except InvalidNameException :
2014-07-11 02:44:36 -04:00
logger . log ( u " Unable to parse the filename " + file + " into a valid episode " , logger . DEBUG )
2014-03-10 01:18:05 -04:00
return None
2014-07-06 13:12:18 -04:00
except InvalidShowException :
2014-07-11 02:44:36 -04:00
logger . log ( u " Unable to parse the filename " + file + " into a valid show " , logger . DEBUG )
2014-07-06 13:12:18 -04:00
return None
2014-03-10 01:18:05 -04:00
2014-07-24 14:16:59 -04:00
if not len ( parse_result . episode_numbers ) :
2014-03-10 01:18:05 -04:00
logger . log ( " parse_result: " + str ( parse_result ) )
logger . log ( u " No episode number found in " + file + " , ignoring it " , logger . ERROR )
return None
# for now lets assume that any episode in the show dir belongs to that show
2014-03-20 14:03:22 -04:00
season = parse_result . season_number if parse_result . season_number != None else 1
2014-03-10 01:18:05 -04:00
episodes = parse_result . episode_numbers
rootEp = None
2014-05-30 06:01:49 -04:00
sql_l = [ ]
2014-03-10 01:18:05 -04:00
for curEpNum in episodes :
episode = int ( curEpNum )
2014-03-25 01:57:24 -04:00
logger . log (
str ( self . indexerid ) + " : " + file + " parsed to " + self . name + " " + str ( season ) + " x " + str ( episode ) ,
logger . DEBUG )
2014-03-10 01:18:05 -04:00
checkQualityAgain = False
same_file = False
2014-12-05 21:03:20 -05:00
curEp = self . getEpisode ( season , episode )
if not curEp :
2014-03-10 01:18:05 -04:00
try :
curEp = self . getEpisode ( season , episode , file )
2014-12-05 21:03:20 -05:00
if not curEp :
raise exceptions . EpisodeNotFoundException
2014-03-10 01:18:05 -04:00
except exceptions . EpisodeNotFoundException :
2014-03-25 01:57:24 -04:00
logger . log ( str ( self . indexerid ) + u " : Unable to figure out what this file is, skipping " ,
logger . ERROR )
2014-03-10 01:18:05 -04:00
continue
else :
# if there is a new file associated with this ep then re-check the quality
if curEp . location and ek . ek ( os . path . normpath , curEp . location ) != ek . ek ( os . path . normpath , file ) :
2014-03-25 01:57:24 -04:00
logger . log (
u " The old episode had a different file associated with it, I will re-check the quality based on the new filename " + file ,
logger . DEBUG )
2014-03-10 01:18:05 -04:00
checkQualityAgain = True
with curEp . lock :
old_size = curEp . file_size
curEp . location = file
# if the sizes are the same then it's probably the same file
if old_size and curEp . file_size == old_size :
same_file = True
else :
same_file = False
curEp . checkForMetaFiles ( )
2014-03-20 14:03:22 -04:00
if rootEp == None :
2014-03-10 01:18:05 -04:00
rootEp = curEp
else :
if curEp not in rootEp . relatedEps :
2014-12-05 21:03:20 -05:00
with rootEp . lock :
rootEp . relatedEps . append ( curEp )
2014-03-10 01:18:05 -04:00
# if it's a new file then
if not same_file :
2014-12-05 21:03:20 -05:00
with curEp . lock :
curEp . release_name = ' '
2014-03-10 01:18:05 -04:00
# if they replace a file on me I'll make some attempt at re-checking the quality unless I know it's the same file
if checkQualityAgain and not same_file :
2014-05-26 02:29:22 -04:00
newQuality = Quality . nameQuality ( file , self . is_anime )
2014-03-25 01:57:24 -04:00
logger . log ( u " Since this file has been renamed, I checked " + file + " and found quality " +
Quality . qualityStrings [ newQuality ] , logger . DEBUG )
2014-03-10 01:18:05 -04:00
if newQuality != Quality . UNKNOWN :
2014-12-05 21:03:20 -05:00
with curEp . lock :
curEp . status = Quality . compositeStatus ( DOWNLOADED , newQuality )
2014-03-10 01:18:05 -04:00
# check for status/quality changes as long as it's a new file
2014-03-25 01:57:24 -04:00
elif not same_file and sickbeard . helpers . isMediaFile ( file ) and curEp . status not in Quality . DOWNLOADED + [
ARCHIVED , IGNORED ] :
2014-03-10 01:18:05 -04:00
oldStatus , oldQuality = Quality . splitCompositeStatus ( curEp . status )
2014-05-26 02:29:22 -04:00
newQuality = Quality . nameQuality ( file , self . is_anime )
2014-03-10 01:18:05 -04:00
if newQuality == Quality . UNKNOWN :
newQuality = Quality . assumeQuality ( file )
newStatus = None
# if it was snatched and now exists then set the status correctly
if oldStatus == SNATCHED and oldQuality < = newQuality :
2014-03-25 01:57:24 -04:00
logger . log ( u " STATUS: this ep used to be snatched with quality " + Quality . qualityStrings [
oldQuality ] + u " but a file exists with quality " + Quality . qualityStrings [
newQuality ] + u " so I ' m setting the status to DOWNLOADED " , logger . DEBUG )
2014-03-10 01:18:05 -04:00
newStatus = DOWNLOADED
# if it was snatched proper and we found a higher quality one then allow the status change
elif oldStatus == SNATCHED_PROPER and oldQuality < newQuality :
2014-03-25 01:57:24 -04:00
logger . log ( u " STATUS: this ep used to be snatched proper with quality " + Quality . qualityStrings [
oldQuality ] + u " but a file exists with quality " + Quality . qualityStrings [
newQuality ] + u " so I ' m setting the status to DOWNLOADED " , logger . DEBUG )
2014-03-10 01:18:05 -04:00
newStatus = DOWNLOADED
elif oldStatus not in ( SNATCHED , SNATCHED_PROPER ) :
newStatus = DOWNLOADED
2014-12-02 07:59:26 -05:00
if newStatus is not None :
2014-03-10 01:18:05 -04:00
with curEp . lock :
2014-03-25 01:57:24 -04:00
logger . log ( u " STATUS: we have an associated file, so setting the status from " + str (
2014-08-18 08:40:29 -04:00
curEp . status ) + u " to DOWNLOADED/ " + str ( Quality . statusFromName ( file , anime = self . is_anime ) ) ,
logger . DEBUG )
2014-03-10 01:18:05 -04:00
curEp . status = Quality . compositeStatus ( newStatus , newQuality )
with curEp . lock :
2014-05-30 06:01:49 -04:00
sql_l . append ( curEp . get_sql ( ) )
2014-07-14 22:00:53 -04:00
if len ( sql_l ) > 0 :
2014-06-21 18:46:59 -04:00
myDB = db . DBConnection ( )
myDB . mass_action ( sql_l )
2014-06-30 11:57:32 -04:00
2014-03-10 01:18:05 -04:00
# creating metafiles on the root should be good enough
2014-12-05 21:03:20 -05:00
if rootEp :
2014-03-10 01:18:05 -04:00
with rootEp . lock :
rootEp . createMetaFiles ( )
return rootEp
def loadFromDB ( self , skipNFO = False ) :
logger . log ( str ( self . indexerid ) + u " : Loading show info from database " )
2014-06-21 18:46:59 -04:00
myDB = db . DBConnection ( )
sqlResults = myDB . select ( " SELECT * FROM tv_shows WHERE indexer_id = ? " , [ self . indexerid ] )
2014-03-10 01:18:05 -04:00
if len ( sqlResults ) > 1 :
raise exceptions . MultipleDBShowsException ( )
elif len ( sqlResults ) == 0 :
logger . log ( str ( self . indexerid ) + " : Unable to find the show in the database " )
return
else :
2014-12-05 21:03:20 -05:00
self . indexer = int ( sqlResults [ 0 ] [ " indexer " ] or 0 )
2014-03-10 01:18:05 -04:00
if not self . name :
self . name = sqlResults [ 0 ] [ " show_name " ]
if not self . network :
self . network = sqlResults [ 0 ] [ " network " ]
if not self . genre :
self . genre = sqlResults [ 0 ] [ " genre " ]
2014-12-05 21:03:20 -05:00
if not self . classification :
2014-03-10 01:18:05 -04:00
self . classification = sqlResults [ 0 ] [ " classification " ]
self . runtime = sqlResults [ 0 ] [ " runtime " ]
self . status = sqlResults [ 0 ] [ " status " ]
2014-12-05 21:03:20 -05:00
if self . status is None :
2014-03-10 01:18:05 -04:00
self . status = " "
2014-11-23 04:17:37 -05:00
2014-03-10 01:18:05 -04:00
self . airs = sqlResults [ 0 ] [ " airs " ]
2014-12-05 21:03:20 -05:00
if self . airs is None :
2014-03-10 01:18:05 -04:00
self . airs = " "
2014-11-23 04:17:37 -05:00
2014-12-05 21:03:20 -05:00
self . startyear = int ( sqlResults [ 0 ] [ " startyear " ] or 0 )
self . air_by_date = int ( sqlResults [ 0 ] [ " air_by_date " ] or 0 )
self . anime = int ( sqlResults [ 0 ] [ " anime " ] or 0 )
self . sports = int ( sqlResults [ 0 ] [ " sports " ] or 0 )
self . scene = int ( sqlResults [ 0 ] [ " scene " ] or 0 )
self . subtitles = int ( sqlResults [ 0 ] [ " subtitles " ] or 0 )
self . dvdorder = int ( sqlResults [ 0 ] [ " dvdorder " ] or 0 )
self . archive_firstmatch = int ( sqlResults [ 0 ] [ " archive_firstmatch " ] or 0 )
self . quality = int ( sqlResults [ 0 ] [ " quality " ] or UNKNOWN )
self . flatten_folders = int ( sqlResults [ 0 ] [ " flatten_folders " ] or 0 )
self . paused = int ( sqlResults [ 0 ] [ " paused " ] or 0 )
2014-03-10 01:18:05 -04:00
2014-10-28 02:43:53 -04:00
try :
self . location = sqlResults [ 0 ] [ " location " ]
except Exception :
dirty_setter ( " _location " ) ( self , sqlResults [ 0 ] [ " location " ] )
self . _isDirGood = False
2014-03-10 01:18:05 -04:00
if not self . lang :
self . lang = sqlResults [ 0 ] [ " lang " ]
self . last_update_indexer = sqlResults [ 0 ] [ " last_update_indexer " ]
2014-04-24 01:18:16 -04:00
self . rls_ignore_words = sqlResults [ 0 ] [ " rls_ignore_words " ]
self . rls_require_words = sqlResults [ 0 ] [ " rls_require_words " ]
2014-12-05 21:03:20 -05:00
self . default_ep_status = int ( sqlResults [ 0 ] [ " default_ep_status " ] or SKIPPED )
2014-11-23 04:17:37 -05:00
2014-03-10 01:18:05 -04:00
if not self . imdbid :
2014-03-25 01:57:24 -04:00
self . imdbid = sqlResults [ 0 ] [ " imdb_id " ]
2014-03-10 01:18:05 -04:00
2014-05-27 03:44:23 -04:00
# Get IMDb_info from database
2014-06-21 18:46:59 -04:00
myDB = db . DBConnection ( )
sqlResults = myDB . select ( " SELECT * FROM imdb_info WHERE indexer_id = ? " , [ self . indexerid ] )
2014-03-10 01:18:05 -04:00
if len ( sqlResults ) == 0 :
logger . log ( str ( self . indexerid ) + " : Unable to find IMDb show info in the database " )
return
else :
self . imdb_info = dict ( zip ( sqlResults [ 0 ] . keys ( ) , sqlResults [ 0 ] ) )
2014-06-23 23:25:20 -04:00
self . dirty = False
return True
2014-03-10 01:18:05 -04:00
def loadFromIndexer ( self , cache = True , tvapi = None , cachedSeason = None ) :
2014-03-25 01:57:24 -04:00
logger . log ( str ( self . indexerid ) + u " : Loading show info from " + sickbeard . indexerApi ( self . indexer ) . name )
2014-03-10 01:18:05 -04:00
# There's gotta be a better way of doing this but we don't wanna
# change the cache value elsewhere
if tvapi is None :
2014-03-26 15:28:46 -04:00
lINDEXER_API_PARMS = sickbeard . indexerApi ( self . indexer ) . api_params . copy ( )
2014-03-10 01:18:05 -04:00
if not cache :
2014-03-12 01:28:30 -04:00
lINDEXER_API_PARMS [ ' cache ' ] = False
2014-03-10 01:18:05 -04:00
if self . lang :
2014-03-12 01:28:30 -04:00
lINDEXER_API_PARMS [ ' language ' ] = self . lang
2014-03-10 01:18:05 -04:00
if self . dvdorder != 0 :
2014-03-12 01:28:30 -04:00
lINDEXER_API_PARMS [ ' dvdorder ' ] = True
2014-03-10 01:18:05 -04:00
2014-03-26 15:28:46 -04:00
t = sickbeard . indexerApi ( self . indexer ) . indexer ( * * lINDEXER_API_PARMS )
2014-03-10 01:18:05 -04:00
else :
t = tvapi
myEp = t [ self . indexerid ]
2014-03-11 15:39:25 -04:00
try :
2014-05-29 09:27:05 -04:00
self . name = myEp [ ' seriesname ' ] . strip ( )
2014-03-11 15:39:25 -04:00
except AttributeError :
2014-03-25 01:57:24 -04:00
raise sickbeard . indexer_attributenotfound (
" Found %s , but attribute ' seriesname ' was empty. " % ( self . indexerid ) )
2014-03-10 01:18:05 -04:00
self . classification = getattr ( myEp , ' classification ' , ' Scripted ' )
self . genre = getattr ( myEp , ' genre ' , ' ' )
self . network = getattr ( myEp , ' network ' , ' ' )
self . runtime = getattr ( myEp , ' runtime ' , ' ' )
self . imdbid = getattr ( myEp , ' imdb_id ' , ' ' )
if getattr ( myEp , ' airs_dayofweek ' , None ) is not None and getattr ( myEp , ' airs_time ' , None ) is not None :
self . airs = myEp [ " airs_dayofweek " ] + " " + myEp [ " airs_time " ]
2014-12-05 21:03:20 -05:00
if self . airs is None :
self . airs = ' '
2014-03-10 01:18:05 -04:00
if getattr ( myEp , ' firstaired ' , None ) is not None :
2014-05-29 01:40:12 -04:00
self . startyear = int ( str ( myEp [ " firstaired " ] ) . split ( ' - ' ) [ 0 ] )
2014-03-10 01:18:05 -04:00
self . status = getattr ( myEp , ' status ' , ' ' )
def loadIMDbInfo ( self , imdbapi = None ) :
2014-03-25 01:57:24 -04:00
imdb_info = { ' imdb_id ' : self . imdbid ,
' title ' : ' ' ,
' year ' : ' ' ,
' akas ' : [ ] ,
' runtimes ' : ' ' ,
' genres ' : [ ] ,
' countries ' : ' ' ,
2014-04-24 16:21:04 -04:00
' country_codes ' : [ ] ,
2014-03-25 01:57:24 -04:00
' certificates ' : [ ] ,
' rating ' : ' ' ,
2014-03-10 01:18:05 -04:00
' votes ' : ' ' ,
' last_update ' : ' '
2014-03-25 01:57:24 -04:00
}
2014-12-03 01:13:28 -05:00
i = imdb . IMDb ( )
if not self . imdbid :
self . imdbid = i . title2imdbID ( self . name , kind = ' tv series ' )
2014-03-10 01:18:05 -04:00
if self . imdbid :
logger . log ( str ( self . indexerid ) + u " : Loading show info from IMDb " )
2014-03-25 01:57:24 -04:00
2014-03-10 01:18:05 -04:00
imdbTv = i . get_movie ( str ( re . sub ( " [^0-9] " , " " , self . imdbid ) ) )
2014-03-25 01:57:24 -04:00
2014-04-24 16:21:04 -04:00
for key in filter ( lambda x : x . replace ( ' _ ' , ' ' ) in imdbTv . keys ( ) , imdb_info . keys ( ) ) :
2014-03-10 01:18:05 -04:00
# Store only the first value for string type
if type ( imdb_info [ key ] ) == type ( ' ' ) and type ( imdbTv . get ( key ) ) == type ( [ ] ) :
2014-04-24 16:21:04 -04:00
imdb_info [ key ] = imdbTv . get ( key . replace ( ' _ ' , ' ' ) ) [ 0 ]
2014-03-10 01:18:05 -04:00
else :
2014-04-24 16:21:04 -04:00
imdb_info [ key ] = imdbTv . get ( key . replace ( ' _ ' , ' ' ) )
2014-03-25 01:57:24 -04:00
2014-05-27 03:44:23 -04:00
# Filter only the value
2014-06-07 14:36:26 -04:00
if imdb_info [ ' runtimes ' ] :
imdb_info [ ' runtimes ' ] = re . search ( ' \ d+ ' , imdb_info [ ' runtimes ' ] ) . group ( 0 )
else :
imdb_info [ ' runtimes ' ] = self . runtime
if imdb_info [ ' akas ' ] :
imdb_info [ ' akas ' ] = ' | ' . join ( imdb_info [ ' akas ' ] )
else :
imdb_info [ ' akas ' ] = ' '
2014-03-25 01:57:24 -04:00
2014-06-07 11:33:18 -04:00
# Join all genres in a string
2014-06-07 14:36:26 -04:00
if imdb_info [ ' genres ' ] :
imdb_info [ ' genres ' ] = ' | ' . join ( imdb_info [ ' genres ' ] )
else :
imdb_info [ ' genres ' ] = ' '
2014-03-25 01:57:24 -04:00
2014-06-07 11:33:18 -04:00
# Get only the production country certificate if any
2014-03-10 01:18:05 -04:00
if imdb_info [ ' certificates ' ] and imdb_info [ ' countries ' ] :
dct = { }
try :
for item in imdb_info [ ' certificates ' ] :
dct [ item . split ( ' : ' ) [ 0 ] ] = item . split ( ' : ' ) [ 1 ]
2014-03-25 01:57:24 -04:00
2014-03-10 01:18:05 -04:00
imdb_info [ ' certificates ' ] = dct [ imdb_info [ ' countries ' ] ]
except :
2014-03-25 01:57:24 -04:00
imdb_info [ ' certificates ' ] = ' '
2014-03-10 01:18:05 -04:00
else :
2014-03-25 01:57:24 -04:00
imdb_info [ ' certificates ' ] = ' '
2014-06-07 14:36:26 -04:00
if imdb_info [ ' country_codes ' ] :
imdb_info [ ' country_codes ' ] = ' | ' . join ( imdb_info [ ' country_codes ' ] )
else :
imdb_info [ ' country_codes ' ] = ' '
2014-03-10 01:18:05 -04:00
imdb_info [ ' last_update ' ] = datetime . date . today ( ) . toordinal ( )
2014-03-25 01:57:24 -04:00
2014-05-27 03:44:23 -04:00
# Rename dict keys without spaces for DB upsert
2014-03-25 01:57:24 -04:00
self . imdb_info = dict (
2014-05-21 23:12:15 -04:00
( k . replace ( ' ' , ' _ ' ) , k ( v ) if hasattr ( v , ' keys ' ) else v ) for k , v in imdb_info . items ( ) )
2014-03-25 01:57:24 -04:00
logger . log ( str ( self . indexerid ) + u " : Obtained info from IMDb -> " + str ( self . imdb_info ) , logger . DEBUG )
2014-03-10 01:18:05 -04:00
def nextEpisode ( self ) :
logger . log ( str ( self . indexerid ) + " : Finding the episode which airs next " , logger . DEBUG )
2014-07-09 03:15:27 -04:00
curDate = datetime . date . today ( ) . toordinal ( )
if not self . nextaired or self . nextaired and curDate > self . nextaired :
myDB = db . DBConnection ( )
2014-07-14 22:00:53 -04:00
sqlResults = myDB . select (
" SELECT airdate, season, episode FROM tv_episodes WHERE showid = ? AND airdate >= ? AND status in (?,?) ORDER BY airdate ASC LIMIT 1 " ,
[ self . indexerid , datetime . date . today ( ) . toordinal ( ) , UNAIRED , WANTED ] )
2014-03-10 01:18:05 -04:00
2014-07-09 03:15:27 -04:00
if sqlResults == None or len ( sqlResults ) == 0 :
logger . log ( str ( self . indexerid ) + u " : No episode found... need to implement a show status " ,
logger . DEBUG )
2014-07-09 12:01:12 -04:00
self . nextaired = " "
2014-07-09 03:15:27 -04:00
else :
logger . log ( str ( self . indexerid ) + u " : Found episode " + str ( sqlResults [ 0 ] [ " season " ] ) + " x " + str (
sqlResults [ 0 ] [ " episode " ] ) , logger . DEBUG )
self . nextaired = sqlResults [ 0 ] [ ' airdate ' ]
return self . nextaired
2014-03-10 01:18:05 -04:00
2014-08-29 06:29:56 -04:00
def deleteShow ( self , full = False ) :
2014-03-10 01:18:05 -04:00
2014-03-20 06:24:58 -04:00
sql_l = [ [ " DELETE FROM tv_episodes WHERE showid = ? " , [ self . indexerid ] ] ,
2014-03-25 01:57:24 -04:00
[ " DELETE FROM tv_shows WHERE indexer_id = ? " , [ self . indexerid ] ] ,
2014-05-29 09:27:05 -04:00
[ " DELETE FROM imdb_info WHERE indexer_id = ? " , [ self . indexerid ] ] ,
[ " DELETE FROM xem_refresh WHERE indexer_id = ? " , [ self . indexerid ] ] ,
[ " DELETE FROM scene_numbering WHERE indexer_id = ? " , [ self . indexerid ] ] ]
2014-03-20 06:24:58 -04:00
2014-06-21 18:46:59 -04:00
myDB = db . DBConnection ( )
myDB . mass_action ( sql_l )
2014-06-30 11:57:32 -04:00
2014-11-05 00:36:16 -05:00
action = ( ' delete ' , ' trash ' ) [ sickbeard . TRASH_REMOVE_SHOW ]
2014-03-10 01:18:05 -04:00
# remove self from show list
2014-03-25 01:57:24 -04:00
sickbeard . showList = [ x for x in sickbeard . showList if int ( x . indexerid ) != self . indexerid ]
2014-03-10 01:18:05 -04:00
# clear the cache
image_cache_dir = ek . ek ( os . path . join , sickbeard . CACHE_DIR , ' images ' )
for cache_file in ek . ek ( glob . glob , ek . ek ( os . path . join , image_cache_dir , str ( self . indexerid ) + ' .* ' ) ) :
2014-11-05 00:36:16 -05:00
logger . log ( u ' Attempt to %s cache file %s ' % ( action , cache_file ) )
try :
if sickbeard . TRASH_REMOVE_SHOW :
send2trash ( cache_file )
else :
os . remove ( cache_file )
except OSError , e :
logger . log ( u ' Unable to %s %s : %s / %s ' % ( action , cache_file , repr ( e ) , str ( e ) ) , logger . WARNING )
2014-03-10 01:18:05 -04:00
2014-08-29 06:29:56 -04:00
# remove entire show folder
if full :
try :
2014-11-05 00:36:16 -05:00
logger . log ( u ' Attempt to %s show folder %s ' % ( action , self . _location ) )
2014-08-29 06:29:56 -04:00
# check first the read-only attribute
file_attribute = ek . ek ( os . stat , self . location ) [ 0 ]
if ( not file_attribute & stat . S_IWRITE ) :
# File is read-only, so make it writeable
2014-11-05 00:36:16 -05:00
logger . log ( ' Attempting to make writeable the read only folder %s ' % self . _location , logger . DEBUG )
2014-08-29 06:29:56 -04:00
try :
ek . ek ( os . chmod , self . location , stat . S_IWRITE )
except :
2014-11-05 00:36:16 -05:00
logger . log ( u ' Unable to change permissions of %s ' % self . _location , logger . WARNING )
if sickbeard . TRASH_REMOVE_SHOW :
send2trash ( self . location )
else :
ek . ek ( shutil . rmtree , self . location )
logger . log ( u ' %s show folder %s ' %
( ( ' Deleted ' , ' Trashed ' ) [ sickbeard . TRASH_REMOVE_SHOW ] ,
self . _location ) )
2014-08-29 06:29:56 -04:00
2014-11-05 00:36:16 -05:00
except exceptions . ShowDirNotFoundException :
logger . log ( u " Show folder does not exist, no need to %s %s " % ( action , self . _location ) , logger . WARNING )
2014-08-29 06:29:56 -04:00
except OSError , e :
2014-11-05 00:36:16 -05:00
logger . log ( u ' Unable to %s %s : %s / %s ' % ( action , self . _location , repr ( e ) , str ( e ) ) , logger . WARNING )
2014-08-29 06:29:56 -04:00
2014-03-10 01:18:05 -04:00
def populateCache ( self ) :
cache_inst = image_cache . ImageCache ( )
2014-03-25 01:57:24 -04:00
2014-03-10 01:18:05 -04:00
logger . log ( u " Checking & filling cache for show " + self . name )
cache_inst . fill_cache ( self )
def refreshDir ( self ) :
# make sure the show dir is where we think it is unless dirs are created on the fly
if not ek . ek ( os . path . isdir , self . _location ) and not sickbeard . CREATE_MISSING_SHOW_DIRS :
return False
# load from dir
self . loadEpisodesFromDir ( )
# run through all locations from DB, check that they exist
logger . log ( str ( self . indexerid ) + u " : Loading all episodes with a location from the database " )
2014-06-21 18:46:59 -04:00
myDB = db . DBConnection ( )
sqlResults = myDB . select ( " SELECT * FROM tv_episodes WHERE showid = ? AND location != ' ' " , [ self . indexerid ] )
2014-03-10 01:18:05 -04:00
2014-05-30 06:01:49 -04:00
sql_l = [ ]
2014-03-10 01:18:05 -04:00
for ep in sqlResults :
curLoc = os . path . normpath ( ep [ " location " ] )
season = int ( ep [ " season " ] )
episode = int ( ep [ " episode " ] )
try :
curEp = self . getEpisode ( season , episode )
2014-12-05 21:03:20 -05:00
if not curEp :
raise exceptions . EpisodeDeletedException
2014-03-10 01:18:05 -04:00
except exceptions . EpisodeDeletedException :
2014-03-25 01:57:24 -04:00
logger . log ( u " The episode was deleted while we were refreshing it, moving on to the next one " ,
logger . DEBUG )
2014-03-10 01:18:05 -04:00
continue
# if the path doesn't exist or if it's not in our show dir
2014-03-25 01:57:24 -04:00
if not ek . ek ( os . path . isfile , curLoc ) or not os . path . normpath ( curLoc ) . startswith (
os . path . normpath ( self . location ) ) :
2014-03-10 01:18:05 -04:00
2014-05-17 18:14:31 -04:00
# check if downloaded files still exist, update our data if this has changed
2014-05-19 19:43:01 -04:00
if not sickbeard . SKIP_REMOVED_FILES :
2014-05-17 18:14:31 -04:00
with curEp . lock :
# if it used to have a file associated with it and it doesn't anymore then set it to IGNORED
if curEp . location and curEp . status in Quality . DOWNLOADED :
logger . log ( str ( self . indexerid ) + u " : Location for " + str ( season ) + " x " + str (
2014-05-27 03:44:23 -04:00
episode ) + " doesn ' t exist, removing it and changing our status to IGNORED " ,
logger . DEBUG )
2014-05-17 18:14:31 -04:00
curEp . status = IGNORED
curEp . subtitles = list ( )
curEp . subtitles_searchcount = 0
curEp . subtitles_lastsearch = str ( datetime . datetime . min )
curEp . location = ' '
curEp . hasnfo = False
curEp . hastbn = False
curEp . release_name = ' '
2014-05-30 06:01:49 -04:00
sql_l . append ( curEp . get_sql ( ) )
2014-05-14 08:33:36 -04:00
else :
# the file exists, set its modify file stamp
if sickbeard . AIRDATE_EPISODES :
2014-12-05 21:03:20 -05:00
with curEp . lock :
curEp . airdateModifyStamp ( )
2014-05-14 08:33:36 -04:00
2014-07-14 22:00:53 -04:00
if len ( sql_l ) > 0 :
2014-06-21 18:46:59 -04:00
myDB = db . DBConnection ( )
myDB . mass_action ( sql_l )
2014-06-30 11:57:32 -04:00
2014-03-10 01:18:05 -04:00
def downloadSubtitles ( self , force = False ) :
if not ek . ek ( os . path . isdir , self . _location ) :
logger . log ( str ( self . indexerid ) + " : Show dir doesn ' t exist, can ' t download subtitles " , logger . DEBUG )
return
2014-12-03 01:13:28 -05:00
2014-03-10 01:18:05 -04:00
logger . log ( str ( self . indexerid ) + " : Downloading subtitles " , logger . DEBUG )
2014-03-25 01:57:24 -04:00
2014-03-10 01:18:05 -04:00
try :
2014-12-03 01:13:28 -05:00
episodes = self . getAllEpisodes ( has_location = True )
if not len ( episodes ) > 0 :
logger . log ( str ( self . indexerid ) + " : No episodes to download subtitles for " + self . name , logger . DEBUG )
return
2014-03-10 01:18:05 -04:00
2014-12-03 01:13:28 -05:00
for episode in episodes :
episode . downloadSubtitles ( force = force )
except Exception :
logger . log ( " Error occurred when downloading subtitles: " + traceback . format_exc ( ) , logger . DEBUG )
2014-03-10 01:18:05 -04:00
2014-06-23 23:25:20 -04:00
def saveToDB ( self , forceSave = False ) :
if not self . dirty and not forceSave :
2014-12-03 01:13:28 -05:00
logger . log ( str ( self . indexerid ) + " : Not saving show to db - record is not dirty " , logger . DEBUG )
2014-06-23 23:25:20 -04:00
return
2014-03-10 01:18:05 -04:00
logger . log ( str ( self . indexerid ) + u " : Saving show info to database " , logger . DEBUG )
controlValueDict = { " indexer_id " : self . indexerid }
newValueDict = { " indexer " : self . indexer ,
" show_name " : self . name ,
" location " : self . _location ,
" network " : self . network ,
" genre " : self . genre ,
" classification " : self . classification ,
" runtime " : self . runtime ,
" quality " : self . quality ,
" airs " : self . airs ,
" status " : self . status ,
" flatten_folders " : self . flatten_folders ,
" paused " : self . paused ,
" air_by_date " : self . air_by_date ,
2014-05-30 01:48:02 -04:00
" anime " : self . anime ,
" scene " : self . scene ,
2014-04-28 05:15:29 -04:00
" sports " : self . sports ,
2014-03-10 01:18:05 -04:00
" subtitles " : self . subtitles ,
" dvdorder " : self . dvdorder ,
2014-03-18 09:50:13 -04:00
" archive_firstmatch " : self . archive_firstmatch ,
2014-03-10 01:18:05 -04:00
" startyear " : self . startyear ,
" lang " : self . lang ,
" imdb_id " : self . imdbid ,
2014-04-24 01:18:16 -04:00
" last_update_indexer " : self . last_update_indexer ,
" rls_ignore_words " : self . rls_ignore_words ,
2014-11-23 04:17:37 -05:00
" rls_require_words " : self . rls_require_words ,
" default_ep_status " : self . default_ep_status
2014-03-25 01:57:24 -04:00
}
2014-06-07 17:32:38 -04:00
2014-06-21 18:46:59 -04:00
myDB = db . DBConnection ( )
myDB . upsert ( " tv_shows " , newValueDict , controlValueDict )
2014-06-07 17:32:38 -04:00
2014-05-26 02:29:22 -04:00
helpers . update_anime_support ( )
2014-03-10 01:18:05 -04:00
if self . imdbid :
controlValueDict = { " indexer_id " : self . indexerid }
newValueDict = self . imdb_info
2014-03-25 01:57:24 -04:00
2014-06-21 18:46:59 -04:00
myDB = db . DBConnection ( )
myDB . upsert ( " imdb_info " , newValueDict , controlValueDict )
2014-03-10 01:18:05 -04:00
def __str__ ( self ) :
toReturn = " "
toReturn + = " indexerid: " + str ( self . indexerid ) + " \n "
toReturn + = " indexer: " + str ( self . indexer ) + " \n "
toReturn + = " name: " + self . name + " \n "
toReturn + = " location: " + self . _location + " \n "
if self . network :
toReturn + = " network: " + self . network + " \n "
if self . airs :
toReturn + = " airs: " + self . airs + " \n "
if self . status :
toReturn + = " status: " + self . status + " \n "
toReturn + = " startyear: " + str ( self . startyear ) + " \n "
2014-05-18 11:57:07 -04:00
if self . genre :
toReturn + = " genre: " + self . genre + " \n "
2014-03-10 01:18:05 -04:00
toReturn + = " classification: " + self . classification + " \n "
toReturn + = " runtime: " + str ( self . runtime ) + " \n "
toReturn + = " quality: " + str ( self . quality ) + " \n "
2014-05-30 01:48:02 -04:00
toReturn + = " scene: " + str ( self . is_scene ) + " \n "
toReturn + = " sports: " + str ( self . is_sports ) + " \n "
2014-05-26 02:29:22 -04:00
toReturn + = " anime: " + str ( self . is_anime ) + " \n "
2014-03-10 01:18:05 -04:00
return toReturn
def wantEpisode ( self , season , episode , quality , manualSearch = False ) :
2014-03-25 01:57:24 -04:00
logger . log ( u " Checking if found episode " + str ( season ) + " x " + str ( episode ) + " is wanted at quality " +
Quality . qualityStrings [ quality ] , logger . DEBUG )
2014-03-10 01:18:05 -04:00
# if the quality isn't one we want under any circumstances then just say no
anyQualities , bestQualities = Quality . splitQuality ( self . quality )
2014-03-25 01:57:24 -04:00
logger . log ( u " any,best = " + str ( anyQualities ) + " " + str ( bestQualities ) + " and found " + str ( quality ) ,
logger . DEBUG )
2014-03-10 01:18:05 -04:00
if quality not in anyQualities + bestQualities :
2014-03-20 04:15:22 -04:00
logger . log ( u " Don ' t want this quality, ignoring found episode " , logger . DEBUG )
2014-03-10 01:18:05 -04:00
return False
2014-06-21 18:46:59 -04:00
myDB = db . DBConnection ( )
sqlResults = myDB . select ( " SELECT status FROM tv_episodes WHERE showid = ? AND season = ? AND episode = ? " ,
[ self . indexerid , season , episode ] )
2014-03-10 01:18:05 -04:00
if not sqlResults or not len ( sqlResults ) :
2014-03-20 04:15:22 -04:00
logger . log ( u " Unable to find a matching episode in database, ignoring found episode " , logger . DEBUG )
2014-03-10 01:18:05 -04:00
return False
epStatus = int ( sqlResults [ 0 ] [ " status " ] )
2014-03-20 04:15:22 -04:00
epStatus_text = statusStrings [ epStatus ]
2014-03-10 01:18:05 -04:00
2014-03-20 04:15:22 -04:00
logger . log ( u " Existing episode status: " + str ( epStatus ) + " ( " + epStatus_text + " ) " , logger . DEBUG )
2014-03-10 01:18:05 -04:00
# if we know we don't want it then just say no
if epStatus in ( SKIPPED , IGNORED , ARCHIVED ) and not manualSearch :
2014-03-20 04:15:22 -04:00
logger . log ( u " Existing episode status is skipped/ignored/archived, ignoring found episode " , logger . DEBUG )
2014-03-10 01:18:05 -04:00
return False
# if it's one of these then we want it as long as it's in our allowed initial qualities
if quality in anyQualities + bestQualities :
if epStatus in ( WANTED , UNAIRED , SKIPPED ) :
2014-03-20 04:15:22 -04:00
logger . log ( u " Existing episode status is wanted/unaired/skipped, getting found episode " , logger . DEBUG )
2014-03-10 01:18:05 -04:00
return True
elif manualSearch :
2014-03-25 01:57:24 -04:00
logger . log (
u " Usually ignoring found episode, but forced search allows the quality, getting found episode " ,
logger . DEBUG )
2014-03-10 01:18:05 -04:00
return True
else :
2014-03-25 01:57:24 -04:00
logger . log ( u " Quality is on wanted list, need to check if it ' s better than existing quality " ,
logger . DEBUG )
2014-03-10 01:18:05 -04:00
curStatus , curQuality = Quality . splitCompositeStatus ( epStatus )
# if we are re-downloading then we only want it if it's in our bestQualities list and better than what we have
2014-03-20 04:15:22 -04:00
if curStatus in Quality . DOWNLOADED + Quality . SNATCHED + Quality . SNATCHED_PROPER + Quality . SNATCHED_BEST and quality in bestQualities and quality > curQuality :
2014-03-25 01:57:24 -04:00
logger . log ( u " Episode already exists but the found episode has better quality, getting found episode " ,
logger . DEBUG )
2014-03-10 01:18:05 -04:00
return True
2014-03-20 04:15:22 -04:00
else :
2014-03-25 01:57:24 -04:00
logger . log ( u " Episode already exists and the found episode has same/lower quality, ignoring found episode " ,
logger . DEBUG )
2014-03-10 01:18:05 -04:00
2014-03-20 04:15:22 -04:00
logger . log ( u " None of the conditions were met, ignoring found episode " , logger . DEBUG )
2014-03-10 01:18:05 -04:00
return False
def getOverview ( self , epStatus ) :
if epStatus == WANTED :
return Overview . WANTED
elif epStatus in ( UNAIRED , UNKNOWN ) :
return Overview . UNAIRED
elif epStatus in ( SKIPPED , IGNORED ) :
return Overview . SKIPPED
elif epStatus == ARCHIVED :
return Overview . GOOD
2014-03-19 19:33:49 -04:00
elif epStatus in Quality . DOWNLOADED + Quality . SNATCHED + Quality . SNATCHED_PROPER + Quality . FAILED + Quality . SNATCHED_BEST :
2014-03-10 01:18:05 -04:00
anyQualities , bestQualities = Quality . splitQuality ( self . quality ) # @UnusedVariable
if bestQualities :
maxBestQuality = max ( bestQualities )
else :
maxBestQuality = None
epStatus , curQuality = Quality . splitCompositeStatus ( epStatus )
if epStatus == FAILED :
return Overview . WANTED
2014-03-19 19:33:49 -04:00
elif epStatus in ( SNATCHED , SNATCHED_PROPER , SNATCHED_BEST ) :
2014-03-10 01:18:05 -04:00
return Overview . SNATCHED
# if they don't want re-downloads then we call it good if they have anything
2014-03-20 14:03:22 -04:00
elif maxBestQuality == None :
2014-03-10 01:18:05 -04:00
return Overview . GOOD
# if they have one but it's not the best they want then mark it as qual
elif curQuality < maxBestQuality :
return Overview . QUAL
# if it's >= maxBestQuality then it's good
else :
return Overview . GOOD
2014-07-14 22:00:53 -04:00
def __getstate__ ( self ) :
d = dict ( self . __dict__ )
del d [ ' lock ' ]
return d
def __setstate__ ( self , d ) :
d [ ' lock ' ] = threading . Lock ( )
self . __dict__ . update ( d )
2014-08-18 08:40:29 -04:00
2014-03-10 01:18:05 -04:00
class TVEpisode ( object ) :
2014-05-04 23:04:46 -04:00
def __init__ ( self , show , season , episode , file = " " ) :
2014-03-10 01:18:05 -04:00
self . _name = " "
2014-05-03 08:07:44 -04:00
self . _season = season
self . _episode = episode
2014-05-26 02:29:22 -04:00
self . _absolute_number = 0
2014-03-10 01:18:05 -04:00
self . _description = " "
self . _subtitles = list ( )
self . _subtitles_searchcount = 0
self . _subtitles_lastsearch = str ( datetime . datetime . min )
self . _airdate = datetime . date . fromordinal ( 1 )
self . _hasnfo = False
self . _hastbn = False
self . _status = UNKNOWN
self . _indexerid = 0
self . _file_size = 0
self . _release_name = ' '
self . _is_proper = False
2014-07-22 00:53:32 -04:00
self . _version = 0
self . _release_group = ' '
2014-03-10 01:18:05 -04:00
# setting any of the above sets the dirty flag
self . dirty = True
self . show = show
2014-05-30 06:01:49 -04:00
self . scene_season = 0
self . scene_episode = 0
self . scene_absolute_number = 0
2014-03-10 01:18:05 -04:00
self . _location = file
2014-05-05 01:50:28 -04:00
self . _indexer = int ( self . show . indexer )
2014-03-10 01:18:05 -04:00
self . lock = threading . Lock ( )
2014-05-04 23:04:46 -04:00
self . specifyEpisode ( self . season , self . episode )
2014-03-10 01:18:05 -04:00
self . relatedEps = [ ]
self . checkForMetaFiles ( )
2014-09-20 08:03:48 -04:00
self . wantedQuality = [ ]
2014-03-10 01:18:05 -04:00
name = property ( lambda self : self . _name , dirty_setter ( " _name " ) )
season = property ( lambda self : self . _season , dirty_setter ( " _season " ) )
episode = property ( lambda self : self . _episode , dirty_setter ( " _episode " ) )
2014-05-26 02:29:22 -04:00
absolute_number = property ( lambda self : self . _absolute_number , dirty_setter ( " _absolute_number " ) )
2014-03-10 01:18:05 -04:00
description = property ( lambda self : self . _description , dirty_setter ( " _description " ) )
subtitles = property ( lambda self : self . _subtitles , dirty_setter ( " _subtitles " ) )
subtitles_searchcount = property ( lambda self : self . _subtitles_searchcount , dirty_setter ( " _subtitles_searchcount " ) )
subtitles_lastsearch = property ( lambda self : self . _subtitles_lastsearch , dirty_setter ( " _subtitles_lastsearch " ) )
airdate = property ( lambda self : self . _airdate , dirty_setter ( " _airdate " ) )
hasnfo = property ( lambda self : self . _hasnfo , dirty_setter ( " _hasnfo " ) )
hastbn = property ( lambda self : self . _hastbn , dirty_setter ( " _hastbn " ) )
status = property ( lambda self : self . _status , dirty_setter ( " _status " ) )
indexer = property ( lambda self : self . _indexer , dirty_setter ( " _indexer " ) )
indexerid = property ( lambda self : self . _indexerid , dirty_setter ( " _indexerid " ) )
2014-05-27 03:44:23 -04:00
# location = property(lambda self: self._location, dirty_setter("_location"))
2014-03-10 01:18:05 -04:00
file_size = property ( lambda self : self . _file_size , dirty_setter ( " _file_size " ) )
release_name = property ( lambda self : self . _release_name , dirty_setter ( " _release_name " ) )
is_proper = property ( lambda self : self . _is_proper , dirty_setter ( " _is_proper " ) )
2014-07-22 00:53:32 -04:00
version = property ( lambda self : self . _version , dirty_setter ( " _version " ) )
release_group = property ( lambda self : self . _release_group , dirty_setter ( " _release_group " ) )
2014-03-10 01:18:05 -04:00
def _set_location ( self , new_location ) :
logger . log ( u " Setter sets location to " + new_location , logger . DEBUG )
2014-05-27 03:44:23 -04:00
# self._location = newLocation
2014-03-10 01:18:05 -04:00
dirty_setter ( " _location " ) ( self , new_location )
if new_location and ek . ek ( os . path . isfile , new_location ) :
self . file_size = ek . ek ( os . path . getsize , new_location )
else :
self . file_size = 0
location = property ( lambda self : self . _location , _set_location )
def refreshSubtitles ( self ) :
""" Look for subtitles files and refresh the subtitles property """
self . subtitles = subtitles . subtitlesLanguages ( self . location )
2014-03-25 01:57:24 -04:00
def downloadSubtitles ( self , force = False ) :
2014-05-27 03:44:23 -04:00
# TODO: Add support for force option
2014-03-10 01:18:05 -04:00
if not ek . ek ( os . path . isfile , self . location ) :
2014-03-25 01:57:24 -04:00
logger . log (
str ( self . show . indexerid ) + " : Episode file doesn ' t exist, can ' t download subtitles for episode " + str (
self . season ) + " x " + str ( self . episode ) , logger . DEBUG )
2014-03-10 01:18:05 -04:00
return
2014-03-25 01:57:24 -04:00
logger . log ( str ( self . show . indexerid ) + " : Downloading subtitles for episode " + str ( self . season ) + " x " + str (
self . episode ) , logger . DEBUG )
2014-03-10 01:18:05 -04:00
previous_subtitles = self . subtitles
try :
2014-05-14 21:41:08 -04:00
need_languages = set ( sickbeard . SUBTITLES_LANGUAGES ) - set ( self . subtitles )
subtitles = subliminal . download_subtitles ( [ self . location ] , languages = need_languages ,
services = sickbeard . subtitles . getEnabledServiceList ( ) , force = force ,
2014-12-03 01:13:28 -05:00
multi = sickbeard . SUBTITLES_MULTI , cache_dir = sickbeard . CACHE_DIR )
2014-03-10 01:18:05 -04:00
if sickbeard . SUBTITLES_DIR :
2014-05-14 21:41:08 -04:00
for video in subtitles :
2014-03-10 01:18:05 -04:00
subs_new_path = ek . ek ( os . path . join , os . path . dirname ( video . path ) , sickbeard . SUBTITLES_DIR )
dir_exists = helpers . makeDir ( subs_new_path )
if not dir_exists :
2014-03-25 01:57:24 -04:00
logger . log ( u " Unable to create subtitles folder " + subs_new_path , logger . ERROR )
2014-03-10 01:18:05 -04:00
else :
helpers . chmodAsParent ( subs_new_path )
2014-05-14 21:41:08 -04:00
for subtitle in subtitles . get ( video ) :
2014-03-10 01:18:05 -04:00
new_file_path = ek . ek ( os . path . join , subs_new_path , os . path . basename ( subtitle . path ) )
helpers . moveFile ( subtitle . path , new_file_path )
helpers . chmodAsParent ( new_file_path )
else :
2014-05-14 21:41:08 -04:00
for video in subtitles :
for subtitle in subtitles . get ( video ) :
2014-03-10 01:18:05 -04:00
helpers . chmodAsParent ( subtitle . path )
except Exception as e :
logger . log ( " Error occurred when downloading subtitles: " + traceback . format_exc ( ) , logger . ERROR )
return
self . refreshSubtitles ( )
2014-05-27 03:44:23 -04:00
self . subtitles_searchcount = self . subtitles_searchcount + 1 if self . subtitles_searchcount else 1 # added the if because sometime it raise an error
2014-03-10 01:18:05 -04:00
self . subtitles_lastsearch = datetime . datetime . now ( ) . strftime ( " % Y- % m- %d % H: % M: % S " )
self . saveToDB ( )
newsubtitles = set ( self . subtitles ) . difference ( set ( previous_subtitles ) )
if newsubtitles :
2014-05-14 21:41:08 -04:00
subtitleList = " , " . join ( subliminal . language . Language ( x ) . name for x in newsubtitles )
2014-03-25 01:57:24 -04:00
logger . log ( str ( self . show . indexerid ) + u " : Downloaded " + subtitleList + " subtitles for episode " + str (
self . season ) + " x " + str ( self . episode ) , logger . DEBUG )
2014-03-10 01:18:05 -04:00
notifiers . notify_subtitle_download ( self . prettyName ( ) , subtitleList )
else :
2014-03-25 01:57:24 -04:00
logger . log (
str ( self . show . indexerid ) + u " : No subtitles downloaded for episode " + str ( self . season ) + " x " + str (
self . episode ) , logger . DEBUG )
2014-03-10 01:18:05 -04:00
if sickbeard . SUBTITLES_HISTORY :
2014-05-14 21:41:08 -04:00
for video in subtitles :
for subtitle in subtitles . get ( video ) :
2014-03-10 01:18:05 -04:00
history . logSubtitle ( self . show . indexerid , self . season , self . episode , self . status , subtitle )
return subtitles
def checkForMetaFiles ( self ) :
oldhasnfo = self . hasnfo
oldhastbn = self . hastbn
cur_nfo = False
cur_tbn = False
# check for nfo and tbn
2014-05-05 01:50:28 -04:00
if ek . ek ( os . path . isfile , self . location ) :
2014-03-10 01:18:05 -04:00
for cur_provider in sickbeard . metadata_provider_dict . values ( ) :
if cur_provider . episode_metadata :
new_result = cur_provider . _has_episode_metadata ( self )
else :
new_result = False
cur_nfo = new_result or cur_nfo
if cur_provider . episode_thumbnails :
new_result = cur_provider . _has_episode_thumb ( self )
else :
new_result = False
cur_tbn = new_result or cur_tbn
self . hasnfo = cur_nfo
self . hastbn = cur_tbn
# if either setting has changed return true, if not return false
return oldhasnfo != self . hasnfo or oldhastbn != self . hastbn
2014-05-04 23:04:46 -04:00
def specifyEpisode ( self , season , episode ) :
2014-03-10 01:18:05 -04:00
2014-05-03 20:22:33 -04:00
sqlResult = self . loadFromDB ( season , episode )
2014-03-10 01:18:05 -04:00
2014-05-05 01:50:28 -04:00
if not sqlResult :
2014-03-10 01:18:05 -04:00
# only load from NFO if we didn't load from DB
if ek . ek ( os . path . isfile , self . location ) :
try :
self . loadFromNFO ( self . location )
except exceptions . NoNFOException :
2014-03-25 01:57:24 -04:00
logger . log ( str ( self . show . indexerid ) + u " : There was an error loading the NFO for episode " + str (
season ) + " x " + str ( episode ) , logger . ERROR )
2014-03-10 01:18:05 -04:00
pass
2014-03-20 09:45:43 -04:00
# if we tried loading it from NFO and didn't find the NFO, try the Indexers
2014-05-02 07:33:06 -04:00
if not self . hasnfo :
2014-03-10 01:18:05 -04:00
try :
2014-05-04 23:04:46 -04:00
result = self . loadFromIndexer ( season , episode )
2014-03-10 01:18:05 -04:00
except exceptions . EpisodeDeletedException :
result = False
2014-03-20 09:45:43 -04:00
# if we failed SQL *and* NFO, Indexers then fail
2014-05-02 07:33:06 -04:00
if not result :
2014-03-25 01:57:24 -04:00
raise exceptions . EpisodeNotFoundException (
" Couldn ' t find episode " + str ( season ) + " x " + str ( episode ) )
2014-03-10 01:18:05 -04:00
2014-05-03 20:22:33 -04:00
def loadFromDB ( self , season , episode ) :
2014-03-25 01:57:24 -04:00
logger . log (
str ( self . show . indexerid ) + u " : Loading episode details from DB for episode " + str ( season ) + " x " + str (
episode ) , logger . DEBUG )
2014-03-10 01:18:05 -04:00
2014-06-21 18:46:59 -04:00
myDB = db . DBConnection ( )
sqlResults = myDB . select ( " SELECT * FROM tv_episodes WHERE showid = ? AND season = ? AND episode = ? " ,
[ self . show . indexerid , season , episode ] )
2014-03-10 01:18:05 -04:00
if len ( sqlResults ) > 1 :
raise exceptions . MultipleDBEpisodesException ( " Your DB has two records for the same show somehow. " )
elif len ( sqlResults ) == 0 :
2014-03-25 01:57:24 -04:00
logger . log ( str ( self . show . indexerid ) + u " : Episode " + str ( self . season ) + " x " + str (
self . episode ) + " not found in the database " , logger . DEBUG )
2014-03-10 01:18:05 -04:00
return False
else :
2014-05-27 03:44:23 -04:00
# NAMEIT logger.log(u"AAAAA from" + str(self.season)+"x"+str(self.episode) + " -" + self.name + " to " + str(sqlResults[0]["name"]))
2014-03-10 01:18:05 -04:00
if sqlResults [ 0 ] [ " name " ] :
self . name = sqlResults [ 0 ] [ " name " ]
2014-05-03 05:23:26 -04:00
2014-03-10 01:18:05 -04:00
self . season = season
self . episode = episode
2014-05-26 02:29:22 -04:00
self . absolute_number = sqlResults [ 0 ] [ " absolute_number " ]
2014-03-10 01:18:05 -04:00
self . description = sqlResults [ 0 ] [ " description " ]
if not self . description :
self . description = " "
if sqlResults [ 0 ] [ " subtitles " ] and sqlResults [ 0 ] [ " subtitles " ] :
self . subtitles = sqlResults [ 0 ] [ " subtitles " ] . split ( " , " )
self . subtitles_searchcount = sqlResults [ 0 ] [ " subtitles_searchcount " ]
self . subtitles_lastsearch = sqlResults [ 0 ] [ " subtitles_lastsearch " ]
self . airdate = datetime . date . fromordinal ( int ( sqlResults [ 0 ] [ " airdate " ] ) )
2014-05-27 03:44:23 -04:00
# logger.log(u"1 Status changes from " + str(self.status) + " to " + str(sqlResults[0]["status"]), logger.DEBUG)
2014-03-10 01:18:05 -04:00
self . status = int ( sqlResults [ 0 ] [ " status " ] )
# don't overwrite my location
if sqlResults [ 0 ] [ " location " ] and sqlResults [ 0 ] [ " location " ] :
self . location = os . path . normpath ( sqlResults [ 0 ] [ " location " ] )
if sqlResults [ 0 ] [ " file_size " ] :
self . file_size = int ( sqlResults [ 0 ] [ " file_size " ] )
else :
self . file_size = 0
self . indexerid = int ( sqlResults [ 0 ] [ " indexerid " ] )
2014-03-25 01:57:24 -04:00
self . indexer = int ( sqlResults [ 0 ] [ " indexer " ] )
2014-03-10 01:18:05 -04:00
2014-08-18 08:40:29 -04:00
sickbeard . scene_numbering . xem_refresh ( self . show . indexerid , self . show . indexer )
2014-05-27 03:44:23 -04:00
2014-06-07 14:36:26 -04:00
try :
2014-08-18 08:40:29 -04:00
self . scene_season = int ( sqlResults [ 0 ] [ " scene_season " ] )
2014-08-18 08:48:23 -04:00
except :
self . scene_season = 0
try :
2014-06-07 14:36:26 -04:00
self . scene_episode = int ( sqlResults [ 0 ] [ " scene_episode " ] )
except :
2014-08-18 08:48:23 -04:00
self . scene_episode = 0
2014-05-27 03:44:23 -04:00
2014-06-07 14:36:26 -04:00
try :
self . scene_absolute_number = int ( sqlResults [ 0 ] [ " scene_absolute_number " ] )
except :
2014-08-18 08:48:23 -04:00
self . scene_absolute_number = 0
if self . scene_absolute_number == 0 :
2014-08-18 08:40:29 -04:00
self . scene_absolute_number = sickbeard . scene_numbering . get_scene_absolute_numbering (
self . show . indexerid ,
self . show . indexer ,
self . absolute_number
)
2014-05-27 03:44:23 -04:00
2014-08-18 08:48:23 -04:00
if self . scene_season == 0 or self . scene_episode == 0 :
self . scene_season , self . scene_episode = sickbeard . scene_numbering . get_scene_numbering (
self . show . indexerid ,
self . show . indexer ,
self . season , self . episode
)
2014-03-10 01:18:05 -04:00
if sqlResults [ 0 ] [ " release_name " ] is not None :
self . release_name = sqlResults [ 0 ] [ " release_name " ]
if sqlResults [ 0 ] [ " is_proper " ] :
self . is_proper = int ( sqlResults [ 0 ] [ " is_proper " ] )
2014-07-22 00:53:32 -04:00
if sqlResults [ 0 ] [ " version " ] :
self . version = int ( sqlResults [ 0 ] [ " version " ] )
if sqlResults [ 0 ] [ " release_group " ] is not None :
self . release_group = sqlResults [ 0 ] [ " release_group " ]
2014-03-10 01:18:05 -04:00
self . dirty = False
return True
2014-05-04 23:04:46 -04:00
def loadFromIndexer ( self , season = None , episode = None , cache = True , tvapi = None , cachedSeason = None ) :
2014-03-10 01:18:05 -04:00
if season is None :
season = self . season
if episode is None :
episode = self . episode
2014-03-25 01:57:24 -04:00
logger . log ( str ( self . show . indexerid ) + u " : Loading episode details from " + sickbeard . indexerApi (
self . show . indexer ) . name + " for episode " + str ( season ) + " x " + str ( episode ) , logger . DEBUG )
2014-03-10 01:18:05 -04:00
indexer_lang = self . show . lang
try :
if cachedSeason is None :
if tvapi is None :
2014-03-26 15:28:46 -04:00
lINDEXER_API_PARMS = sickbeard . indexerApi ( self . indexer ) . api_params . copy ( )
2014-03-10 01:18:05 -04:00
if not cache :
2014-03-12 01:28:30 -04:00
lINDEXER_API_PARMS [ ' cache ' ] = False
2014-03-10 01:18:05 -04:00
if indexer_lang :
2014-03-12 01:28:30 -04:00
lINDEXER_API_PARMS [ ' language ' ] = indexer_lang
2014-03-10 01:18:05 -04:00
if self . show . dvdorder != 0 :
2014-03-12 01:28:30 -04:00
lINDEXER_API_PARMS [ ' dvdorder ' ] = True
2014-03-10 01:18:05 -04:00
2014-03-26 15:28:46 -04:00
t = sickbeard . indexerApi ( self . indexer ) . indexer ( * * lINDEXER_API_PARMS )
2014-03-10 01:18:05 -04:00
else :
t = tvapi
myEp = t [ self . show . indexerid ] [ season ] [ episode ]
else :
myEp = cachedSeason [ episode ]
2014-03-25 01:57:24 -04:00
except ( sickbeard . indexer_error , IOError ) , e :
logger . log ( u " " + sickbeard . indexerApi ( self . indexer ) . name + " threw up an error: " + ex ( e ) , logger . DEBUG )
2014-03-10 01:18:05 -04:00
# if the episode is already valid just log it, if not throw it up
if self . name :
2014-03-25 01:57:24 -04:00
logger . log ( u " " + sickbeard . indexerApi (
self . indexer ) . name + " timed out but we have enough info from other sources, allowing the error " ,
logger . DEBUG )
2014-03-10 01:18:05 -04:00
return
else :
2014-03-25 01:57:24 -04:00
logger . log ( u " " + sickbeard . indexerApi ( self . indexer ) . name + " timed out, unable to create the episode " ,
logger . ERROR )
2014-03-10 01:18:05 -04:00
return False
2014-03-25 01:57:24 -04:00
except ( sickbeard . indexer_episodenotfound , sickbeard . indexer_seasonnotfound ) :
logger . log ( u " Unable to find the episode on " + sickbeard . indexerApi (
self . indexer ) . name + " ... has it been removed? Should I delete from db? " , logger . DEBUG )
2014-03-20 09:45:43 -04:00
# if I'm no longer on the Indexers but I once was then delete myself from the DB
2014-03-10 01:18:05 -04:00
if self . indexerid != - 1 :
self . deleteEpisode ( )
return
if getattr ( myEp , ' episodename ' , None ) is None :
2014-03-25 01:57:24 -04:00
logger . log ( u " This episode ( " + self . show . name + " - " + str ( season ) + " x " + str (
episode ) + " ) has no name on " + sickbeard . indexerApi ( self . indexer ) . name + " " )
2014-03-20 14:03:22 -04:00
# if I'm incomplete on TVDB but I once was complete then just delete myself from the DB for now
2014-03-10 01:18:05 -04:00
if self . indexerid != - 1 :
self . deleteEpisode ( )
return False
2014-05-29 01:40:12 -04:00
if getattr ( myEp , ' absolute_number ' , None ) is None :
2014-05-27 03:44:23 -04:00
logger . log ( u " This episode ( " + self . show . name + " - " + str ( season ) + " x " + str (
episode ) + " ) has no absolute number on " + sickbeard . indexerApi (
2014-12-02 14:09:53 -05:00
self . indexer ) . name , logger . DEBUG )
2014-05-26 02:29:22 -04:00
else :
2014-05-27 03:44:23 -04:00
logger . log (
str ( self . show . indexerid ) + " : The absolute_number for " + str ( season ) + " x " + str ( episode ) + " is : " +
2014-05-29 01:40:12 -04:00
str ( myEp [ " absolute_number " ] ) , logger . DEBUG )
2014-05-26 02:29:22 -04:00
self . absolute_number = int ( myEp [ " absolute_number " ] )
2014-03-10 01:18:05 -04:00
self . name = getattr ( myEp , ' episodename ' , " " )
self . season = season
self . episode = episode
2014-08-18 08:40:29 -04:00
sickbeard . scene_numbering . xem_refresh ( self . show . indexerid , self . show . indexer )
self . scene_absolute_number = sickbeard . scene_numbering . get_scene_absolute_numbering (
self . show . indexerid ,
self . show . indexer ,
self . absolute_number
)
self . scene_season , self . scene_episode = sickbeard . scene_numbering . get_scene_numbering (
self . show . indexerid ,
self . show . indexer ,
self . season , self . episode
)
2014-03-10 01:18:05 -04:00
self . description = getattr ( myEp , ' overview ' , " " )
2014-03-25 01:57:24 -04:00
firstaired = getattr ( myEp , ' firstaired ' , None )
2014-03-27 05:42:00 -04:00
if firstaired is None or firstaired in " 0000-00-00 " :
2014-03-21 07:09:18 -04:00
firstaired = str ( datetime . date . fromordinal ( 1 ) )
rawAirdate = [ int ( x ) for x in firstaired . split ( " - " ) ]
2014-03-10 01:18:05 -04:00
2014-03-21 07:09:18 -04:00
try :
2014-03-10 01:18:05 -04:00
self . airdate = datetime . date ( rawAirdate [ 0 ] , rawAirdate [ 1 ] , rawAirdate [ 2 ] )
2014-03-27 05:42:00 -04:00
except ( ValueError , IndexError ) :
2014-03-25 01:57:24 -04:00
logger . log ( u " Malformed air date retrieved from " + sickbeard . indexerApi (
self . indexer ) . name + " ( " + self . show . name + " - " + str ( season ) + " x " + str ( episode ) + " ) " ,
logger . ERROR )
2014-03-20 14:03:22 -04:00
# if I'm incomplete on TVDB but I once was complete then just delete myself from the DB for now
2014-03-10 01:18:05 -04:00
if self . indexerid != - 1 :
self . deleteEpisode ( )
return False
2014-05-27 03:44:23 -04:00
# early conversion to int so that episode doesn't get marked dirty
2014-03-25 01:57:24 -04:00
self . indexerid = getattr ( myEp , ' id ' , None )
2014-03-16 09:33:00 -04:00
if self . indexerid is None :
2014-03-25 01:57:24 -04:00
logger . log ( u " Failed to retrieve ID from " + sickbeard . indexerApi ( self . indexer ) . name , logger . ERROR )
2014-03-10 01:18:05 -04:00
if self . indexerid != - 1 :
self . deleteEpisode ( )
return False
2014-06-10 08:40:11 -04:00
# don't update show status if show dir is missing, unless it's missing on purpose
2014-07-14 22:00:53 -04:00
if not ek . ek ( os . path . isdir ,
self . show . _location ) and not sickbeard . CREATE_MISSING_SHOW_DIRS and not sickbeard . ADD_SHOWS_WO_DIR :
2014-03-25 01:57:24 -04:00
logger . log (
u " The show dir is missing, not bothering to change the episode statuses since it ' d probably be invalid " )
2014-03-10 01:18:05 -04:00
return
2014-05-29 01:40:12 -04:00
if self . location :
logger . log ( str ( self . show . indexerid ) + u " : Setting status for " + str ( season ) + " x " + str (
episode ) + " based on status " + str ( self . status ) + " and existence of " + self . location , logger . DEBUG )
2014-03-10 01:18:05 -04:00
2014-06-10 08:47:57 -04:00
# if we don't have the file
2014-03-10 01:18:05 -04:00
if not ek . ek ( os . path . isfile , self . location ) :
2014-06-10 08:47:57 -04:00
# if it hasn't aired yet set the status to UNAIRED
if self . airdate > = datetime . date . today ( ) and self . status in [ SKIPPED , UNAIRED , UNKNOWN , WANTED ] :
logger . log ( u " Episode airs in the future, marking it " + str ( UNAIRED ) , logger . DEBUG )
2014-03-10 01:18:05 -04:00
self . status = UNAIRED
2014-06-10 08:47:57 -04:00
2014-03-10 01:18:05 -04:00
# if there's no airdate then set it to skipped (and respect ignored)
elif self . airdate == datetime . date . fromordinal ( 1 ) :
if self . status == IGNORED :
logger . log ( u " Episode has no air date, but it ' s already marked as ignored " , logger . DEBUG )
else :
logger . log ( u " Episode has no air date, automatically marking it skipped " , logger . DEBUG )
self . status = SKIPPED
2014-06-10 08:47:57 -04:00
2014-03-10 01:18:05 -04:00
# if we don't have the file and the airdate is in the past
else :
if self . status == UNAIRED :
2014-12-05 21:03:20 -05:00
if self . season > 0 :
self . status = WANTED
else :
self . status = SKIPPED
2014-03-10 01:18:05 -04:00
2014-12-05 21:03:20 -05:00
# if we somehow are still UNKNOWN then just use the shows defined default status or SKIPPED
2014-03-10 01:18:05 -04:00
elif self . status == UNKNOWN :
2014-11-23 04:17:37 -05:00
self . status = self . show . default_ep_status
2014-03-10 01:18:05 -04:00
else :
2014-03-25 01:57:24 -04:00
logger . log (
u " Not touching status because we have no ep file, the airdate is in the past, and the status is " + str (
self . status ) , logger . DEBUG )
2014-03-10 01:18:05 -04:00
# if we have a media file then it's downloaded
elif sickbeard . helpers . isMediaFile ( self . location ) :
# leave propers alone, you have to either post-process them or manually change them back
if self . status not in Quality . SNATCHED_PROPER + Quality . DOWNLOADED + Quality . SNATCHED + [ ARCHIVED ] :
2014-03-25 01:57:24 -04:00
logger . log (
u " 5 Status changes from " + str ( self . status ) + " to " + str ( Quality . statusFromName ( self . location ) ) ,
logger . DEBUG )
2014-07-27 06:59:21 -04:00
self . status = Quality . statusFromName ( self . location , anime = self . show . is_anime )
2014-03-10 01:18:05 -04:00
# shouldn't get here probably
else :
logger . log ( u " 6 Status changes from " + str ( self . status ) + " to " + str ( UNKNOWN ) , logger . DEBUG )
self . status = UNKNOWN
def loadFromNFO ( self , location ) :
if not ek . ek ( os . path . isdir , self . show . _location ) :
2014-03-25 01:57:24 -04:00
logger . log (
str ( self . show . indexerid ) + u " : The show dir is missing, not bothering to try loading the episode NFO " )
2014-03-10 01:18:05 -04:00
return
2014-03-25 01:57:24 -04:00
logger . log (
str ( self . show . indexerid ) + u " : Loading episode details from the NFO file associated with " + location ,
logger . DEBUG )
2014-03-10 01:18:05 -04:00
self . location = location
if self . location != " " :
if self . status == UNKNOWN :
if sickbeard . helpers . isMediaFile ( self . location ) :
2014-03-25 01:57:24 -04:00
logger . log ( u " 7 Status changes from " + str ( self . status ) + " to " + str (
2014-07-27 06:59:21 -04:00
Quality . statusFromName ( self . location , anime = self . show . is_anime ) ) , logger . DEBUG )
self . status = Quality . statusFromName ( self . location , anime = self . show . is_anime )
2014-03-10 01:18:05 -04:00
nfoFile = sickbeard . helpers . replaceExtension ( self . location , " nfo " )
logger . log ( str ( self . show . indexerid ) + u " : Using NFO name " + nfoFile , logger . DEBUG )
if ek . ek ( os . path . isfile , nfoFile ) :
try :
showXML = etree . ElementTree ( file = nfoFile )
except ( SyntaxError , ValueError ) , e :
2014-03-25 01:57:24 -04:00
logger . log ( u " Error loading the NFO, backing up the NFO and skipping for now: " + ex ( e ) ,
2014-05-27 03:44:23 -04:00
logger . ERROR ) # TODO: figure out what's wrong and fix it
2014-03-10 01:18:05 -04:00
try :
ek . ek ( os . rename , nfoFile , nfoFile + " .old " )
except Exception , e :
2014-03-25 01:57:24 -04:00
logger . log (
u " Failed to rename your episode ' s NFO file - you need to delete it or fix it: " + ex ( e ) ,
logger . ERROR )
2014-03-10 01:18:05 -04:00
raise exceptions . NoNFOException ( " Error in NFO format " )
for epDetails in showXML . getiterator ( ' episodedetails ' ) :
if epDetails . findtext ( ' season ' ) is None or int ( epDetails . findtext ( ' season ' ) ) != self . season or \
2014-03-25 01:57:24 -04:00
epDetails . findtext ( ' episode ' ) is None or int (
epDetails . findtext ( ' episode ' ) ) != self . episode :
logger . log ( str (
self . show . indexerid ) + u " : NFO has an <episodedetails> block for a different episode - wanted " + str (
self . season ) + " x " + str ( self . episode ) + " but got " + str (
epDetails . findtext ( ' season ' ) ) + " x " + str ( epDetails . findtext ( ' episode ' ) ) , logger . DEBUG )
2014-03-10 01:18:05 -04:00
continue
if epDetails . findtext ( ' title ' ) is None or epDetails . findtext ( ' aired ' ) is None :
raise exceptions . NoNFOException ( " Error in NFO format (missing episode title or airdate) " )
self . name = epDetails . findtext ( ' title ' )
self . episode = int ( epDetails . findtext ( ' episode ' ) )
self . season = int ( epDetails . findtext ( ' season ' ) )
2014-08-18 08:40:29 -04:00
sickbeard . scene_numbering . xem_refresh ( self . show . indexerid , self . show . indexer )
self . scene_absolute_number = sickbeard . scene_numbering . get_scene_absolute_numbering (
self . show . indexerid ,
self . show . indexer ,
self . absolute_number
)
self . scene_season , self . scene_episode = sickbeard . scene_numbering . get_scene_numbering (
self . show . indexerid ,
self . show . indexer ,
self . season , self . episode
)
2014-03-10 01:18:05 -04:00
self . description = epDetails . findtext ( ' plot ' )
if self . description is None :
self . description = " "
if epDetails . findtext ( ' aired ' ) :
rawAirdate = [ int ( x ) for x in epDetails . findtext ( ' aired ' ) . split ( " - " ) ]
self . airdate = datetime . date ( rawAirdate [ 0 ] , rawAirdate [ 1 ] , rawAirdate [ 2 ] )
else :
self . airdate = datetime . date . fromordinal ( 1 )
self . hasnfo = True
else :
self . hasnfo = False
if ek . ek ( os . path . isfile , sickbeard . helpers . replaceExtension ( nfoFile , " tbn " ) ) :
self . hastbn = True
else :
self . hastbn = False
def __str__ ( self ) :
toReturn = " "
2014-03-25 01:57:24 -04:00
toReturn + = str ( self . show . name ) + " - " + str ( self . season ) + " x " + str ( self . episode ) + " - " + str (
self . name ) + " \n "
2014-03-10 01:18:05 -04:00
toReturn + = " location: " + str ( self . location ) + " \n "
toReturn + = " description: " + str ( self . description ) + " \n "
toReturn + = " subtitles: " + str ( " , " . join ( self . subtitles ) ) + " \n "
toReturn + = " subtitles_searchcount: " + str ( self . subtitles_searchcount ) + " \n "
toReturn + = " subtitles_lastsearch: " + str ( self . subtitles_lastsearch ) + " \n "
toReturn + = " airdate: " + str ( self . airdate . toordinal ( ) ) + " ( " + str ( self . airdate ) + " ) \n "
toReturn + = " hasnfo: " + str ( self . hasnfo ) + " \n "
toReturn + = " hastbn: " + str ( self . hastbn ) + " \n "
toReturn + = " status: " + str ( self . status ) + " \n "
return toReturn
2014-05-14 05:42:08 -04:00
def createMetaFiles ( self ) :
2014-03-10 01:18:05 -04:00
if not ek . ek ( os . path . isdir , self . show . _location ) :
logger . log ( str ( self . show . indexerid ) + u " : The show dir is missing, not bothering to try to create metadata " )
return
2014-05-14 05:42:08 -04:00
self . createNFO ( )
2014-03-10 01:18:05 -04:00
self . createThumbnail ( )
if self . checkForMetaFiles ( ) :
self . saveToDB ( )
2014-05-14 05:42:08 -04:00
def createNFO ( self ) :
2014-03-10 01:18:05 -04:00
result = False
for cur_provider in sickbeard . metadata_provider_dict . values ( ) :
2014-05-14 05:42:08 -04:00
result = cur_provider . create_episode_metadata ( self ) or result
2014-03-10 01:18:05 -04:00
return result
2014-05-14 05:42:08 -04:00
def createThumbnail ( self ) :
2014-03-10 01:18:05 -04:00
result = False
for cur_provider in sickbeard . metadata_provider_dict . values ( ) :
result = cur_provider . create_episode_thumb ( self ) or result
return result
def deleteEpisode ( self ) :
2014-03-25 01:57:24 -04:00
logger . log ( u " Deleting " + self . show . name + " " + str ( self . season ) + " x " + str ( self . episode ) + " from the DB " ,
logger . DEBUG )
2014-03-10 01:18:05 -04:00
# remove myself from the show dictionary
if self . show . getEpisode ( self . season , self . episode , noCreate = True ) == self :
logger . log ( u " Removing myself from my show ' s list " , logger . DEBUG )
2014-06-11 10:16:24 -04:00
del self . show . episodes [ self . season ] [ self . episode ]
2014-03-10 01:18:05 -04:00
# delete myself from the DB
logger . log ( u " Deleting myself from the database " , logger . DEBUG )
2014-06-21 18:46:59 -04:00
myDB = db . DBConnection ( )
sql = " DELETE FROM tv_episodes WHERE showid= " + str ( self . show . indexerid ) + " AND season= " + str (
self . season ) + " AND episode= " + str ( self . episode )
myDB . action ( sql )
2014-03-10 01:18:05 -04:00
raise exceptions . EpisodeDeletedException ( )
2014-03-20 06:24:58 -04:00
def get_sql ( self , forceSave = False ) :
"""
Creates SQL queue for this episode if any of its data has been changed since the last save .
forceSave : If True it will create SQL queue even if no data has been changed since the
last save ( aka if the record is not dirty ) .
"""
if not self . dirty and not forceSave :
2014-04-24 07:52:44 -04:00
logger . log ( str ( self . show . indexerid ) + u " : Not creating SQL queue - record is not dirty " , logger . DEBUG )
2014-03-20 06:24:58 -04:00
return
2014-06-21 18:46:59 -04:00
myDB = db . DBConnection ( )
rows = myDB . select (
' SELECT episode_id FROM tv_episodes WHERE showid = ? AND season = ? AND episode = ? ' ,
[ self . show . indexerid , self . season , self . episode ] )
2014-06-06 18:16:15 -04:00
epID = None
if rows :
epID = int ( rows [ 0 ] [ ' episode_id ' ] )
if epID :
# use a custom update method to get the data into the DB for existing records.
return [
" UPDATE tv_episodes SET indexerid = ?, indexer = ?, name = ?, description = ?, subtitles = ?, "
" subtitles_searchcount = ?, subtitles_lastsearch = ?, airdate = ?, hasnfo = ?, hastbn = ?, status = ?, "
" location = ?, file_size = ?, release_name = ?, is_proper = ?, showid = ?, season = ?, episode = ?, "
2014-07-22 00:53:32 -04:00
" absolute_number = ?, version = ?, release_group = ? WHERE episode_id = ? " ,
2014-06-06 18:16:15 -04:00
[ self . indexerid , self . indexer , self . name , self . description , " , " . join ( [ sub for sub in self . subtitles ] ) ,
2014-06-07 11:33:18 -04:00
self . subtitles_searchcount , self . subtitles_lastsearch , self . airdate . toordinal ( ) , self . hasnfo ,
self . hastbn ,
self . status , self . location , self . file_size , self . release_name , self . is_proper , self . show . indexerid ,
2014-07-22 00:53:32 -04:00
self . season , self . episode , self . absolute_number , self . version , self . release_group , epID ] ]
2014-06-06 18:16:15 -04:00
else :
# use a custom insert method to get the data into the DB.
return [
2014-07-22 00:53:32 -04:00
" INSERT OR IGNORE INTO tv_episodes (episode_id, indexerid, indexer, name, description, subtitles, "
" subtitles_searchcount, subtitles_lastsearch, airdate, hasnfo, hastbn, status, location, file_size, "
" release_name, is_proper, showid, season, episode, absolute_number, version, release_group) VALUES "
" ((SELECT episode_id FROM tv_episodes WHERE showid = ? AND season = ? AND episode = ?) "
" ,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?); " ,
2014-06-07 11:33:18 -04:00
[ self . show . indexerid , self . season , self . episode , self . indexerid , self . indexer , self . name ,
self . description ,
2014-06-06 18:16:15 -04:00
" , " . join ( [ sub for sub in self . subtitles ] ) , self . subtitles_searchcount , self . subtitles_lastsearch ,
self . airdate . toordinal ( ) , self . hasnfo , self . hastbn , self . status , self . location , self . file_size ,
2014-06-07 11:33:18 -04:00
self . release_name , self . is_proper , self . show . indexerid , self . season , self . episode ,
2014-07-22 00:53:32 -04:00
self . absolute_number , self . version , self . release_group ] ]
2014-03-20 06:24:58 -04:00
2014-03-10 01:18:05 -04:00
def saveToDB ( self , forceSave = False ) :
"""
Saves this episode to the database if any of its data has been changed since the last save .
forceSave : If True it will save to the database even if no data has been changed since the
last save ( aka if the record is not dirty ) .
"""
if not self . dirty and not forceSave :
logger . log ( str ( self . show . indexerid ) + u " : Not saving episode to db - record is not dirty " , logger . DEBUG )
return
logger . log ( str ( self . show . indexerid ) + u " : Saving episode details to database " , logger . DEBUG )
logger . log ( u " STATUS IS " + str ( self . status ) , logger . DEBUG )
newValueDict = { " indexerid " : self . indexerid ,
" indexer " : self . indexer ,
" name " : self . name ,
" description " : self . description ,
" subtitles " : " , " . join ( [ sub for sub in self . subtitles ] ) ,
" subtitles_searchcount " : self . subtitles_searchcount ,
" subtitles_lastsearch " : self . subtitles_lastsearch ,
" airdate " : self . airdate . toordinal ( ) ,
" hasnfo " : self . hasnfo ,
" hastbn " : self . hastbn ,
" status " : self . status ,
" location " : self . location ,
" file_size " : self . file_size ,
" release_name " : self . release_name ,
2014-05-26 02:29:22 -04:00
" is_proper " : self . is_proper ,
2014-07-22 00:53:32 -04:00
" absolute_number " : self . absolute_number ,
" version " : self . version ,
" release_group " : self . release_group
2014-05-27 03:44:23 -04:00
}
2014-03-10 01:18:05 -04:00
controlValueDict = { " showid " : self . show . indexerid ,
" season " : self . season ,
" episode " : self . episode }
# use a custom update/insert method to get the data into the DB
2014-06-21 18:46:59 -04:00
myDB = db . DBConnection ( )
myDB . upsert ( " tv_episodes " , newValueDict , controlValueDict )
2014-03-10 01:18:05 -04:00
def fullPath ( self ) :
2014-03-20 14:03:22 -04:00
if self . location == None or self . location == " " :
2014-03-10 01:18:05 -04:00
return None
else :
return ek . ek ( os . path . join , self . show . location , self . location )
2014-05-31 06:35:11 -04:00
def createStrings ( self , pattern = None ) :
patterns = [
' % S.N.S % SE %0E ' ,
' % S.N.S % 0SE %E ' ,
' % S.N.S % SE %E ' ,
' % S.N.S % 0SE %0E ' ,
' % SN S % SE %0E ' ,
' % SN S % 0SE %E ' ,
' % SN S % SE %E ' ,
' % SN S % 0SE %0E '
]
strings = [ ]
if not pattern :
for p in patterns :
strings + = [ self . _format_pattern ( p ) ]
return strings
return self . _format_pattern ( pattern )
2014-03-10 01:18:05 -04:00
def prettyName ( self ) :
"""
Returns the name of this episode in a " pretty " human - readable format . Used for logging
and notifications and such .
Returns : A string representing the episode ' s name and season/ep numbers
"""
2014-05-26 02:29:22 -04:00
2014-05-31 06:35:11 -04:00
if self . show . anime and not self . show . scene :
return self . _format_pattern ( ' % SN - % AB - %E N ' )
2014-05-30 02:22:01 -04:00
elif self . show . air_by_date :
return self . _format_pattern ( ' % SN - % AD - %E N ' )
2014-05-31 06:35:11 -04:00
return self . _format_pattern ( ' % SN - % Sx %0E - %E N ' )
2014-04-26 06:37:40 -04:00
2014-03-10 01:18:05 -04:00
def _ep_name ( self ) :
"""
Returns the name of the episode to use during renaming . Combines the names of related episodes .
Eg . " Ep Name (1) " and " Ep Name (2) " becomes " Ep Name "
" Ep Name " and " Other Ep Name " becomes " Ep Name & Other Ep Name "
"""
multiNameRegex = " (.*) \ ( \ d { 1,2} \ ) "
self . relatedEps = sorted ( self . relatedEps , key = lambda x : x . episode )
if len ( self . relatedEps ) == 0 :
goodName = self . name
else :
goodName = ' '
singleName = True
curGoodName = None
for curName in [ self . name ] + [ x . name for x in self . relatedEps ] :
match = re . match ( multiNameRegex , curName )
if not match :
singleName = False
break
2014-03-20 14:03:22 -04:00
if curGoodName == None :
2014-03-10 01:18:05 -04:00
curGoodName = match . group ( 1 )
elif curGoodName != match . group ( 1 ) :
singleName = False
break
if singleName :
goodName = curGoodName
else :
goodName = self . name
for relEp in self . relatedEps :
goodName + = " & " + relEp . name
return goodName
def _replace_map ( self ) :
"""
Generates a replacement map for this episode which maps all possible custom naming patterns to the correct
value for this episode .
Returns : A dict with patterns as the keys and their replacement values as the values .
"""
ep_name = self . _ep_name ( )
def dot ( name ) :
return helpers . sanitizeSceneName ( name )
def us ( name ) :
return re . sub ( ' [ -] ' , ' _ ' , name )
def release_name ( name ) :
2014-07-19 18:23:01 -04:00
if name :
name = helpers . remove_non_release_groups ( helpers . remove_extension ( name ) )
2014-03-10 01:18:05 -04:00
return name
2014-07-15 05:40:21 -04:00
def release_group ( show , name ) :
2014-07-19 18:23:01 -04:00
if name :
name = helpers . remove_non_release_groups ( helpers . remove_extension ( name ) )
else :
return " "
2014-03-10 01:18:05 -04:00
try :
2014-07-15 05:40:21 -04:00
np = NameParser ( name , showObj = show , naming_pattern = True )
2014-03-10 01:18:05 -04:00
parse_result = np . parse ( name )
2014-07-06 09:11:04 -04:00
except ( InvalidNameException , InvalidShowException ) , e :
2014-03-10 01:18:05 -04:00
logger . log ( u " Unable to get parse release_group: " + ex ( e ) , logger . DEBUG )
return ' '
if not parse_result . release_group :
return ' '
return parse_result . release_group
epStatus , epQual = Quality . splitCompositeStatus ( self . status ) # @UnusedVariable
if sickbeard . NAMING_STRIP_YEAR :
show_name = re . sub ( " \ ( \ d+ \ )$ " , " " , self . show . name ) . rstrip ( )
else :
show_name = self . show . name
2014-04-30 08:10:13 -04:00
return {
' % SN ' : show_name ,
' % S.N ' : dot ( show_name ) ,
' % S_N ' : us ( show_name ) ,
' %E N ' : ep_name ,
' %E .N ' : dot ( ep_name ) ,
' %E _N ' : us ( ep_name ) ,
' % QN ' : Quality . qualityStrings [ epQual ] ,
' % Q.N ' : dot ( Quality . qualityStrings [ epQual ] ) ,
' % Q_N ' : us ( Quality . qualityStrings [ epQual ] ) ,
' % S ' : str ( self . season ) ,
2014-05-08 10:03:50 -04:00
' % 0S ' : ' %02d ' % self . season ,
2014-04-30 08:10:13 -04:00
' %E ' : str ( self . episode ) ,
2014-05-08 10:03:50 -04:00
' %0E ' : ' %02d ' % self . episode ,
2014-05-03 18:29:00 -04:00
' %X S ' : str ( self . scene_season ) ,
2014-05-08 10:03:50 -04:00
' %0X S ' : ' %02d ' % self . scene_season ,
2014-05-03 18:29:00 -04:00
' %X E ' : str ( self . scene_episode ) ,
2014-05-08 10:03:50 -04:00
' %0X E ' : ' %02d ' % self . scene_episode ,
2014-05-31 06:35:11 -04:00
' % AB ' : ' % (#)03d ' % { ' # ' : self . absolute_number } ,
2014-06-07 04:17:12 -04:00
' %X AB ' : ' % (#)03d ' % { ' # ' : self . scene_absolute_number } ,
2014-04-30 08:10:13 -04:00
' % RN ' : release_name ( self . release_name ) ,
2014-07-15 05:40:21 -04:00
' % RG ' : release_group ( self . show , self . release_name ) ,
2014-04-30 08:10:13 -04:00
' % AD ' : str ( self . airdate ) . replace ( ' - ' , ' ' ) ,
' % A.D ' : str ( self . airdate ) . replace ( ' - ' , ' . ' ) ,
' % A_D ' : us ( str ( self . airdate ) ) ,
' % A-D ' : str ( self . airdate ) ,
' % Y ' : str ( self . airdate . year ) ,
' % M ' : str ( self . airdate . month ) ,
' % D ' : str ( self . airdate . day ) ,
' % 0M ' : ' %02d ' % self . airdate . month ,
' % 0D ' : ' %02d ' % self . airdate . day ,
' % RT ' : " PROPER " if self . is_proper else " " ,
}
2014-04-29 00:55:59 -04:00
2014-03-10 01:18:05 -04:00
def _format_string ( self , pattern , replace_map ) :
"""
Replaces all template strings with the correct value
"""
result_name = pattern
# do the replacements
for cur_replacement in sorted ( replace_map . keys ( ) , reverse = True ) :
result_name = result_name . replace ( cur_replacement , helpers . sanitizeFileName ( replace_map [ cur_replacement ] ) )
2014-03-25 01:57:24 -04:00
result_name = result_name . replace ( cur_replacement . lower ( ) ,
helpers . sanitizeFileName ( replace_map [ cur_replacement ] . lower ( ) ) )
2014-03-10 01:18:05 -04:00
return result_name
2014-06-07 04:17:12 -04:00
def _format_pattern ( self , pattern = None , multi = None , anime_type = None ) :
2014-03-10 01:18:05 -04:00
"""
Manipulates an episode naming pattern and then fills the template in
"""
2014-03-20 14:03:22 -04:00
if pattern == None :
2014-03-10 01:18:05 -04:00
pattern = sickbeard . NAMING_PATTERN
2014-03-20 14:03:22 -04:00
if multi == None :
2014-03-10 01:18:05 -04:00
multi = sickbeard . NAMING_MULTI_EP
2014-06-07 04:17:12 -04:00
if anime_type == None :
2014-07-15 05:40:21 -04:00
anime_type = sickbeard . NAMING_ANIME
2014-06-07 04:17:12 -04:00
2014-03-10 01:18:05 -04:00
replace_map = self . _replace_map ( )
result_name = pattern
# if there's no release group then replace it with a reasonable facsimile
if not replace_map [ ' % RN ' ] :
2014-04-28 05:15:29 -04:00
if self . show . air_by_date or self . show . sports :
2014-05-26 17:32:47 -04:00
result_name = result_name . replace ( ' % RN ' , ' % S.N. % A.D. %E .N-SiCKRAGE ' )
result_name = result_name . replace ( ' %r n ' , ' %s .n. % A.D. %e .n-sickrage ' )
2014-07-15 05:40:21 -04:00
elif anime_type != 3 :
2014-06-07 04:17:12 -04:00
result_name = result_name . replace ( ' % RN ' , ' % S.N. % AB. %E .N-SiCKRAGE ' )
result_name = result_name . replace ( ' %r n ' , ' %s .n. %a b. %e .n-sickrage ' )
2014-03-10 01:18:05 -04:00
else :
2014-05-26 17:32:47 -04:00
result_name = result_name . replace ( ' % RN ' , ' % S.N.S % 0SE %0E . %E .N-SiCKRAGE ' )
result_name = result_name . replace ( ' %r n ' , ' %s .n.s %0s e %0e . %e .n-sickrage ' )
2014-03-10 01:18:05 -04:00
2014-05-26 17:32:47 -04:00
result_name = result_name . replace ( ' % RG ' , ' SICKRAGE ' )
result_name = result_name . replace ( ' %r g ' , ' sickrage ' )
2014-03-10 01:18:05 -04:00
logger . log ( u " Episode has no release name, replacing it with a generic one: " + result_name , logger . DEBUG )
2014-05-12 10:09:55 -04:00
if not replace_map [ ' % RT ' ] :
result_name = re . sub ( ' ([ _.-]*) % RT([ _.-]*) ' , r ' \ 2 ' , result_name )
2014-03-10 01:18:05 -04:00
# split off ep name part only
name_groups = re . split ( r ' [ \\ /] ' , result_name )
# figure out the double-ep numbering style for each group, if applicable
for cur_name_group in name_groups :
season_format = sep = ep_sep = ep_format = None
season_ep_regex = '''
( ? P < pre_sep > [ _ . - ] * )
( ( ? : s ( ? : eason | eries ) ? \s * ) ? % 0 ? S ( ? ! [ . _ ] ? N ) )
( . * ? )
( % 0 ? E ( ? ! [ . _ ] ? N ) )
( ? P < post_sep > [ _ . - ] * )
'''
ep_only_regex = ' (E? % 0?E(?![._]?N)) '
# try the normal way
season_ep_match = re . search ( season_ep_regex , cur_name_group , re . I | re . X )
ep_only_match = re . search ( ep_only_regex , cur_name_group , re . I | re . X )
# if we have a season and episode then collect the necessary data
if season_ep_match :
season_format = season_ep_match . group ( 2 )
ep_sep = season_ep_match . group ( 3 )
ep_format = season_ep_match . group ( 4 )
sep = season_ep_match . group ( ' pre_sep ' )
if not sep :
sep = season_ep_match . group ( ' post_sep ' )
if not sep :
sep = ' '
# force 2-3-4 format if they chose to extend
if multi in ( NAMING_EXTEND , NAMING_LIMITED_EXTEND , NAMING_LIMITED_EXTEND_E_PREFIXED ) :
ep_sep = ' - '
regex_used = season_ep_regex
# if there's no season then there's not much choice so we'll just force them to use 03-04-05 style
elif ep_only_match :
season_format = ' '
ep_sep = ' - '
ep_format = ep_only_match . group ( 1 )
sep = ' '
regex_used = ep_only_regex
else :
continue
# we need at least this much info to continue
if not ep_sep or not ep_format :
continue
# start with the ep string, eg. E03
ep_string = self . _format_string ( ep_format . upper ( ) , replace_map )
for other_ep in self . relatedEps :
# for limited extend we only append the last ep
2014-03-25 01:57:24 -04:00
if multi in ( NAMING_LIMITED_EXTEND , NAMING_LIMITED_EXTEND_E_PREFIXED ) and other_ep != self . relatedEps [
- 1 ] :
2014-03-10 01:18:05 -04:00
continue
elif multi == NAMING_DUPLICATE :
# add " - S01"
ep_string + = sep + season_format
elif multi == NAMING_SEPARATED_REPEAT :
ep_string + = sep
# add "E04"
ep_string + = ep_sep
if multi == NAMING_LIMITED_EXTEND_E_PREFIXED :
ep_string + = ' E '
ep_string + = other_ep . _format_string ( ep_format . upper ( ) , other_ep . _replace_map ( ) )
2014-07-15 05:40:21 -04:00
if anime_type != 3 :
2014-06-07 04:17:12 -04:00
if self . absolute_number == 0 :
curAbsolute_number = self . episode
else :
curAbsolute_number = self . absolute_number
if self . season != 0 : # dont set absolute numbers if we are on specials !
if anime_type == 1 : # this crazy person wants both ! (note: +=)
ep_string + = sep + " % (#)03d " % {
" # " : curAbsolute_number }
elif anime_type == 2 : # total anime freak only need the absolute number ! (note: =)
ep_string = " % (#)03d " % { " # " : curAbsolute_number }
for relEp in self . relatedEps :
if relEp . absolute_number != 0 :
ep_string + = ' - ' + " % (#)03d " % { " # " : relEp . absolute_number }
else :
ep_string + = ' - ' + " % (#)03d " % { " # " : relEp . episode }
regex_replacement = None
if anime_type == 2 :
regex_replacement = r ' \ g<pre_sep> ' + ep_string + r ' \ g<post_sep> '
elif season_ep_match :
2014-03-10 01:18:05 -04:00
regex_replacement = r ' \ g<pre_sep> \ g<2> \ g<3> ' + ep_string + r ' \ g<post_sep> '
elif ep_only_match :
regex_replacement = ep_string
2014-06-07 04:17:12 -04:00
if regex_replacement :
# fill out the template for this piece and then insert this piece into the actual pattern
cur_name_group_result = re . sub ( ' (?i)(?x) ' + regex_used , regex_replacement , cur_name_group )
# cur_name_group_result = cur_name_group.replace(ep_format, ep_string)
# logger.log(u"found "+ep_format+" as the ep pattern using "+regex_used+" and replaced it with "+regex_replacement+" to result in "+cur_name_group_result+" from "+cur_name_group, logger.DEBUG)
result_name = result_name . replace ( cur_name_group , cur_name_group_result )
2014-03-10 01:18:05 -04:00
result_name = self . _format_string ( result_name , replace_map )
logger . log ( u " formatting pattern: " + pattern + " -> " + result_name , logger . DEBUG )
return result_name
def proper_path ( self ) :
"""
Figures out the path where this episode SHOULD live according to the renaming rules , relative from the show dir
"""
2014-07-19 15:50:08 -04:00
anime_type = sickbeard . NAMING_ANIME
if not self . show . is_anime :
anime_type = 3
result = self . formatted_filename ( anime_type = anime_type )
2014-03-10 01:18:05 -04:00
# if they want us to flatten it and we're allowed to flatten it then we will
if self . show . flatten_folders and not sickbeard . NAMING_FORCE_FOLDERS :
return result
# if not we append the folder on and use that
else :
result = ek . ek ( os . path . join , self . formatted_dir ( ) , result )
return result
def formatted_dir ( self , pattern = None , multi = None ) :
"""
Just the folder name of the episode
"""
2014-03-20 14:03:22 -04:00
if pattern == None :
2014-03-10 01:18:05 -04:00
# we only use ABD if it's enabled, this is an ABD show, AND this is not a multi-ep
if self . show . air_by_date and sickbeard . NAMING_CUSTOM_ABD and not self . relatedEps :
pattern = sickbeard . NAMING_ABD_PATTERN
2014-04-28 05:15:29 -04:00
elif self . show . sports and sickbeard . NAMING_CUSTOM_SPORTS and not self . relatedEps :
pattern = sickbeard . NAMING_SPORTS_PATTERN
2014-09-28 05:20:42 -04:00
elif self . show . anime and sickbeard . NAMING_CUSTOM_ANIME :
pattern = sickbeard . NAMING_ANIME_PATTERN
2014-03-10 01:18:05 -04:00
else :
pattern = sickbeard . NAMING_PATTERN
# split off the dirs only, if they exist
name_groups = re . split ( r ' [ \\ /] ' , pattern )
if len ( name_groups ) == 1 :
return ' '
else :
return self . _format_pattern ( os . sep . join ( name_groups [ : - 1 ] ) , multi )
2014-06-07 04:17:12 -04:00
def formatted_filename ( self , pattern = None , multi = None , anime_type = None ) :
2014-03-10 01:18:05 -04:00
"""
Just the filename of the episode , formatted based on the naming settings
"""
2014-03-20 14:03:22 -04:00
if pattern == None :
2014-03-10 01:18:05 -04:00
# we only use ABD if it's enabled, this is an ABD show, AND this is not a multi-ep
if self . show . air_by_date and sickbeard . NAMING_CUSTOM_ABD and not self . relatedEps :
pattern = sickbeard . NAMING_ABD_PATTERN
2014-04-28 05:15:29 -04:00
elif self . show . sports and sickbeard . NAMING_CUSTOM_SPORTS and not self . relatedEps :
2014-04-28 09:35:49 -04:00
pattern = sickbeard . NAMING_SPORTS_PATTERN
2014-09-28 05:20:42 -04:00
elif self . show . anime and sickbeard . NAMING_CUSTOM_ANIME :
pattern = sickbeard . NAMING_ANIME_PATTERN
2014-03-10 01:18:05 -04:00
else :
pattern = sickbeard . NAMING_PATTERN
2014-05-08 13:10:13 -04:00
# split off the dirs only, if they exist
2014-03-10 01:18:05 -04:00
name_groups = re . split ( r ' [ \\ /] ' , pattern )
2014-06-07 04:17:12 -04:00
return self . _format_pattern ( name_groups [ - 1 ] , multi , anime_type )
2014-03-10 01:18:05 -04:00
def rename ( self ) :
"""
Renames an episode file and all related files to the location and filename as specified
in the naming settings .
"""
if not ek . ek ( os . path . isfile , self . location ) :
logger . log ( u " Can ' t perform rename on " + self . location + " when it doesn ' t exist, skipping " , logger . WARNING )
return
proper_path = self . proper_path ( )
absolute_proper_path = ek . ek ( os . path . join , self . show . location , proper_path )
absolute_current_path_no_ext , file_ext = ek . ek ( os . path . splitext , self . location )
absolute_current_path_no_ext_length = len ( absolute_current_path_no_ext )
related_subs = [ ]
current_path = absolute_current_path_no_ext
if absolute_current_path_no_ext . startswith ( self . show . location ) :
current_path = absolute_current_path_no_ext [ len ( self . show . location ) : ]
2014-03-25 01:57:24 -04:00
logger . log ( u " Renaming/moving episode from the base path " + self . location + " to " + absolute_proper_path ,
logger . DEBUG )
2014-03-10 01:18:05 -04:00
# if it's already named correctly then don't do anything
if proper_path == current_path :
2014-03-25 01:57:24 -04:00
logger . log ( str ( self . indexerid ) + u " : File " + self . location + " is already named correctly, skipping " ,
logger . DEBUG )
2014-03-10 01:18:05 -04:00
return
2014-03-29 00:41:35 -04:00
related_files = postProcessor . PostProcessor ( self . location ) . list_associated_files (
2014-03-25 01:57:24 -04:00
self . location )
2014-03-10 01:18:05 -04:00
if self . show . subtitles and sickbeard . SUBTITLES_DIR != ' ' :
2014-05-27 03:44:23 -04:00
related_subs = postProcessor . PostProcessor ( self . location ) . list_associated_files ( sickbeard . SUBTITLES_DIR ,
subtitles_only = True )
2014-03-10 01:18:05 -04:00
absolute_proper_subs_path = ek . ek ( os . path . join , sickbeard . SUBTITLES_DIR , self . formatted_filename ( ) )
logger . log ( u " Files associated to " + self . location + " : " + str ( related_files ) , logger . DEBUG )
# move the ep file
result = helpers . rename_ep_file ( self . location , absolute_proper_path , absolute_current_path_no_ext_length )
# move related files
for cur_related_file in related_files :
2014-03-25 01:57:24 -04:00
cur_result = helpers . rename_ep_file ( cur_related_file , absolute_proper_path ,
absolute_current_path_no_ext_length )
2014-05-02 07:33:06 -04:00
if not cur_result :
2014-03-10 01:18:05 -04:00
logger . log ( str ( self . indexerid ) + u " : Unable to rename file " + cur_related_file , logger . ERROR )
for cur_related_sub in related_subs :
2014-05-30 06:01:49 -04:00
absolute_proper_subs_path = ek . ek ( os . path . join , sickbeard . SUBTITLES_DIR , self . formatted_filename ( ) )
2014-05-27 03:44:23 -04:00
cur_result = helpers . rename_ep_file ( cur_related_sub , absolute_proper_subs_path ,
absolute_current_path_no_ext_length )
2014-05-02 07:33:06 -04:00
if not cur_result :
2014-03-10 01:18:05 -04:00
logger . log ( str ( self . indexerid ) + u " : Unable to rename file " + cur_related_sub , logger . ERROR )
# save the ep
with self . lock :
2014-05-02 07:33:06 -04:00
if result :
2014-03-10 01:18:05 -04:00
self . location = absolute_proper_path + file_ext
for relEp in self . relatedEps :
relEp . location = absolute_proper_path + file_ext
# in case something changed with the metadata just do a quick check
for curEp in [ self ] + self . relatedEps :
curEp . checkForMetaFiles ( )
2014-06-07 14:36:26 -04:00
# save any changes to the databas
2014-05-30 06:01:49 -04:00
sql_l = [ ]
2014-03-10 01:18:05 -04:00
with self . lock :
2014-06-07 17:32:38 -04:00
for relEp in [ self ] + self . relatedEps :
2014-05-30 06:01:49 -04:00
sql_l . append ( relEp . get_sql ( ) )
2014-07-14 22:00:53 -04:00
if len ( sql_l ) > 0 :
2014-06-21 18:46:59 -04:00
myDB = db . DBConnection ( )
myDB . mass_action ( sql_l )
2014-06-30 11:57:32 -04:00
2014-07-21 09:29:07 -04:00
def airdateModifyStamp ( self ) :
"""
Make the modify date and time of a file reflect the show air date and time .
Note : Also called from postProcessor
"""
hr = min = 0
airs = re . search ( ' .*?( \ d { 1,2})(?:: \ s*?( \ d {2} ))? \ s*(pm)? ' , self . show . airs , re . I )
if airs :
hr = int ( airs . group ( 1 ) )
hr = ( 12 + hr , hr ) [ None is airs . group ( 3 ) ]
2014-11-23 07:53:31 -05:00
hr = ( hr , hr - 12 ) [ 0 == hr % 12 and 0 != hr ]
2014-07-21 09:29:07 -04:00
min = int ( ( airs . group ( 2 ) , min ) [ None is airs . group ( 2 ) ] )
airtime = datetime . time ( hr , min )
airdatetime = datetime . datetime . combine ( self . airdate , airtime )
filemtime = datetime . datetime . fromtimestamp ( os . path . getmtime ( self . location ) )
if filemtime != airdatetime :
import time
airdatetime = airdatetime . timetuple ( )
if helpers . touchFile ( self . location , time . mktime ( airdatetime ) ) :
logger . log ( str ( self . show . indexerid ) + u " : Changed modify date of " + os . path . basename ( self . location )
+ " to show air date " + time . strftime ( " % b %d , % Y ( % H: % M) " , airdatetime ) )
2014-07-14 22:00:53 -04:00
def __getstate__ ( self ) :
d = dict ( self . __dict__ )
del d [ ' lock ' ]
return d
def __setstate__ ( self , d ) :
d [ ' lock ' ] = threading . Lock ( )
self . __dict__ . update ( d )