Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 327 lines (251 sloc) 11.571 kb
357a78a @midgetspy Added GPL stuff
authored
1 # Author: Nic Wolfe <nic@wolfeden.ca>
2 # URL: http://code.google.com/p/sickbeard/
3 #
e46eb09 @midgetspy Fixed some comments
authored
4 # This file is part of Sick Beard.
357a78a @midgetspy Added GPL stuff
authored
5 #
e46eb09 @midgetspy Fixed some comments
authored
6 # Sick Beard is free software: you can redistribute it and/or modify
357a78a @midgetspy Added GPL stuff
authored
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation, either version 3 of the License, or
9 # (at your option) any later version.
10 #
e46eb09 @midgetspy Fixed some comments
authored
11 # Sick Beard is distributed in the hope that it will be useful,
357a78a @midgetspy Added GPL stuff
authored
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
e46eb09 @midgetspy Fixed some comments
authored
17 # along with Sick Beard. If not, see <http://www.gnu.org/licenses/>.
357a78a @midgetspy Added GPL stuff
authored
18
19
20
7c9aaa8 @midgetspy Initial commit
authored
21 import os
22 import shutil
23 import sys
24
25 import contactXBMC
26 import exceptions
27 import helpers
28 import sickbeard
16e5b92 @midgetspy * Fixed issue 20
authored
29 import sqlite3
30 import db
7c9aaa8 @midgetspy Initial commit
authored
31
32 from logging import *
33 from common import *
34
35 from sickbeard import classes
16e5b92 @midgetspy * Fixed issue 20
authored
36 from lib.tvdb_api import tvnamer, tvdb_api, tvdb_exceptions
7c9aaa8 @midgetspy Initial commit
authored
37
38 #from tvdb_api.nfogen import createXBMCInfo
39
40 sample_ratio = 0.3
41
42 # #########################
43 # Find the file we're dealing with
44 # #########################
45 def findMainFile (show_dir):
46 # init vars
47 biggest_file = None
48 biggest_file_size = 0
49 next_biggest_file_size = 0
50
51 # find the biggest file in the folder
52 for file in filter(helpers.isMediaFile, os.listdir(show_dir)):
53 cur_size = os.path.getsize(os.path.join(show_dir, file))
54 if cur_size > biggest_file_size:
55 biggest_file = file
56 next_biggest_file_size = biggest_file_size
57 biggest_file_size = cur_size
58
59 if biggest_file == None:
60 return biggest_file
61
62 # it should be by far the biggest file in the folder. If it isn't, we have a problem (multi-show nzb or something, not going to deal with it)
63 if float(next_biggest_file_size) / float(biggest_file_size) > sample_ratio:
64 Logger().log("Multiple files in the folder are comparably large, giving up", ERROR)
65 return None
66
67 return os.path.join(show_dir, biggest_file)
68
69
70 # ########################
71 # Checks if another file exists already, if it
72 # does then we replace only if it's larger.
73 # ########################
74 def moveEpisode(file, ep):
75
76 # if the ep already has an associated file
77 if ep.fullPath() != None and os.path.isfile(ep.fullPath()):
78
79 Logger().log("The episode already has a file downloaded", DEBUG)
80
81 # see if it's smaller than our new file
82 if os.path.getsize(file) > os.path.getsize(ep.fullPath()):
83
84 Logger().log("The old file is smaller, replacing it", DEBUG)
85
86 try:
87 os.remove(ep.fullPath())
88 Logger().log("Deleted " + str(ep.fullPath()), DEBUG)
89 with ep.lock:
90 ep.location = None
91 except OSError as e:
92 Logger().log("Unable to delete existing file, it's probably in use (" + str(e) + ")", ERROR)
93
94 # move it to the right folder
95 if ep.show.seasonfolders == True:
96 seasonFolder = 'Season ' + str(ep.season)
97 else:
98 seasonFolder = ''
99 Logger().log("Seasonfolders were " + str(ep.show.seasonfolders) + " which gave " + seasonFolder)
100
101 destDir = os.path.join(ep.show.location, seasonFolder)
102
103 Logger().log("Moving from " + file + " to " + destDir, DEBUG)
104 try:
105 shutil.move(file, destDir)
106 for curEp in [ep] + ep.relatedEps:
107 with curEp.lock:
108 curEp.location = os.path.join(destDir, os.path.basename(file))
109
110 # don't mess up the status - if this is a legit download it should be SNATCHED
111 if curEp.status != PREDOWNLOADED:
112 curEp.status = DOWNLOADED
113
114 except IOError as e:
115 Logger().log("Unable to move the file: " + str(e), ERROR)
116 return False
117
118 return True
119
120
121 def renameFile(curFile, newName):
122
123 filePath = os.path.split(curFile)
124 oldFile = os.path.splitext(filePath[1])
125
126 newFilename = os.path.join(filePath[0], helpers.sanitizeFileName(newName) + oldFile[1])
127 Logger().log("Renaming from " + curFile + " to " + newFilename)
128
129 try:
130 os.rename(curFile, newFilename)
131 except (OSError, IOError) as e:
132 Logger().log("Failed renaming " + curFile + " to " + os.path.basename(newFilename) + ": " + str(e), ERROR)
133 return False
134
135 return newFilename
136
137
138 def doIt(downloadDir, showList):
139
140 returnStr = ""
141
142 if not os.path.isdir(downloadDir):
143 return "Uh, this is not a directory: " + str(downloadDir)
144
145 # pretty up the path, just in case
146 downloadDir = os.path.abspath(downloadDir)
147 logStr = "Pretty'd up folder is " + downloadDir
148 Logger().log(logStr, DEBUG)
149 returnStr += logStr + "\n"
150
151 # TODO: check if it's failed and deal with it if it is
152 if downloadDir.startswith('_FAILED_'):
153 logStr = "The directory name indicates it failed to extract, cancelling"
154 Logger().log(logStr, DEBUG)
155 returnStr += logStr + "\n"
156 return returnStr
157
158 # find the file we're dealing with
159 biggest_file = findMainFile(downloadDir)
160 if biggest_file == None:
161 logStr = "Unable to find the biggest file - is this really a TV download?"
162 Logger().log(logStr, DEBUG)
163 returnStr += logStr + "\n"
164 return returnStr
165
166 logStr = "The biggest file in the dir is: " + biggest_file
167 Logger().log(logStr, DEBUG)
168 returnStr += logStr + "\n"
169
170 # try to use the file name to get the episode
171 result = None
172 for curName in (biggest_file, downloadDir.split(os.path.sep)[-1]):
173
174 result = tvnamer.processSingleName(curName)
175 logStr = curName + " parsed into: " + str(result)
176 Logger().log(logStr, DEBUG)
177 returnStr += logStr + "\n"
178
179 # if this one doesn't work try the next one
180 if result == None:
181 logStr = "Unable to parse this name"
182 Logger().log(logStr, DEBUG)
183 returnStr += logStr + "\n"
184 continue
16e5b92 @midgetspy * Fixed issue 20
authored
185
186 try:
187 t = tvdb_api.Tvdb(custom_ui=classes.ShowListUI, lastTimeout=sickbeard.LAST_TVDB_TIMEOUT)
188 showObj = t[result["file_seriesname"]]
189 showInfo = (int(showObj["id"]), showObj["seriesname"])
0f4c429 @midgetspy Tons of untested changes, fun.
authored
190 except (tvdb_exceptions.tvdb_error, IOError) as e:
16e5b92 @midgetspy * Fixed issue 20
authored
191
192 logStr = "TVDB didn't respond, trying to look up the show in the DB instead"
193 Logger().log(logStr, DEBUG)
194 returnStr += logStr + "\n"
195
196 # if tvdb fails then try looking it up in the db
0f4c429 @midgetspy Tons of untested changes, fun.
authored
197 #myDB = db.DBConnection()
198 #myDB.checkDB()
16e5b92 @midgetspy * Fixed issue 20
authored
199
0f4c429 @midgetspy Tons of untested changes, fun.
authored
200 #sqlResults = []
7c9aaa8 @midgetspy Initial commit
authored
201
0f4c429 @midgetspy Tons of untested changes, fun.
authored
202 #try:
203 # sql = "SELECT * FROM tv_shows WHERE show_name LIKE ?"
204 # sqlArgs = ['%'+result["file_seriesname"]]
205 # Logger().log("SQL: "+sql+" % "+str(sqlArgs))
206 # sqlResults = myDB.connection.execute(sql, sqlArgs).fetchall()
207 #except sqlite3.DatabaseError as e:
208 # Logger().log("Fatal error executing query '" + sql + "': " + str(e), ERROR)
209 # raise
16e5b92 @midgetspy * Fixed issue 20
authored
210
0f4c429 @midgetspy Tons of untested changes, fun.
authored
211 #if len(sqlResults) != 1:
212 # if len(sqlResults) == 0:
213 # logStr = "Unable to match a record in the DB for "+result["file_seriesname"]
214 # else:
215 # logStr = "Multiple results for "+result["file_seriesname"]+" in the DB, unable to match show name"
216 # Logger().log(logStr, DEBUG)
217 # returnStr += logStr + "\n"
218 # continue
219
220 #showInfo = (int(sqlResults[0]["tvdb_id"]), sqlResults[0]["show_name"])
221
222 showInfo = helpers.searchDBForShow(result["file_seriesname"])
16e5b92 @midgetspy * Fixed issue 20
authored
223
0f4c429 @midgetspy Tons of untested changes, fun.
authored
224 # if we didn't get anything from TVDB or the DB then try the next option
225 if showInfo == None:
226 continue
227
7c9aaa8 @midgetspy Initial commit
authored
228 # find the show in the showlist
229 try:
16e5b92 @midgetspy * Fixed issue 20
authored
230 showResults = helpers.findCertainShow(showList, showInfo[0])
7c9aaa8 @midgetspy Initial commit
authored
231 except exceptions.MultipleShowObjectsException:
232 raise #TODO: later I'll just log this, for now I want to know about it ASAP
233
234 if showResults != None:
235 logStr = "Found the show in our list, continuing"
236 Logger().log(logStr, DEBUG)
237 returnStr += logStr + "\n"
238 break
16e5b92 @midgetspy * Fixed issue 20
authored
239
240 # end for
7c9aaa8 @midgetspy Initial commit
authored
241
242 if result == None:
243 logStr = "Unable to figure out what this episode is, giving up"
244 Logger().log(logStr, DEBUG)
245 returnStr += logStr + "\n"
246 return returnStr
247
248 if showResults == None:
249 logStr = "The episode doesn't match a show in my list - bad naming?"
250 Logger().log(logStr, DEBUG)
251 returnStr += logStr + "\n"
252 return returnStr
253
254
255
256 # get or create the episode (should be created probably, but not for sure)
257 season = int(result["seasno"])
258
259 rootEp = None
260 for curEpisode in result["epno"]:
261 episode = int(curEpisode)
262
16e5b92 @midgetspy * Fixed issue 20
authored
263 logStr = "TVDB thinks the file is " + showInfo[1] + str(season) + "x" + str(episode)
7c9aaa8 @midgetspy Initial commit
authored
264 Logger().log(logStr, DEBUG)
265 returnStr += logStr + "\n"
266
267 # now that we've figured out which episode this file is just load it manually
268 curEp = showResults.getEpisode(season, episode, True)
269
270 if rootEp == None:
271 rootEp = curEp
272 rootEp.relatedEps = []
273 else:
274 rootEp.relatedEps.append(curEp)
275
276 if sickbeard.XBMC_NOTIFY_ONDOWNLOAD == True:
277 contactXBMC.notifyXBMC(rootEp.prettyName(), "Download finished")
278
279 # rename it
280 logStr = "Renaming the file " + biggest_file + " to " + rootEp.prettyName()
281 Logger().log(logStr, DEBUG)
282 returnStr += logStr + "\n"
283
284 result = renameFile(biggest_file, rootEp.prettyName())
285
286 if result == False:
287 logStr = "ERROR: Unable to rename the file " + biggest_file
288 Logger().log(logStr, DEBUG)
289 returnStr += logStr + "\n"
290 return logStr
291
292 result = moveEpisode(os.path.join(downloadDir, os.path.basename(result)), rootEp)
293 if result == True:
294 rootEp.createMetaFiles()
295 rootEp.saveToDB()
296 logStr = "File was moved successfully"
297 Logger().log(logStr, DEBUG)
298 returnStr += logStr + "\n"
299 else:
300 logStr = "I couldn't move it, giving up"
301 Logger().log(logStr, DEBUG)
302 returnStr += logStr + "\n"
303 return returnStr
304
305 # generate nfo/tbn
306
307 logStr = "Deleting folder " + downloadDir
308 Logger().log(logStr, DEBUG)
309 returnStr += logStr + "\n"
310
311 # we don't want to put predownloads in the library until we can deal with removing them
312 if sickbeard.XBMC_UPDATE_LIBRARY == True and rootEp.status != PREDOWNLOADED:
313 contactXBMC.updateLibrary(rootEp.show.location)
314
315 # delete the old folder full of useless files
316 try:
317 shutil.rmtree(downloadDir)
318 except (OSError, IOError) as e:
319 logStr = "Warning: unable to remove the folder " + downloadDir + ": " + str(e)
320 Logger().log(logStr, ERROR)
321 returnStr += logStr + "\n"
322
323 return returnStr
324
325 if __name__ == "__main__":
326 doIt(sys.argv[1])
Something went wrong with that request. Please try again.