def update_show_indexer_metadata(self, show_obj): if self.show_metadata and show_obj and self._has_show_metadata(show_obj): logger.log( u"Metadata provider " + self.name + " updating show indexer info metadata file for " + show_obj.name, logger.DEBUG, ) nfo_file_path = self.get_show_file_path(show_obj) try: with ek.ek(open, nfo_file_path, "r") as xmlFileObj: showXML = etree.ElementTree(file=xmlFileObj) indexerid = showXML.find("id") root = showXML.getroot() if indexerid: indexerid.text = show_obj.indexerid else: etree.SubElement(root, "id").text = str(show_obj.indexerid) # Make it purdy helpers.indentXML(root) showXML.write(nfo_file_path) helpers.chmodAsParent(nfo_file_path) return True except IOError, e: logger.log( u"Unable to write file to " + nfo_file_path + " - are you sure the folder is writable? " + ex(e), logger.ERROR, )
def _write_image(self, image_data, image_path): """ Saves the data in image_data to the location image_path. Returns True/False to represent success or failure. image_data: binary image data to write to file image_path: file location to save the image to """ # don't bother overwriting it if ek.ek(os.path.isfile, image_path): logger.log(u"Image already exists, not downloading", logger.DEBUG) return False if not image_data: logger.log(u"Unable to retrieve image, skipping", logger.WARNING) return False image_dir = ek.ek(os.path.dirname, image_path) try: if not ek.ek(os.path.isdir, image_dir): logger.log("Metadata dir didn't exist, creating it at "+image_dir, logger.DEBUG) ek.ek(os.makedirs, image_dir) helpers.chmodAsParent(image_dir) outFile = ek.ek(open, image_path, 'wb') outFile.write(image_data) outFile.close() helpers.chmodAsParent(image_path) except IOError, e: logger.log(u"Unable to write image to "+image_path+" - are you sure the show folder is writable? "+ex(e), logger.ERROR) return False
def addDefaultShow(indexer, indexer_id, name, status): """ Adds a new show with the default settings """ if not Show.find(sickbeard.showList, int(indexer_id)): logger.log(u"Adding show " + str(indexer_id)) root_dirs = sickbeard.ROOT_DIRS.split('|') try: location = root_dirs[int(root_dirs[0]) + 1] except Exception: location = None if location: showPath = ek(os.path.join, location, sanitize_filename(name)) dir_exists = helpers.makeDir(showPath) if not dir_exists: logger.log(u"Unable to create the folder %s , can't add the show" % showPath, logger.WARNING) return else: helpers.chmodAsParent(showPath) sickbeard.showQueueScheduler.action.addShow(int(indexer), int(indexer_id), showPath, default_status=status, quality=int(sickbeard.QUALITY_DEFAULT), flatten_folders=int(sickbeard.FLATTEN_FOLDERS_DEFAULT), paused=sickbeard.TRAKT_START_PAUSED, default_status_after=status) else: logger.log(u"There was an error creating the show, no root directory setting found", logger.WARNING) return
def update_show_indexer_metadata(self, show_obj): if self.show_metadata and show_obj and self._has_show_metadata(show_obj): logger.log( u"Metadata provider " + self.name + " updating show indexer info metadata file for " + show_obj.name, logger.DEBUG) nfo_file_path = self.get_show_file_path(show_obj) assert isinstance(nfo_file_path, unicode) try: with io.open(nfo_file_path, 'rb') as xmlFileObj: showXML = etree.ElementTree(file=xmlFileObj) indexerid = showXML.find('id') root = showXML.getroot() if indexerid is not None: indexerid.text = str(show_obj.indexerid) else: etree.SubElement(root, "id").text = str(show_obj.indexerid) # Make it purdy helpers.indentXML(root) showXML.write(nfo_file_path, encoding='UTF-8') helpers.chmodAsParent(nfo_file_path) return True except IOError, e: logger.log( u"Unable to write file to " + nfo_file_path + " - are you sure the folder is writable? " + ex(e), logger.ERROR)
def downloadResult(self, result): """ Save the result to disk. """ logger.log(u"Downloading a result from " + self.name + " at " + result.url) data = self.getURL(result.url) if data is None: return False # use the appropriate watch folder if self.providerType == GenericProvider.NZB: saveDir = sickbeard.NZB_DIR writeMode = 'w' elif self.providerType == GenericProvider.TORRENT: saveDir = sickbeard.TORRENT_DIR writeMode = 'wb' else: return False # use the result name as the filename file_name = ek.ek(os.path.join, saveDir, helpers.sanitizeFileName(result.name) + '.' + self.providerType) logger.log(u"Saving to " + file_name, logger.DEBUG) try: with open(file_name, writeMode) as fileOut: fileOut.write(data) helpers.chmodAsParent(file_name) except EnvironmentError, e: logger.log("Unable to save the file: " + ex(e), logger.ERROR) return False
def add_show(indexer, indexer_id, show_name, status): """ Adds a new show with the default settings """ if not Show.find(sickbeard.showList, int(indexer_id)): root_dirs = sickbeard.ROOT_DIRS.split('|') location = root_dirs[int(root_dirs[0]) + 1] if root_dirs else None if location: show_path = ek(os.path.join, location, show_name) logger.log(u"Adding show '{}' with ID: '{}' in location: '{}'".format(show_name, indexer_id, show_path)) dir_exists = helpers.makeDir(show_path) if not dir_exists: logger.log(u"Unable to create the folder {}. Unable to add the show {}".format(show_path, show_name), logger.WARNING) return else: logger.log(u"Creating the folder '{}'".format(show_path), logger.DEBUG) helpers.chmodAsParent(show_path) sickbeard.showQueueScheduler.action.addShow(indexer, indexer_id, show_path, default_status=status, quality=int(sickbeard.QUALITY_DEFAULT), flatten_folders=int(sickbeard.FLATTEN_FOLDERS_DEFAULT), paused=sickbeard.TRAKT_START_PAUSED, default_status_after=status) else: logger.log(u"There was an error creating the show, no root directory setting found", logger.WARNING) return
def addDefaultShow(self, indexer, indexer_id, name, status): """ Adds a new show with the default settings """ if not helpers.findCertainShow(sickbeard.showList, int(indexer_id)): logger.log(u"Adding show " + str(indexer_id)) root_dirs = sickbeard.ROOT_DIRS.split('|') try: location = root_dirs[int(root_dirs[0]) + 1] except: location = None if location: showPath = ek.ek(os.path.join, location, helpers.sanitizeFileName(name)) dir_exists = helpers.makeDir(showPath) if not dir_exists: logger.log(u"Unable to create the folder " + showPath + ", can't add the show", logger.ERROR) return else: helpers.chmodAsParent(showPath) sickbeard.showQueueScheduler.action.addShow(int(indexer), int(indexer_id), showPath, status, int(sickbeard.QUALITY_DEFAULT), int(sickbeard.FLATTEN_FOLDERS_DEFAULT), paused=sickbeard.TRAKT_START_PAUSED) else: logger.log(u"There was an error creating the show, no root directory setting found", logger.ERROR) return
def _int_copy(cur_file_path, new_file_path): self._log(u"Copying file from " + cur_file_path + " to " + new_file_path, logger.DEBUG) try: helpers.copyFile(cur_file_path, new_file_path) helpers.chmodAsParent(new_file_path) except (IOError, OSError), e: logger.log(u"Unable to copy file " + cur_file_path + " to " + new_file_path + ": " + ex(e), logger.ERROR) raise e
def _int_move(cur_file_path, new_file_path): self._log(u"Moving file from " + cur_file_path + " to " + new_file_path, logger.DEBUG) try: helpers.moveFile(cur_file_path, new_file_path) helpers.chmodAsParent(new_file_path) except (IOError, OSError), e: self._log(u"Unable to move file " + cur_file_path + " to " + new_file_path + ": " + ex(e), logger.ERROR) raise e
def _int_hard_link(cur_file_path, new_file_path): self._log(u"Hard linking file from " + cur_file_path + " to " + new_file_path, logger.DEBUG) try: helpers.hardlinkFile(cur_file_path, new_file_path) helpers.chmodAsParent(new_file_path) except (IOError, OSError), e: self._log("Unable to link file " + cur_file_path + " to " + new_file_path + ": "+ex(e), logger.ERROR) raise e
def _int_hard_link(cur_file_path, new_file_path, success_tmpl=u' %s to %s'): try: helpers.hardlinkFile(cur_file_path, new_file_path) helpers.chmodAsParent(new_file_path) self._log(u'Hard linked file from' + (success_tmpl % (cur_file_path, new_file_path)), logger.DEBUG) except (IOError, OSError), e: self._log(u'Unable to link file %s<br />.. %s' % (success_tmpl % (cur_file_path, new_file_path), str(e)), logger.ERROR) raise e
def _int_copy(cur_file_path, new_file_path, success_tmpl=u' %s to %s'): try: helpers.copyFile(cur_file_path, new_file_path) helpers.chmodAsParent(new_file_path) self._log(u'Copied file from' + (success_tmpl % (cur_file_path, new_file_path)), logger.DEBUG) except (IOError, OSError), e: self._log(u'Unable to copy %s<br />.. %s' % (success_tmpl % (cur_file_path, new_file_path), str(e)), logger.ERROR) raise e
def _int_move_and_sym_link(cur_file_path, new_file_path): self._log(u"Moving then symbolic linking file from " + cur_file_path + " to " + new_file_path, logger.DEBUG) try: helpers.moveAndSymlinkFile(cur_file_path, new_file_path) helpers.chmodAsParent(new_file_path) except (IOError, OSError), e: self._log("Unable to link file " + cur_file_path + " to " + new_file_path + ": " + ex(e), logger.ERROR) raise e
def _int_copy (cur_file_path, new_file_path): self._log(u"Copying file from "+cur_file_path+" to "+new_file_path, logger.DEBUG) try: helpers.copyFile(cur_file_path, new_file_path) helpers.chmodAsParent(new_file_path) if sickbeard.UPDATE_DIRECTORY_TIMESTAMP: helpers.touchPath(helpers.getParentDirectory(new_file_path)) except (IOError, OSError), e: logger.log("Unable to copy file "+cur_file_path+" to "+new_file_path+": "+ex(e), logger.ERROR) raise e
def _int_move_and_sym_link(cur_file_path, new_file_path, success_tmpl=u' %s to %s'): try: helpers.moveAndSymlinkFile(cur_file_path, new_file_path) helpers.chmodAsParent(new_file_path) self._log(u'Moved then symbolic linked file from' + (success_tmpl % (cur_file_path, new_file_path)), logger.DEBUG) except (IOError, OSError), e: self._log(u'Unable to link file %s<br />.. %s' % (success_tmpl % (cur_file_path, new_file_path), str(e)), logger.ERROR) raise e
def dumpHTML(data): dumpName = ek(os.path.join, sickbeard.CACHE_DIR, 'custom_torrent.html') try: fileOut = io.open(dumpName, 'wb') fileOut.write(data) fileOut.close() helpers.chmodAsParent(dumpName) except IOError, e: logger.log(u"Unable to save the file: %s " % repr(e), logger.ERROR) return False
def dumpHTML(self, data): dumpName = ek.ek(os.path.join, sickbeard.CACHE_DIR, "custom_torrent.html") try: fileOut = open(dumpName, "wb") fileOut.write(data) fileOut.close() helpers.chmodAsParent(dumpName) except IOError, e: logger.log("Unable to save the file: " + ex(e), logger.ERROR) return False
def dumpHTML(data): dumpName = ek(os.path.join, sickbeard.CACHE_DIR, 'custom_torrent.html') try: fileOut = ek(io.open, dumpName, 'wb') fileOut.write(data) fileOut.close() helpers.chmodAsParent(dumpName) except IOError as e: logging.error("Unable to save the file: %s " % repr(e)) return False logging.info("Saved custom_torrent html dump %s " % dumpName) return True
def _int_link (cur_file_path, new_file_path): self._log(u"Linking file from "+cur_file_path+" to "+new_file_path, logger.DEBUG) est = eec.set(self._link, cur_file_path) try: helpers.moveFile(cur_file_path, new_file_path) helpers.linkFile(cur_file_path, new_file_path) helpers.chmodAsParent(new_file_path) except (IOError, OSError), e: logger.log("Unable to link file "+cur_file_path+" to "+new_file_path+": "+ex(e), logger.ERROR) logger.log(str(e), logger.ERROR); eec.clock(est, False) raise e
def dumpHTML(data): dumpName = ek(os.path.join, sickbeard.CACHE_DIR, "custom_torrent.html") try: fileOut = io.open(dumpName, "wb") fileOut.write(data) fileOut.close() helpers.chmodAsParent(dumpName) except IOError as e: logger.log(u"Unable to save the file: %s " % repr(e), logger.ERROR) return False logger.log(u"Saved custom_torrent html dump %s " % dumpName, logger.INFO) return True
def dumpHTML(data): dumpName = ek(os.path.join, sickbeard.CACHE_DIR, 'custom_torrent.html') try: fileOut = io.open(dumpName, 'wb') fileOut.write(data) fileOut.close() helpers.chmodAsParent(dumpName) except IOError as error: logger.log('Unable to save the file: {0}'.format(ex(error)), logger.ERROR) return False logger.log('Saved custom_torrent html dump {0} '.format(dumpName), logger.INFO) return True
def execute(self): generic_queue.QueueItem.execute(self) ep_obj = self.ep_obj force = self.force random.shuffle(SUBTITLE_SERVICES) logger.log("Searching subtitles for " + ep_obj.prettyName()) if not ek.ek(os.path.isfile, ep_obj.location): logger.log("Can't download subtitles for " + ep_obj.prettyName() + ". Episode file doesn't exist.", logger.DEBUG) return epName = ep_obj.location.rpartition(".")[0] subLanguages = [] if sickbeard.SUBTITLE_LANGUAGES <> '': subLanguages = sickbeard.SUBTITLE_LANGUAGES.split(",") if len(subLanguages) < 1 and ep_obj.show.lang: subLanguages.append(ep_obj.show.lang) if len(subLanguages) < 1: logger.log("Can't download subtitles for " + ep_obj.prettyName() + ". Configure the language to search at post processing options.", logger.DEBUG) return #for lang in subLanguages: #langS = lang.split("-") #if len(langS) > 1: #subLanguages.append(langS[0]) try: sublimLangs = set() for sub in subLanguages: bLang = Language.fromietf(sub) sublimLangs.add(bLang) sublimEpisode = set() sublimEpisode.add(Episode.fromname(ep_obj.location)) downloadedSubs = subliminal.download_best_subtitles( videos=sublimEpisode, languages=sublimLangs, providers=SUBTITLE_SERVICES) subliminal.save_subtitles(downloadedSubs, directory = os.path.dirname(ep_obj.location)) subCount = 0 for video, video_subtitles in downloadedSubs.items(): for sub in video_subtitles: subPath = subliminal.subtitle.get_subtitle_path(ep_obj.location, sub.language) helpers.chmodAsParent(subPath) subCount += 1 except Exception, e: logger.log("Error while downloading subtitles for %s: %s" % (ep_obj.prettyName(), str(e)), logger.ERROR) traceback.print_exc() return False
def write_ep_file(self, ep_obj): """ Generates and writes ep_obj's metadata under the given path with the given filename root. Uses the episode's name with the extension in _ep_nfo_extension. ep_obj: TVEpisode object for which to create the metadata file_name_path: The file name to use for this metadata. Note that the extension will be automatically added based on _ep_nfo_extension. This should include an absolute path. Note that this method expects that _ep_data will return an ElementTree object. If your _ep_data returns data in another format you'll need to override this method. """ data = self._ep_data(ep_obj) if not data: return False nfo_file_path = self.get_episode_file_path(ep_obj) nfo_file_dir = ek(os.path.dirname, nfo_file_path) try: if not ek(os.path.isdir, nfo_file_dir): logger.log(u'Metadata directory did not exist, creating it at {path}'.format (path=nfo_file_dir), logger.DEBUG) ek(os.makedirs, nfo_file_dir) helpers.chmodAsParent(nfo_file_dir) logger.log(u'Writing episode nfo file to {path}'.format (path=nfo_file_path), logger.DEBUG) with io.open(nfo_file_path, 'wb') as nfo_file: # Calling encode directly, b/c often descriptions have wonky characters. data.write(nfo_file, encoding='utf-8', xml_declaration=True) helpers.chmodAsParent(nfo_file_path) except IOError as e: logger.log(u'Unable to write file to {path} - ' u'are you sure the folder is writable? {exception}'.format (path=nfo_file_path, exception=ex(e)), logger.ERROR) return False return True
def _download_result(result): """ Downloads a result to the appropriate black hole folder. Returns a bool representing success. result: SearchResult instance to download. """ res_provider = result.provider if None is res_provider: logger.log(u'Invalid provider name - this is a coding error, report it please', logger.ERROR) return False # nzbs with an URL can just be downloaded from the provider if 'nzb' == result.resultType: new_result = res_provider.download_result(result) # if it's an nzb data result elif 'nzbdata' == result.resultType: # get the final file path to the nzb file_name = ek.ek(os.path.join, sickbeard.NZB_DIR, u'%s.nzb' % result.name) logger.log(u'Saving NZB to %s' % file_name) new_result = True # save the data to disk try: data = result.get_data() if not data: new_result = False else: with ek.ek(open, file_name, 'w') as file_out: file_out.write(data) helpers.chmodAsParent(file_name) except EnvironmentError as e: logger.log(u'Error trying to save NZB to black hole: %s' % ex(e), logger.ERROR) new_result = False elif 'torrent' == res_provider.providerType: new_result = res_provider.download_result(result) else: logger.log(u'Invalid provider type - this is a coding error, report it please', logger.ERROR) new_result = False return new_result
def write_ep_file(self, ep_obj): """ Generates and writes ep_obj's metadata under the given path with the given filename root. Uses the episode's name with the extension in _ep_nfo_extension. ep_obj: TVEpisode object for which to create the metadata file_name_path: The file name to use for this metadata. Note that the extension will be automatically added based on _ep_nfo_extension. This should include an absolute path. Note that this method expects that _ep_data will return an ElementTree object. If your _ep_data returns data in another format you'll need to override this method. """ data = self._ep_data(ep_obj) if not data: return False nfo_file_path = self.get_episode_file_path(ep_obj) nfo_file_dir = ek.ek(os.path.dirname, nfo_file_path) try: if not ek.ek(os.path.isdir, nfo_file_dir): logger.log( u"Metadata dir didn't exist, creating it at " + nfo_file_dir, logger.DEBUG) ek.ek(os.makedirs, nfo_file_dir) helpers.chmodAsParent(nfo_file_dir) logger.log(u"Writing episode nfo file to " + nfo_file_path, logger.DEBUG) nfo_file = ek.ek(open, nfo_file_path, 'w') data.write(nfo_file, encoding="utf-8") nfo_file.close() helpers.chmodAsParent(nfo_file_path) except IOError, e: logger.log( u"Unable to write file to " + nfo_file_path + " - are you sure the folder is writable? " + ex(e), logger.ERROR) return False
def write_show_file(self, show_obj): """ Generates and writes show_obj's metadata under the given path to the filename given by get_show_file_path() show_obj: TVShow object for which to create the metadata path: An absolute or relative path where we should put the file. Note that the file name will be the default show_file_name. Note that this method expects that _show_data will return an ElementTree object. If your _show_data returns data in another format you'll need to override this method. """ data = self._show_data(show_obj) if not data: return False nfo_file_path = self.get_show_file_path(show_obj) nfo_file_dir = ek(os.path.dirname, nfo_file_path) try: if not ek(os.path.isdir, nfo_file_dir): logger.log(u'Metadata directory did not exist, creating it at {path}'.format (path=nfo_file_dir), logger.DEBUG) ek(os.makedirs, nfo_file_dir) helpers.chmodAsParent(nfo_file_dir) logger.log(u'Writing show nfo file to {path}'.format (path=nfo_file_path), logger.DEBUG) nfo_file = io.open(nfo_file_path, 'wb') data.write(nfo_file, encoding='utf-8', xml_declaration=True) nfo_file.close() helpers.chmodAsParent(nfo_file_path) except IOError as e: logger.log(u'Unable to write file to {path} - ' u'are you sure the folder is writable? {exception}'.format (path=nfo_file_path, exception=ex(e)), logger.ERROR) return False return True
def write_show_file(self, show_obj): """ Generates and writes show_obj's metadata under the given path to the filename given by get_show_file_path() show_obj: TVShow object for which to create the metadata path: An absolute or relative path where we should put the file. Note that the file name will be the default show_file_name. Note that this method expects that _show_data will return an ElementTree object. If your _show_data returns data in another format you'll need to override this method. """ data = self._show_data(show_obj) if not data: return False nfo_file_path = self.get_show_file_path(show_obj) nfo_file_dir = ek.ek(os.path.dirname, nfo_file_path) try: if not ek.ek(os.path.isdir, nfo_file_dir): logger.log( u"Metadata dir didn't exist, creating it at " + nfo_file_dir, logger.DEBUG) ek.ek(os.makedirs, nfo_file_dir) helpers.chmodAsParent(nfo_file_dir) logger.log(u"Writing show nfo file to " + nfo_file_path, logger.DEBUG) nfo_file = ek.ek(open, nfo_file_path, 'w') data.write(nfo_file, encoding="utf-8") nfo_file.close() helpers.chmodAsParent(nfo_file_path) except IOError, e: logger.log( u"Unable to write file to " + nfo_file_path + " - are you sure the folder is writable? " + ex(e), logger.ERROR) return False
def downloadResult(self, result): try: logger.log(u"Downloading a result from " + self.name + " at " + result.url) torrentFileName = ek.ek(os.path.join, sickbeard.TORRENT_DIR, helpers.sanitizeFileName(result.name) + '.' + self.providerType) #add self referer to get application/x-bittorrent from torcache.net data = self.getURL(result.url, [("Referer", result.url)]) if data == None: return False fileOut = open(torrentFileName, 'wb') logger.log(u"Saving to " + torrentFileName, logger.DEBUG) fileOut.write(data) fileOut.close() helpers.chmodAsParent(torrentFileName) return self._verify_download(torrentFileName) except Exception, e: logger.log("Unable to save the file: "+str(e).decode('utf-8'), logger.ERROR) return False
def _commitSTRM(strmName, strmContents): # get the final file path to the strm file destinationPath = ek.ek(os.path.join, sickbeard.TV_DOWNLOAD_DIR, strmName) helpers.makeDir(destinationPath) fileName = ek.ek(os.path.join, destinationPath, strmName + ".strm") logger.log(u"Saving STRM to " + fileName) # save the data to disk try: fileOut = open(fileName, "w") fileOut.write(strmContents) fileOut.close() helpers.chmodAsParent(fileName) return True except IOError, e: logger.log(u"Error trying to save STRM to TV downloader directory: "+ex(e), logger.ERROR) return False
def _downloadResult(result): """ Downloads a result to the appropriate black hole folder. Returns a bool representing success. result: SearchResult instance to download. """ resProvider = result.provider if resProvider == None: logger.log(u"Invalid provider name - this is a coding error, report it please", logger.ERROR) return False # nzbs with an URL can just be downloaded from the provider if result.resultType == "nzb": newResult = resProvider.download_result(result) # if it's an nzb data result elif result.resultType == "nzbdata": # get the final file path to the nzb fileName = ek.ek(os.path.join, sickbeard.NZB_DIR, result.name + ".nzb") logger.log(u"Saving NZB to " + fileName) newResult = True # save the data to disk try: with ek.ek(open, fileName, 'w') as fileOut: fileOut.write(result.extraInfo[0]) helpers.chmodAsParent(fileName) except EnvironmentError as e: logger.log(u"Error trying to save NZB to black hole: " + ex(e), logger.ERROR) newResult = False elif resProvider.providerType == "torrent": newResult = resProvider.download_result(result) else: logger.log(u"Invalid provider type - this is a coding error, report it please", logger.ERROR) newResult = False return newResult
elif result.resultType == "torrentdata": # get the final file path to the nzb fileName = ek.ek(os.path.join, sickbeard.TORRENT_DIR, result.name + ".torrent") logger.log(u"Saving Torrent to " + fileName) newResult = True # save the data to disk try: fileOut = open(fileName, "wb") fileOut.write(result.extraInfo[0]) fileOut.close() helpers.chmodAsParent(fileName) except IOError, e: logger.log(u"Error trying to save Torrent to black hole: " + ex(e), logger.ERROR) newResult = False elif resProvider.providerType == "torrent": newResult = resProvider.downloadResult(result) else: logger.log( u"Invalid provider type - this is a coding error, report it please", logger.ERROR) return False if newResult:
def process(self): """ Post-process a given file :return: True on success, False on failure """ self._log(u"Processing " + self.file_path + " (" + str(self.nzb_name) + ")") if ek(os.path.isdir, self.file_path): self._log(u"File %s seems to be a directory" % self.file_path) return False if not ek(os.path.exists, self.file_path): self._log(u"File %s doesn't exist, did unrar fail?" % self.file_path) return False for ignore_file in self.IGNORED_FILESTRINGS: if ignore_file in self.file_path: self._log(u"File %s is ignored type, skipping" % self.file_path) return False # reset per-file stuff self.in_history = False # reset the anidb episode object self.anidbEpisode = None # try to find the file info (show, season, episodes, quality, version) = self._find_info() if not show: self._log(u"This show isn't in your list, you need to add it to SR before post-processing an episode") raise EpisodePostProcessingFailedException() elif season == None or not episodes: self._log(u"Not enough information to determine what episode this is. Quitting post-processing") return False # retrieve/create the corresponding TVEpisode objects ep_obj = self._get_ep_obj(show, season, episodes) old_ep_status, old_ep_quality = common.Quality.splitCompositeStatus(ep_obj.status) # get the quality of the episode we're processing if quality and not common.Quality.qualityStrings[quality] == 'Unknown': self._log(u"Snatch history had a quality in it, using that: " + common.Quality.qualityStrings[quality], logger.DEBUG) new_ep_quality = quality else: new_ep_quality = self._get_quality(ep_obj) logger.log(u"Quality of the episode we're processing: %s" % new_ep_quality, logger.DEBUG) # see if this is a priority download (is it snatched, in history, PROPER, or BEST) priority_download = self._is_priority(ep_obj, new_ep_quality) self._log(u"Is ep a priority download: " + str(priority_download), logger.DEBUG) # get the version of the episode we're processing if version: self._log(u"Snatch history had a version in it, using that: v" + str(version), logger.DEBUG) new_ep_version = version else: new_ep_version = -1 # check for an existing file existing_file_status = self._checkForExistingFile(ep_obj.location) # if it's not priority then we don't want to replace smaller files in case it was a mistake if not priority_download: # Not a priority and the quality is lower than what we already have if (new_ep_quality < old_ep_quality and old_ep_quality != common.Quality.UNKNOWN) and not existing_file_status == PostProcessor.DOESNT_EXIST: self._log(u"File exists and new file quality is lower than existing, marking it unsafe to replace") return False # if there's an existing file that we don't want to replace stop here if existing_file_status == PostProcessor.EXISTS_LARGER: if self.is_proper: self._log( u"File exists and new file is smaller, new file is a proper/repack, marking it safe to replace") return True else: self._log(u"File exists and new file is smaller, marking it unsafe to replace") return False elif existing_file_status == PostProcessor.EXISTS_SAME: self._log(u"File exists and new file is same size, marking it unsafe to replace") return False # if the file is priority then we're going to replace it even if it exists else: self._log( u"This download is marked a priority download so I'm going to replace an existing file if I find one") # try to find out if we have enough space to perform the copy or move action. if not helpers.isFileLocked(self.file_path, False): if not verify_freespace(self.file_path, ep_obj.show._location, [ep_obj] + ep_obj.relatedEps): self._log("Not enough space to continue PP, exiting", logger.WARNING) return False else: self._log("Unable to determine needed filespace as the source file is locked for access") # delete the existing file (and company) for cur_ep in [ep_obj] + ep_obj.relatedEps: try: self._delete(cur_ep.location, associated_files=True) # clean up any left over folders if cur_ep.location: helpers.delete_empty_folders(ek(os.path.dirname, cur_ep.location), keep_dir=ep_obj.show._location) except (OSError, IOError): raise EpisodePostProcessingFailedException("Unable to delete the existing files") # set the status of the episodes # for curEp in [ep_obj] + ep_obj.relatedEps: # curEp.status = common.Quality.compositeStatus(common.SNATCHED, new_ep_quality) # if the show directory doesn't exist then make it if allowed if not ek(os.path.isdir, ep_obj.show._location) and sickbeard.CREATE_MISSING_SHOW_DIRS: self._log(u"Show directory doesn't exist, creating it", logger.DEBUG) try: ek(os.mkdir, ep_obj.show._location) helpers.chmodAsParent(ep_obj.show._location) # do the library update for synoindex notifiers.synoindex_notifier.addFolder(ep_obj.show._location) except (OSError, IOError): raise EpisodePostProcessingFailedException("Unable to create the show directory: " + ep_obj.show._location) # get metadata for the show (but not episode because it hasn't been fully processed) ep_obj.show.writeMetadata(True) # update the ep info before we rename so the quality & release name go into the name properly sql_l = [] for cur_ep in [ep_obj] + ep_obj.relatedEps: with cur_ep.lock: if self.release_name: self._log("Found release name " + self.release_name, logger.DEBUG) cur_ep.release_name = self.release_name else: cur_ep.release_name = "" if ep_obj.status in common.Quality.SNATCHED_BEST: cur_ep.status = common.Quality.compositeStatus(common.ARCHIVED, new_ep_quality) else: cur_ep.status = common.Quality.compositeStatus(common.DOWNLOADED, new_ep_quality) cur_ep.subtitles = u'' cur_ep.subtitles_searchcount = 0 cur_ep.subtitles_lastsearch = '0001-01-01 00:00:00' cur_ep.is_proper = self.is_proper cur_ep.version = new_ep_version if self.release_group: cur_ep.release_group = self.release_group else: cur_ep.release_group = "" sql_l.append(cur_ep.get_sql()) # Just want to keep this consistent for failed handling right now releaseName = show_name_helpers.determineReleaseName(self.folder_path, self.nzb_name) if releaseName is not None: failed_history.logSuccess(releaseName) else: self._log(u"Couldn't find release in snatch history", logger.WARNING) # find the destination folder try: proper_path = ep_obj.proper_path() proper_absolute_path = ek(os.path.join, ep_obj.show.location, proper_path) dest_path = ek(os.path.dirname, proper_absolute_path) except ShowDirectoryNotFoundException: raise EpisodePostProcessingFailedException( u"Unable to post-process an episode if the show dir doesn't exist, quitting") self._log(u"Destination folder for this episode: " + dest_path, logger.DEBUG) # create any folders we need helpers.make_dirs(dest_path) # figure out the base name of the resulting episode file if sickbeard.RENAME_EPISODES: orig_extension = self.file_name.rpartition('.')[-1] new_base_name = ek(os.path.basename, proper_path) new_file_name = new_base_name + '.' + orig_extension else: # if we're not renaming then there's no new base name, we'll just use the existing name new_base_name = None new_file_name = self.file_name # add to anidb if ep_obj.show.is_anime and sickbeard.ANIDB_USE_MYLIST: self._add_to_anidb_mylist(self.file_path) try: # move the episode and associated files to the show dir if self.process_method == "copy": if helpers.isFileLocked(self.file_path, False): raise EpisodePostProcessingFailedException("File is locked for reading") self._copy(self.file_path, dest_path, new_base_name, sickbeard.MOVE_ASSOCIATED_FILES, sickbeard.USE_SUBTITLES and ep_obj.show.subtitles) elif self.process_method == "move": if helpers.isFileLocked(self.file_path, True): raise EpisodePostProcessingFailedException("File is locked for reading/writing") self._move(self.file_path, dest_path, new_base_name, sickbeard.MOVE_ASSOCIATED_FILES, sickbeard.USE_SUBTITLES and ep_obj.show.subtitles) elif self.process_method == "hardlink": self._hardlink(self.file_path, dest_path, new_base_name, sickbeard.MOVE_ASSOCIATED_FILES, sickbeard.USE_SUBTITLES and ep_obj.show.subtitles) elif self.process_method == "symlink": if helpers.isFileLocked(self.file_path, True): raise EpisodePostProcessingFailedException("File is locked for reading/writing") self._moveAndSymlink(self.file_path, dest_path, new_base_name, sickbeard.MOVE_ASSOCIATED_FILES, sickbeard.USE_SUBTITLES and ep_obj.show.subtitles) else: logger.log(u"Unknown process method: " + str(self.process_method), logger.ERROR) raise EpisodePostProcessingFailedException("Unable to move the files to their new home") except (OSError, IOError): raise EpisodePostProcessingFailedException("Unable to move the files to their new home") # download subtitles if sickbeard.USE_SUBTITLES and ep_obj.show.subtitles: for cur_ep in [ep_obj] + ep_obj.relatedEps: with cur_ep.lock: cur_ep.location = ek(os.path.join, dest_path, new_file_name) cur_ep.refreshSubtitles() cur_ep.downloadSubtitles(force=True) # now that processing has finished, we can put the info in the DB. If we do it earlier, then when processing fails, it won't try again. if len(sql_l) > 0: myDB = db.DBConnection() myDB.mass_action(sql_l) # put the new location in the database sql_l = [] for cur_ep in [ep_obj] + ep_obj.relatedEps: with cur_ep.lock: cur_ep.location = ek(os.path.join, dest_path, new_file_name) sql_l.append(cur_ep.get_sql()) if len(sql_l) > 0: myDB = db.DBConnection() myDB.mass_action(sql_l) # set file modify stamp to show airdate if sickbeard.AIRDATE_EPISODES: for cur_ep in [ep_obj] + ep_obj.relatedEps: with cur_ep.lock: cur_ep.airdateModifyStamp() # generate nfo/tbn ep_obj.createMetaFiles() # log it to history history.logDownload(ep_obj, self.file_path, new_ep_quality, self.release_group, new_ep_version) #If any notification fails, don't stop postProcessor try: # send notifications notifiers.notify_download(ep_obj._format_pattern('%SN - %Sx%0E - %EN - %QN')) # do the library update for KODI notifiers.kodi_notifier.update_library(ep_obj.show.name) # do the library update for Plex notifiers.plex_notifier.update_library(ep_obj) # do the library update for EMBY notifiers.emby_notifier.update_library(ep_obj.show) # do the library update for NMJ # nmj_notifier kicks off its library update when the notify_download is issued (inside notifiers) # do the library update for Synology Indexer notifiers.synoindex_notifier.addFile(ep_obj.location) # do the library update for pyTivo notifiers.pytivo_notifier.update_library(ep_obj) # do the library update for Trakt notifiers.trakt_notifier.update_library(ep_obj) except: logger.log(u"Some notifications could not be sent. Continuing with postProcessing...") self._run_extra_scripts(ep_obj) return True
def run(self): # TODO: Put that in the __init__ before starting the thread? if not sickbeard.USE_SUBTITLES: logger.log(u'Subtitles support disabled', logger.DEBUG) return if len(sickbeard.subtitles.getEnabledServiceList()) < 1: logger.log( u'Not enough services selected. At least 1 service is required to search subtitles in the background', logger.ERROR) return logger.log(u'Checking for subtitles', logger.MESSAGE) # get episodes on which we want subtitles # criteria is: # - show subtitles = 1 # - episode subtitles != config wanted languages or SINGLE (depends on config multi) # - search count < 2 and diff(airdate, now) > 1 week : now -> 1d # - search count < 7 and diff(airdate, now) <= 1 week : now -> 4h -> 8h -> 16h -> 1d -> 1d -> 1d myDB = db.DBConnection() today = datetime.date.today().toordinal() # you have 5 minutes to understand that one. Good luck sqlResults = myDB.select( 'SELECT s.show_name, e.showid, e.season, e.episode, e.status, e.subtitles, e.subtitles_searchcount AS searchcount, e.subtitles_lastsearch AS lastsearch, e.location, (? - e.airdate) AS airdate_daydiff FROM tv_episodes AS e INNER JOIN tv_shows AS s ON (e.showid = s.tvdb_id) WHERE s.subtitles = 1 AND e.subtitles NOT LIKE (?) AND ((e.subtitles_searchcount <= 2 AND (? - e.airdate) > 7) OR (e.subtitles_searchcount <= 7 AND (? - e.airdate) <= 7)) AND (e.status IN (' + ','.join([str(x) for x in Quality.DOWNLOADED + [ARCHIVED]]) + ') OR (e.status IN (' + ','.join( [str(x) for x in Quality.SNATCHED + Quality.SNATCHED_PROPER]) + ') AND e.location != ""))', [today, wantedLanguages(True), today, today]) if len(sqlResults) == 0: logger.log('No subtitles to download', logger.MESSAGE) return rules = self._getRules() now = datetime.datetime.now() for epToSub in sqlResults: if not ek.ek(os.path.isfile, epToSub['location']): logger.log( 'Episode file does not exist, cannot download subtitles for episode %dx%d of show %s' % (epToSub['season'], epToSub['episode'], epToSub['show_name']), logger.DEBUG) continue # Old shows rule if ((epToSub['airdate_daydiff'] > 7 and epToSub['searchcount'] < 2 and now - datetime.datetime.strptime(epToSub['lastsearch'], '%Y-%m-%d %H:%M:%S') > datetime.timedelta(hours=rules['old'][epToSub['searchcount']]) ) or # Recent shows rule (epToSub['airdate_daydiff'] <= 7 and epToSub['searchcount'] < 7 and now - datetime.datetime.strptime(epToSub['lastsearch'], '%Y-%m-%d %H:%M:%S') > datetime.timedelta(hours=rules['new'][epToSub['searchcount']]) )): logger.log( 'Downloading subtitles for episode %dx%d of show %s' % (epToSub['season'], epToSub['episode'], epToSub['show_name']), logger.DEBUG) showObj = helpers.findCertainShow(sickbeard.showList, int(epToSub['showid'])) if not showObj: logger.log(u'Show not found', logger.DEBUG) return epObj = showObj.getEpisode(int(epToSub["season"]), int(epToSub["episode"])) if isinstance(epObj, str): logger.log(u'Episode not found', logger.DEBUG) return previous_subtitles = epObj.subtitles try: subtitles = epObj.downloadSubtitles() if sickbeard.SUBTITLES_DIR: for video in subtitles: 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: logger.log( u"Unable to create subtitles folder " + subs_new_path, logger.ERROR) else: helpers.chmodAsParent(subs_new_path) for subtitle in subtitles.get(video): 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: for video in subtitles: for subtitle in subtitles.get(video): helpers.chmodAsParent(subtitle.path) except: logger.log(u'Unable to find subtitles', logger.DEBUG) return
class SubtitleQueueItem(generic_queue.QueueItem): def __init__(self, ep_obj, force): generic_queue.QueueItem.__init__(self, 'Search', SUBTITLE_SEARCH) self.priority = generic_queue.QueuePriorities.NORMAL self.ep_obj = ep_obj self.force = force self.success = None def execute(self): generic_queue.QueueItem.execute(self) ep_obj = self.ep_obj force = self.force random.shuffle(SUBTITLE_SERVICES) logger.log("Searching subtitles for " + ep_obj.prettyName()) if not ek.ek(os.path.isfile, ep_obj.location): logger.log( "Can't download subtitles for " + ep_obj.prettyName() + ". Episode file doesn't exist.", logger.DEBUG) return epName = ep_obj.location.rpartition(".")[0] subLanguages = sickbeard.SUBTITLE_LANGUAGES.split(",") if len(subLanguages) < 1 and ep_obj.show.lang: subLanguages.append(ep_obj.show.lang) if len(subLanguages) < 1: logger.log( "Can't download subtitles for " + ep_obj.prettyName() + ". Configure the language to search at post processing options.", logger.DEBUG) return #for lang in subLanguages: #langS = lang.split("-") #if len(langS) > 1: #subLanguages.append(langS[0]) try: subEpisodes = subliminal.download_subtitles( [ep_obj.location], languages=subLanguages, services=SUBTITLE_SERVICES, force=force, multi=True, cache_dir=sickbeard.CACHE_DIR, max_depth=3, scan_filter=None, order=None) except Exception, e: logger.log( "Error while downloading subtitles for %s: %s" % (ep_obj.prettyName(), str(e)), logger.ERROR) traceback.print_exc() return False subCount = 0 for subEpisode in subEpisodes: subtitles = subEpisodes[subEpisode] for subtitle in subtitles: helpers.chmodAsParent(subtitle.path) subCount += 1 if subCount > 0: for subEpisode in subEpisodes: subtitles = subEpisodes[subEpisode] for item in subtitles: logger.log( "Downloaded subtitle for %s: %s from %s" % (ep_obj.prettyName(), item.language, item.service)) self.success = True else: logger.log("No subtitles downloaded for " + ep_obj.prettyName()) self.success = False
try: dest_path = self._find_ep_destination_folder(ep_obj) except exceptions.ShowDirNotFoundException: raise exceptions.PostProcessingFailed( u"Unable to post-process an episode if the show dir doesn't exist, quitting" ) self._log(u"Destination folder for this episode: " + dest_path, logger.DEBUG) # if the dir doesn't exist (new season folder) then make it if not ek.ek(os.path.isdir, dest_path): self._log(u"Season folder didn't exist, creating it", logger.DEBUG) try: ek.ek(os.mkdir, dest_path) helpers.chmodAsParent(dest_path) # do the library update for synoindex notifiers.synoindex_notifier.addFolder(dest_path) except OSError, IOError: raise exceptions.PostProcessingFailed( "Unable to create the episode's destination folder: " + dest_path) # update the statuses before we rename so the quality goes into the name properly for cur_ep in [ep_obj] + ep_obj.relatedEps: with cur_ep.lock: cur_ep.status = common.Quality.compositeStatus( common.DOWNLOADED, new_ep_quality) cur_ep.saveToDB() # figure out the base name of the resulting episode file
def addNewShow(self, whichSeries=None, indexerLang=None, rootDir=None, defaultStatus=None, quality_preset=None, anyQualities=None, bestQualities=None, season_folders=None, subtitles=None, subtitles_sr_metadata=None, fullShowPath=None, other_shows=None, skipShow=None, providedIndexer=None, anime=None, scene=None, blacklist=None, whitelist=None, defaultStatusAfter=None): """ Receive tvdb id, dir, and other options and create a show from them. If extra show dirs are provided then it forwards back to newShow, if not it goes to /home. """ if not indexerLang: indexerLang = sickbeard.INDEXER_DEFAULT_LANGUAGE # grab our list of other dirs if given if not other_shows: other_shows = [] elif not isinstance(other_shows, list): other_shows = [other_shows] def finishAddShow(): # if there are no extra shows then go home if not other_shows: return self.redirect('/home/') # peel off the next one next_show_dir = other_shows[0] rest_of_show_dirs = other_shows[1:] # go to add the next show return self.newShow(next_show_dir, rest_of_show_dirs) # if we're skipping then behave accordingly if skipShow: return finishAddShow() # sanity check on our inputs if (not rootDir and not fullShowPath) or not whichSeries: return _( "Missing params, no Indexer ID or folder: {show_to_add} and {root_dir}/{show_path}" ).format(show_to_add=whichSeries, root_dir=rootDir, show_path=fullShowPath) # figure out what show we're adding and where series_pieces = whichSeries.split('|') if (whichSeries and rootDir) or (whichSeries and fullShowPath and len(series_pieces) > 1): if len(series_pieces) < 6: logger.log( "Unable to add show due to show selection. Not enough arguments: {0}" .format((repr(series_pieces))), logger.ERROR) ui.notifications.error( _("Unknown error. Unable to add show due to problem with show selection." )) return self.redirect('/addShows/existingShows/') indexer = int(series_pieces[1]) indexer_id = int(series_pieces[3]) # Show name was sent in UTF-8 in the form show_name = xhtml_unescape(series_pieces[4]).decode('utf-8') else: # if no indexer was provided use the default indexer set in General settings if not providedIndexer: providedIndexer = sickbeard.INDEXER_DEFAULT indexer = int(providedIndexer) indexer_id = int(whichSeries) show_name = ek(os.path.basename, ek(os.path.normpath, xhtml_unescape(fullShowPath))) # use the whole path if it's given, or else append the show name to the root dir to get the full show path if fullShowPath: show_dir = ek(os.path.normpath, xhtml_unescape(fullShowPath)) extra_check_dir = show_dir else: folder_name = show_name s = sickchill.indexer.series_by_id(indexerid=indexer_id, indexer=indexer, language=indexerLang) if sickbeard.ADD_SHOWS_WITH_YEAR and s.firstAired: try: year = '({0})'.format( dateutil.parser.parse(s.firstAired).year) if year not in folder_name: folder_name = '{0} {1}'.format(s.seriesName, year) except (TypeError, ValueError): logger.log( _('Could not append the show year folder for the show: {0}' ).format(folder_name)) show_dir = ek(os.path.join, rootDir, sanitize_filename(xhtml_unescape(folder_name))) extra_check_dir = ek(os.path.join, rootDir, sanitize_filename(xhtml_unescape(show_name))) # blanket policy - if the dir exists you should have used "add existing show" numbnuts if (ek(os.path.isdir, show_dir) or ek(os.path.isdir, extra_check_dir)) and not fullShowPath: ui.notifications.error( _("Unable to add show"), _("Folder {show_dir} exists already").format( show_dir=show_dir)) return self.redirect('/addShows/existingShows/') # don't create show dir if config says not to if sickbeard.ADD_SHOWS_WO_DIR: logger.log("Skipping initial creation of " + show_dir + " due to config.ini setting") else: dir_exists = helpers.makeDir(show_dir) if not dir_exists: logger.log( "Unable to create the folder " + show_dir + ", can't add the show", logger.ERROR) ui.notifications.error( _("Unable to add show"), _("Unable to create the folder {show_dir}, can't add the show" ).format(show_dir=show_dir)) # Don't redirect to default page because user wants to see the new show return self.redirect("/home/") else: helpers.chmodAsParent(show_dir) # prepare the inputs for passing along scene = config.checkbox_to_value(scene) anime = config.checkbox_to_value(anime) season_folders = config.checkbox_to_value(season_folders) subtitles = config.checkbox_to_value(subtitles) subtitles_sr_metadata = config.checkbox_to_value(subtitles_sr_metadata) if whitelist: whitelist = short_group_names(whitelist) if blacklist: blacklist = short_group_names(blacklist) if not anyQualities: anyQualities = [] if not bestQualities or try_int(quality_preset, None): bestQualities = [] if not isinstance(anyQualities, list): anyQualities = [anyQualities] if not isinstance(bestQualities, list): bestQualities = [bestQualities] newQuality = Quality.combineQualities([int(q) for q in anyQualities], [int(q) for q in bestQualities]) # add the show sickbeard.showQueueScheduler.action.add_show( indexer, indexer_id, showDir=show_dir, default_status=int(defaultStatus), quality=newQuality, season_folders=season_folders, lang=indexerLang, subtitles=subtitles, subtitles_sr_metadata=subtitles_sr_metadata, anime=anime, scene=scene, paused=None, blacklist=blacklist, whitelist=whitelist, default_status_after=int(defaultStatusAfter), root_dir=rootDir) ui.notifications.message( _('Show added'), _('Adding the specified show into {show_dir}').format( show_dir=show_dir)) return finishAddShow()
def run(self): ShowQueueItem.run(self) logger.log(u"Starting to add show {0}".format( "by ShowDir: {0}".format(self.showDir) if self. showDir else "by Indexer Id: {0}".format(self.indexer_id))) # make sure the Indexer IDs are valid try: lINDEXER_API_PARMS = sickbeard.indexerApi( self.indexer).api_params.copy() if self.lang: lINDEXER_API_PARMS['language'] = self.lang logger.log(u"" + str(sickbeard.indexerApi(self.indexer).name) + ": " + repr(lINDEXER_API_PARMS)) t = sickbeard.indexerApi( self.indexer).indexer(**lINDEXER_API_PARMS) s = t[self.indexer_id] # Let's try to create the show Dir if it's not provided. This way we force the show dir to build build using the # Indexers provided series name if not self.showDir and self.root_dir: show_name = get_showname_from_indexer(self.indexer, self.indexer_id, self.lang) if show_name: self.showDir = ek(os.path.join, self.root_dir, sanitize_filename(show_name)) dir_exists = makeDir(self.showDir) if not dir_exists: logger.log( u"Unable to create the folder {0}, can't add the show" .format(self.showDir)) return chmodAsParent(self.showDir) else: logger.log( u"Unable to get a show {0}, can't add the show".format( self.showDir)) return # this usually only happens if they have an NFO in their show dir which gave us a Indexer ID that has no proper english version of the show if getattr(s, 'seriesname', None) is None: logger.log( u"Show in {} has no name on {}, probably searched with the wrong language." .format(self.showDir, sickbeard.indexerApi(self.indexer).name), logger.ERROR) ui.notifications.error( "Unable to add show", "Show in " + self.showDir + " has no name on " + str(sickbeard.indexerApi(self.indexer).name) + ", probably the wrong language. Delete .nfo and add manually in the correct language." ) self._finishEarly() return # if the show has no episodes/seasons if not s: logger.log(u"Show " + str(s['seriesname']) + " is on " + str(sickbeard.indexerApi(self.indexer).name) + " but contains no season/episode data.") ui.notifications.error( "Unable to add show", "Show " + str(s['seriesname']) + " is on " + str(sickbeard.indexerApi(self.indexer).name) + " but contains no season/episode data.") self._finishEarly() return except Exception as e: logger.log( u"%s Error while loading information from indexer %s. Error: %r" % (self.indexer_id, sickbeard.indexerApi( self.indexer).name, ex(e)), logger.ERROR) # logger.log(u"Show name with ID %s doesn't exist on %s anymore. If you are using trakt, it will be removed from your TRAKT watchlist. If you are adding manually, try removing the nfo and adding again" % # (self.indexer_id, sickbeard.indexerApi(self.indexer).name), logger.WARNING) ui.notifications.error( "Unable to add show", "Unable to look up the show in %s on %s using ID %s, not using the NFO. Delete .nfo and try adding manually again." % (self.showDir, sickbeard.indexerApi( self.indexer).name, self.indexer_id)) if sickbeard.USE_TRAKT: trakt_id = sickbeard.indexerApi( self.indexer).config['trakt_id'] trakt_api = TraktAPI(sickbeard.SSL_VERIFY, sickbeard.TRAKT_TIMEOUT) title = self.showDir.split("/")[-1] data = {'shows': [{'title': title, 'ids': {}}]} if trakt_id == 'tvdb_id': data['shows'][0]['ids']['tvdb'] = self.indexer_id else: data['shows'][0]['ids']['tvrage'] = self.indexer_id trakt_api.traktRequest("sync/watchlist/remove", data, method='POST') self._finishEarly() return try: newShow = TVShow(self.indexer, self.indexer_id, self.lang) newShow.loadFromIndexer() self.show = newShow # set up initial values self.show.location = self.showDir self.show.subtitles = self.subtitles if self.subtitles is not None else sickbeard.SUBTITLES_DEFAULT self.show.quality = self.quality if self.quality else sickbeard.QUALITY_DEFAULT self.show.flatten_folders = self.flatten_folders if self.flatten_folders is not None else sickbeard.FLATTEN_FOLDERS_DEFAULT self.show.anime = self.anime if self.anime is not None else sickbeard.ANIME_DEFAULT self.show.scene = self.scene if self.scene is not None else sickbeard.SCENE_DEFAULT self.show.paused = self.paused if self.paused is not None else False # set up default new/missing episode status logger.log( u"Setting all episodes to the specified default status: " + str(self.show.default_ep_status)) self.show.default_ep_status = self.default_status if self.show.anime: self.show.release_groups = BlackAndWhiteList( self.show.indexerid) if self.blacklist: self.show.release_groups.set_black_keywords(self.blacklist) if self.whitelist: self.show.release_groups.set_white_keywords(self.whitelist) # # be smartish about this # if self.show.genre and "talk show" in self.show.genre.lower(): # self.show.air_by_date = 1 # if self.show.genre and "documentary" in self.show.genre.lower(): # self.show.air_by_date = 0 # if self.show.classification and "sports" in self.show.classification.lower(): # self.show.sports = 1 except sickbeard.indexer_exception as e: logger.log( u"Unable to add show due to an error with " + sickbeard.indexerApi(self.indexer).name + ": " + ex(e), logger.ERROR) if self.show: ui.notifications.error( "Unable to add " + str(self.show.name) + " due to an error with " + sickbeard.indexerApi(self.indexer).name + "") else: ui.notifications.error( "Unable to add show due to an error with " + sickbeard.indexerApi(self.indexer).name + "") self._finishEarly() return except MultipleShowObjectsException: logger.log( u"The show in " + self.showDir + " is already in your show list, skipping", logger.WARNING) ui.notifications.error( 'Show skipped', "The show in " + self.showDir + " is already in your show list") self._finishEarly() return except Exception as e: logger.log(u"Error trying to add show: " + ex(e), logger.ERROR) logger.log(traceback.format_exc(), logger.DEBUG) self._finishEarly() raise logger.log(u"Retrieving show info from IMDb", logger.DEBUG) try: self.show.loadIMDbInfo() except imdb_exceptions.IMDbError as e: logger.log(u" Something wrong on IMDb api: " + ex(e), logger.WARNING) except Exception as e: logger.log(u"Error loading IMDb info: " + ex(e), logger.ERROR) try: self.show.saveToDB() except Exception as e: logger.log(u"Error saving the show to the database: " + ex(e), logger.ERROR) logger.log(traceback.format_exc(), logger.DEBUG) self._finishEarly() raise # add it to the show list sickbeard.showList.append(self.show) try: self.show.loadEpisodesFromIndexer() except Exception as e: logger.log( u"Error with " + sickbeard.indexerApi(self.show.indexer).name + ", not creating episode list: " + ex(e), logger.ERROR) logger.log(traceback.format_exc(), logger.DEBUG) # update internal name cache name_cache.buildNameCache(self.show) try: self.show.loadEpisodesFromDir() except Exception as e: logger.log(u"Error searching dir for episodes: " + ex(e), logger.ERROR) logger.log(traceback.format_exc(), logger.DEBUG) # if they set default ep status to WANTED then run the backlog to search for episodes # FIXME: This needs to be a backlog queue item!!! if self.show.default_ep_status == WANTED: logger.log( u"Launching backlog for this show since its episodes are WANTED" ) sickbeard.backlogSearchScheduler.action.searchBacklog([self.show]) self.show.writeMetadata() self.show.updateMetadata() self.show.populateCache() self.show.flushEpisodes() if sickbeard.USE_TRAKT: # if there are specific episodes that need to be added by trakt sickbeard.traktCheckerScheduler.action.manage_new_show(self.show) # add show to trakt.tv library if sickbeard.TRAKT_SYNC: sickbeard.traktCheckerScheduler.action.add_show_watchlist( self.show) if sickbeard.TRAKT_SYNC_WATCHLIST: logger.log(u"update watchlist") notifiers.trakt_notifier.update_watchlist(show_obj=self.show) # Load XEM data to DB for show sickbeard.scene_numbering.xem_refresh(self.show.indexerid, self.show.indexer, force=True) # check if show has XEM mapping so we can determin if searches should go by scene numbering or indexer numbering. if not self.scene and sickbeard.scene_numbering.get_xem_numbering_for_show( self.show.indexerid, self.show.indexer): self.show.scene = 1 # After initial add, set to default_status_after. self.show.default_ep_status = self.default_status_after self.finish()
def downloadResult(self, result): """ Overridden to handle magnet links (using multiple fallbacks), and now libtorrent downloads also. """ logger.log(u"Downloading a result from " + self.name + " at " + result.url) if sickbeard.USE_LIBTORRENT: # libtorrent can download torrent files from urls, but it's probably safer for us # to do it first so that we can report errors immediately. if result.url and (result.url.startswith('http://') or result.url.startswith('https://')): torrent = self.getURL(result.url) # and now that we have it, we can check the torrent file too! if not self.is_valid_torrent_data(torrent): logger.log(u'The torrent retrieved from "%s" is not a valid torrent file.' % (result.url), logger.ERROR) self.blacklistUrl(result.url) return False else: torrent = result.url if torrent: return downloader.download_from_torrent(torrent=torrent, filename=result.name, episodes=result.episodes, originalTorrentUrl=result.url, blacklistOrigUrlOnFailure=True) else: logger.log(u'Failed to retrieve torrent from "%s"' % (result.url), logger.ERROR) return False else: # Ye olde way, using blackhole ... if result.url and result.url.startswith('magnet:'): torrent_hash = self.getHashFromMagnet(result.url) if torrent_hash: urls = [url_fmt % torrent_hash for url_fmt in MAGNET_TO_TORRENT_URLS] else: logger.log(u"Failed to handle magnet url %s, skipping..." % torrent_hash, logger.DEBUG) self.blacklistUrl(result.url) return False else: urls = [result.url] # use the result name as the filename fileName = ek.ek(os.path.join, sickbeard.TORRENT_DIR, helpers.sanitizeFileName(result.name) + '.' + self.providerType) for url in urls: logger.log(u"Trying d/l url: " + url, logger.DEBUG) data = self.getURL(url) if data == None: logger.log(u"Got no data for " + url, logger.DEBUG) # fall through to next iteration elif not self.is_valid_torrent_data(data): logger.log(u"d/l url %s failed, not a valid torrent file" % (url), logger.MESSAGE) self.blacklistUrl(url) else: try: fileOut = open(fileName, 'wb') fileOut.write(data) fileOut.close() helpers.chmodAsParent(fileName) except IOError, e: logger.log("Unable to save the file: "+ex(e), logger.ERROR) return False logger.log(u"Success with url: " + url, logger.DEBUG) return True else:
def _combined_file_operation(self, file_path, new_path, new_base_name, associated_files=False, action=None, subtitles=False): """ Performs a generic operation (move or copy) on a file. Can rename the file as well as change its location, and optionally move associated files too. file_path: The full path of the media file to act on new_path: Destination path where we want to move/copy the file to new_base_name: The base filename (no extension) to use during the copy. Use None to keep the same name. associated_files: Boolean, whether we should copy similarly-named files too action: function that takes an old path and new path and does an operation with them (move/copy) """ if not action: self._log( u"Must provide an action for the combined file operation", logger.ERROR) return file_list = [file_path] if associated_files: file_list = file_list + self._list_associated_files(file_path) elif subtitles: file_list = file_list + self._list_associated_files( file_path, True) if not file_list: self._log( u"There were no files associated with " + file_path + ", not moving anything", logger.DEBUG) return # create base name with file_path (media_file without .extension) old_base_name = file_path.rpartition('.')[0] old_base_name_length = len(old_base_name) # deal with all files for cur_file_path in file_list: cur_file_name = ek.ek(os.path.basename, cur_file_path) # get the extension without . cur_extension = cur_file_path[old_base_name_length + 1:] # check if file have subtitles language if os.path.splitext( cur_extension)[1][1:] in common.subtitleExtensions: cur_lang = os.path.splitext(cur_extension)[0] if cur_lang in sickbeard.SUBTITLES_LANGUAGES: cur_extension = cur_lang + os.path.splitext( cur_extension)[1] # replace .nfo with .nfo-orig to avoid conflicts if cur_extension == 'nfo': cur_extension = 'nfo-orig' # If new base name then convert name if new_base_name: new_file_name = new_base_name + '.' + cur_extension # if we're not renaming we still want to change extensions sometimes else: new_file_name = helpers.replaceExtension( cur_file_name, cur_extension) if sickbeard.SUBTITLES_DIR and cur_extension in common.subtitleExtensions: subs_new_path = ek.ek(os.path.join, new_path, sickbeard.SUBTITLES_DIR) dir_exists = helpers.makeDir(subs_new_path) if not dir_exists: logger.log( u"Unable to create subtitles folder " + subs_new_path, logger.ERROR) else: helpers.chmodAsParent(subs_new_path) new_file_path = ek.ek(os.path.join, subs_new_path, new_file_name) else: new_file_path = ek.ek(os.path.join, new_path, new_file_name) action(cur_file_path, new_file_path)
def run(self): # pylint: disable=too-many-branches, too-many-statements, too-many-return-statements super(QueueItemAdd, self).run() if self.showDir: try: assert isinstance(self.showDir, six.text_type) except AssertionError: logger.log(traceback.format_exc(), logger.WARNING) self._finish_early() return logger.log('Starting to add show {0}'.format( 'by ShowDir: {0}'.format(self.showDir) if self. showDir else 'by Indexer Id: {0}'.format(self.indexer_id))) # make sure the Indexer IDs are valid try: lINDEXER_API_PARMS = sickbeard.indexerApi( self.indexer).api_params.copy() lINDEXER_API_PARMS[ 'language'] = self.lang or sickbeard.INDEXER_DEFAULT_LANGUAGE logger.log('{0}: {1!r}'.format( sickbeard.indexerApi(self.indexer).name, lINDEXER_API_PARMS)) t = sickbeard.indexerApi( self.indexer).indexer(**lINDEXER_API_PARMS) s = t[self.indexer_id] # Let's try to create the show Dir if it's not provided. This way we force the show dir to build build using the # Indexers provided series name if self.root_dir and not self.showDir: show_name = get_showname_from_indexer(self.indexer, self.indexer_id, self.lang) if not show_name: logger.log( 'Unable to get a show {0}, can\'t add the show'.format( self.showDir)) self._finish_early() return self.showDir = ek(os.path.join, self.root_dir, sanitize_filename(show_name)) dir_exists = makeDir(self.showDir) if not dir_exists: logger.log( 'Unable to create the folder {0}, can\'t add the show'. format(self.showDir)) self._finish_early() return chmodAsParent(self.showDir) # this usually only happens if they have an NFO in their show dir which gave us a Indexer ID that has no proper english version of the show if getattr(s, 'seriesname', None) is None: # noinspection PyPep8 error_string = 'Show in {0} has no name on {1}, probably searched with the wrong language. Delete .nfo and add manually in the correct language.'.format( self.showDir, sickbeard.indexerApi(self.indexer).name) logger.log(error_string, logger.WARNING) ui.notifications.error('Unable to add show', error_string) self._finish_early() return # if the show has no episodes/seasons if not s: error_string = 'Show {0} is on {1} but contains no season/episode data.'.format( s[b'seriesname'], sickbeard.indexerApi(self.indexer).name) logger.log(error_string) ui.notifications.error('Unable to add show', error_string) self._finish_early() return except Exception as error: error_string = 'Unable to look up the show in {0} on {1} using ID {2}, not using the NFO. Delete .nfo and try adding manually again.'.format( self.showDir, sickbeard.indexerApi(self.indexer).name, self.indexer_id) logger.log('{0}: {1}'.format(error_string, error), logger.ERROR) ui.notifications.error('Unable to add show', error_string) if sickbeard.USE_TRAKT: trakt_id = sickbeard.indexerApi( self.indexer).config[b'trakt_id'] trakt_api = TraktAPI(sickbeard.SSL_VERIFY, sickbeard.TRAKT_TIMEOUT) title = self.showDir.split('/')[-1] data = {'shows': [{'title': title, 'ids': {}}]} if trakt_id == 'tvdb_id': data['shows'][0]['ids']['tvdb'] = self.indexer_id else: data['shows'][0]['ids']['tvrage'] = self.indexer_id trakt_api.traktRequest('sync/watchlist/remove', data, method='POST') self._finish_early() return try: try: newShow = TVShow(self.indexer, self.indexer_id, self.lang) except MultipleShowObjectsException as error: # If we have the show in our list, but the location is wrong, lets fix it and refresh! existing_show = Show.find(sickbeard.showList, self.indexer_id) # noinspection PyProtectedMember if existing_show and not ek(os.path.isdir, existing_show._location): # pylint: disable=protected-access newShow = existing_show else: raise error newShow.loadFromIndexer() self.show = newShow # set up initial values self.show.location = self.showDir self.show.subtitles = self.subtitles if self.subtitles is not None else sickbeard.SUBTITLES_DEFAULT self.show.subtitles_sr_metadata = self.subtitles_sr_metadata self.show.quality = self.quality if self.quality else sickbeard.QUALITY_DEFAULT self.show.season_folders = self.season_folders if self.season_folders is not None else sickbeard.SEASON_FOLDERS_DEFAULT self.show.anime = self.anime if self.anime is not None else sickbeard.ANIME_DEFAULT self.show.scene = self.scene if self.scene is not None else sickbeard.SCENE_DEFAULT self.show.paused = self.paused if self.paused is not None else False # set up default new/missing episode status logger.log( 'Setting all episodes to the specified default status: {0}'. format(self.show.default_ep_status)) self.show.default_ep_status = self.default_status if self.show.anime: self.show.release_groups = BlackAndWhiteList( self.show.indexerid) if self.blacklist: self.show.release_groups.set_black_keywords(self.blacklist) if self.whitelist: self.show.release_groups.set_white_keywords(self.whitelist) # # be smart-ish about this # if self.show.genre and 'talk show' in self.show.genre.lower(): # self.show.air_by_date = 1 # if self.show.genre and 'documentary' in self.show.genre.lower(): # self.show.air_by_date = 0 # if self.show.classification and 'sports' in self.show.classification.lower(): # self.show.sports = 1 except sickbeard.indexer_exception as error: error_string = 'Unable to add {0} due to an error with {1}'.format( self.show.name if self.show else 'show', sickbeard.indexerApi(self.indexer).name) logger.log('{0}: {1}'.format(error_string, error), logger.ERROR) ui.notifications.error('Unable to add show', error_string) self._finish_early() return except MultipleShowObjectsException: error_string = 'The show in {0} is already in your show list, skipping'.format( self.showDir) logger.log(error_string, logger.WARNING) ui.notifications.error('Show skipped', error_string) self._finish_early() return except Exception as error: logger.log('Error trying to add show: {0}'.format(error), logger.ERROR) logger.log(traceback.format_exc(), logger.DEBUG) self._finish_early() raise logger.log('Retrieving show info from IMDb', logger.DEBUG) try: self.show.loadIMDbInfo() except imdb_exceptions.IMDbError as error: logger.log(' Something wrong on IMDb api: {0}'.format(error), logger.WARNING) except Exception as error: logger.log('Error loading IMDb info: {0}'.format(error), logger.ERROR) try: self.show.saveToDB() except Exception as error: logger.log( 'Error saving the show to the database: {0}'.format(error), logger.ERROR) logger.log(traceback.format_exc(), logger.DEBUG) self._finish_early() raise # add it to the show list if not Show.find(sickbeard.showList, self.indexer_id): sickbeard.showList.append(self.show) try: self.show.loadEpisodesFromIndexer() except Exception as error: logger.log( 'Error with {0}, not creating episode list: {1}'.format( sickbeard.indexerApi(self.show.indexer).name, error), logger.ERROR) logger.log(traceback.format_exc(), logger.DEBUG) # update internal name cache name_cache.buildNameCache(self.show) try: self.show.loadEpisodesFromDir() except Exception as error: logger.log('Error searching dir for episodes: {0}'.format(error), logger.ERROR) logger.log(traceback.format_exc(), logger.DEBUG) # if they set default ep status to WANTED then run the backlog to search for episodes # FIXME: This needs to be a backlog queue item!!! if self.show.default_ep_status == WANTED: logger.log( 'Launching backlog for this show since its episodes are WANTED' ) sickbeard.backlogSearchScheduler.action.searchBacklog([self.show]) self.show.writeMetadata() self.show.updateMetadata() self.show.populateCache() self.show.flushEpisodes() if sickbeard.USE_TRAKT: # if there are specific episodes that need to be added by trakt sickbeard.traktCheckerScheduler.action.manageNewShow(self.show) # add show to trakt.tv library if sickbeard.TRAKT_SYNC: sickbeard.traktCheckerScheduler.action.addShowToTraktLibrary( self.show) if sickbeard.TRAKT_SYNC_WATCHLIST: logger.log('update watchlist') notifiers.trakt_notifier.update_watchlist(show_obj=self.show) # Load XEM data to DB for show scene_numbering.xem_refresh(self.show.indexerid, self.show.indexer, force=True) # check if show has XEM mapping so we can determine if searches should go by scene numbering or indexer numbering. if not self.scene and scene_numbering.get_xem_numbering_for_show( self.show.indexerid, self.show.indexer): self.show.scene = 1 # After initial add, set to default_status_after. self.show.default_ep_status = self.default_status_after super(QueueItemAdd, self).finish() self.finish()
logger.ERROR) return False if not r.status_code == 200: return False magnetFileName = ek.ek( os.path.join, sickbeard.TORRENT_DIR, helpers.sanitizeFileName(result.name) + '.' + self.providerType) magnetFileContent = r.content try: with open(magnetFileName, 'wb') as fileOut: fileOut.write(magnetFileContent) helpers.chmodAsParent(magnetFileName) except EnvironmentError, e: logger.log("Unable to save the file: " + ex(e), logger.ERROR) return False logger.log(u"Saved magnet link to " + magnetFileName + " ", logger.MESSAGE) return True def findPropers(self, search_date=datetime.datetime.today()): results = [] sqlResults = db.DBConnection().select( 'SELECT s.show_name, e.showid, e.season, e.episode, e.status, e.airdate, s.indexer FROM tv_episodes AS e'