diff --git a/sickbeard/network_timezones.py b/sickbeard/network_timezones.py index ab5805272e76ec3250472c9a346360f986c1a9de..deb5ebe07bbf54f25b544f2ef7507c218577d119 100644 --- a/sickbeard/network_timezones.py +++ b/sickbeard/network_timezones.py @@ -1,5 +1,6 @@ # Author: Nic Wolfe <nic@wolfeden.ca> -# URL: http://code.google.com/p/sickbeard/ +# URL: https://sickrage.tv +# Git: https://github.com/SiCKRAGETV/SickRage.git # # This file is part of SickRage. # @@ -49,6 +50,9 @@ def _remove_zoneinfo_failed(filename): # helper to remove old unneeded zoneinfo files def _remove_old_zoneinfo(): + """ + Removes zoneinfo tar.gz file from repository, as we do not need it + """ if zoneinfo.ZONEINFOFILE is not None: cur_zoneinfo = ek(basename, zoneinfo.ZONEINFOFILE) else: @@ -69,6 +73,9 @@ def _remove_old_zoneinfo(): # update the dateutil zoneinfo def _update_zoneinfo(): + """ + Request new zoneinfo directly from repository + """ global sb_timezone sb_timezone = tz.tzlocal() url_zv = 'http://sickragetv.github.io/sb_network_timezones/zoneinfo.txt' @@ -140,6 +147,7 @@ def _update_zoneinfo(): # update the network timezone table def update_network_dict(): + """Update timezone information from SR repositories""" _remove_old_zoneinfo() _update_zoneinfo() @@ -186,6 +194,9 @@ def update_network_dict(): # load network timezones from db into dict def load_network_dict(): + """ + Load network timezones from db into dict network_dict (global dict) + """ try: my_db = db.DBConnection('cache.db') cur_network_list = my_db.select('SELECT * FROM network_timezones;') @@ -201,6 +212,13 @@ def load_network_dict(): # get timezone of a network or return default timezone def get_network_timezone(network, network_dict): + """ + Get a timezone of a network from a given network dict + + :param network: network to look up (needle) + :param network_dict: dict to look up in (haystack) + :return: + """ if network is None: return sb_timezone @@ -223,6 +241,14 @@ def get_network_timezone(network, network_dict): # parse date and time string into local time def parse_date_time(d, t, network): + """ + Parse date and time string into local time + + :param d: date string + :param t: time string + :param network: network to use as base + :return: datetime object containing local time + """ if network_dict is None: load_network_dict() mo = time_regex.search(t) diff --git a/sickbeard/nzbSplitter.py b/sickbeard/nzbSplitter.py index f9de0bf038b78f406d3cebd5c18104a3555ce45f..4713cff07ed98e5c588607c8e0dd1c3c98e19785 100644 --- a/sickbeard/nzbSplitter.py +++ b/sickbeard/nzbSplitter.py @@ -1,5 +1,6 @@ # Author: Nic Wolfe <nic@wolfeden.ca> -# URL: http://code.google.com/p/sickbeard/ +# URL: https://sickrage.tv +# Git: https://github.com/SiCKRAGETV/SickRage.git # # This file is part of SickRage. # @@ -33,6 +34,14 @@ from name_parser.parser import NameParser, InvalidNameException, InvalidShowExce def getSeasonNZBs(name, urlData, season): + """ + Split a season NZB into episodes + + :param name: NZB name + :param urlData: URL to get data from + :param season: Season to check + :return: dict of (episode files, xml matches) + """ try: showXML = etree.ElementTree(etree.XML(urlData)) except SyntaxError: @@ -89,6 +98,12 @@ def createNZBString(fileElements, xmlns): def saveNZB(nzbName, nzbString): + """ + Save NZB to disk + + :param nzbName: Filename/path to write to + :param nzbString: Content to write in file + """ try: with ek(open, nzbName + ".nzb", 'w') as nzb_fh: nzb_fh.write(nzbString) @@ -106,6 +121,12 @@ def stripNS(element, ns): def splitResult(result): + """ + Split result into seperate episodes + + :param result: search result object + :return: False upon failure, a list of episode objects otherwise + """ urlData = helpers.getURL(result.url, session=requests.Session()) if urlData is None: logger.log(u"Unable to load url " + result.url + ", can't download season NZB", logger.ERROR) diff --git a/sickbeard/nzbget.py b/sickbeard/nzbget.py index e4d914e36baefacab535b38289559cc1e85bd4ef..077f57de470be9231c93f4e5f06740c56f88ba1d 100644 --- a/sickbeard/nzbget.py +++ b/sickbeard/nzbget.py @@ -1,5 +1,6 @@ # Author: Nic Wolfe <nic@wolfeden.ca> -# URL: http://code.google.com/p/sickbeard/ +# URL: https://sickrage.tv +# Git: https://github.com/SiCKRAGETV/SickRage.git # # This file is part of SickRage. # @@ -34,6 +35,12 @@ from common import Quality def sendNZB(nzb, proper=False): + """ + Sends NZB to NZBGet client + + :param nzb: nzb object + :param proper: True if this is a Proper download, False if not. Defaults to False + """ addToTop = False nzbgetprio = 0 category = sickbeard.NZBGET_CATEGORY diff --git a/sickbeard/postProcessor.py b/sickbeard/postProcessor.py index 536a7deee981fc05cf110bf864f43a4b372c3b00..54bc70c7a49ac208a1f93f718f7e71434c8ad8f4 100644 --- a/sickbeard/postProcessor.py +++ b/sickbeard/postProcessor.py @@ -1,5 +1,6 @@ # Author: Nic Wolfe <nic@wolfeden.ca> -# URL: http://code.google.com/p/sickbeard/ +# URL: https://sickrage.tv +# Git: https://github.com/SiCKRAGETV/SickRage.git # # This file is part of SickRage. # @@ -98,8 +99,8 @@ class PostProcessor(object): """ A wrapper for the internal logger which also keeps track of messages and saves them to a string for later. - message: The string to log (unicode) - level: The log level to use (optional) + :param message: The string to log (unicode) + :param level: The log level to use (optional) """ logger.log(message, level) self.log += message + '\n' @@ -109,9 +110,9 @@ class PostProcessor(object): Checks if a file exists already and if it does whether it's bigger or smaller than the file we are post processing - existing_file: The file to compare to + ;param existing_file: The file to compare to - Returns: + :return: DOESNT_EXIST if the file doesn't exist EXISTS_LARGER if the file exists and is larger than the file we are post processing EXISTS_SMALLER if the file exists and is smaller than the file we are post processing @@ -147,11 +148,11 @@ class PostProcessor(object): """ For a given file path searches for files with the same name but different extension and returns their absolute paths - file_path: The file to check for associated files + :param file_path: The file to check for associated files - base_name_only: False add extra '.' (conservative search) to file_path minus extension + :param base_name_only: False add extra '.' (conservative search) to file_path minus extension - Returns: A list containing all files which are associated to the given file + :return: A list containing all files which are associated to the given file """ def recursive_glob(treeroot, pattern): results = [] @@ -219,8 +220,8 @@ class PostProcessor(object): """ Deletes the file and optionally all associated files. - file_path: The file to delete - associated_files: True to delete all files which differ only by extension, False to leave them + :param file_path: The file to delete + :param associated_files: True to delete all files which differ only by extension, False to leave them """ if not file_path: @@ -260,11 +261,12 @@ class PostProcessor(object): 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) + :param file_path: The full path of the media file to act on + :param new_path: Destination path where we want to move/copy the file to + :param new_base_name: The base filename (no extension) to use during the copy. Use None to keep the same name. + :param associated_files: Boolean, whether we should copy similarly-named files too + :param action: function that takes an old path and new path and does an operation with them (move/copy) + :param subtitles: Boolean, whether we should process subtitles too """ if not action: @@ -325,10 +327,12 @@ class PostProcessor(object): def _move(self, file_path, new_path, new_base_name, associated_files=False, subtitles=False): """ - file_path: The full path of the media file to move - new_path: Destination path where we want to move the file to - new_base_name: The base filename (no extension) to use during the move. Use None to keep the same name. - associated_files: Boolean, whether we should move similarly-named files too + Move file and set proper permissions + + :param file_path: The full path of the media file to move + :param new_path: Destination path where we want to move the file to + :param new_base_name: The base filename (no extension) to use during the move. Use None to keep the same name. + :param associated_files: Boolean, whether we should move similarly-named files too """ def _int_move(cur_file_path, new_file_path): @@ -346,10 +350,12 @@ class PostProcessor(object): def _copy(self, file_path, new_path, new_base_name, associated_files=False, subtitles=False): """ - file_path: The full path of the media file to copy - new_path: Destination path where we want to 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 + Copy file and set proper permissions + + :param file_path: The full path of the media file to copy + :param new_path: Destination path where we want to copy the file to + :param new_base_name: The base filename (no extension) to use during the copy. Use None to keep the same name. + :param associated_files: Boolean, whether we should copy similarly-named files too """ def _int_copy(cur_file_path, new_file_path): @@ -368,10 +374,12 @@ class PostProcessor(object): def _hardlink(self, file_path, new_path, new_base_name, associated_files=False, subtitles=False): """ - file_path: The full path of the media file to move - new_path: Destination path where we want to create a hard linked file - new_base_name: The base filename (no extension) to use during the link. Use None to keep the same name. - associated_files: Boolean, whether we should move similarly-named files too + Hardlink file and set proper permissions + + :param file_path: The full path of the media file to move + :param new_path: Destination path where we want to create a hard linked file + :param new_base_name: The base filename (no extension) to use during the link. Use None to keep the same name. + :param associated_files: Boolean, whether we should move similarly-named files too """ def _int_hard_link(cur_file_path, new_file_path): @@ -388,10 +396,12 @@ class PostProcessor(object): def _moveAndSymlink(self, file_path, new_path, new_base_name, associated_files=False, subtitles=False): """ - file_path: The full path of the media file to move - new_path: Destination path where we want to move the file to create a symbolic link to - new_base_name: The base filename (no extension) to use during the link. Use None to keep the same name. - associated_files: Boolean, whether we should move similarly-named files too + Move file, symlink source location back to destination, and set proper permissions + + :param file_path: The full path of the media file to move + :param new_path: Destination path where we want to move the file to create a symbolic link to + :param new_base_name: The base filename (no extension) to use during the link. Use None to keep the same name. + :param associated_files: Boolean, whether we should move similarly-named files too """ def _int_move_and_sym_link(cur_file_path, new_file_path): @@ -411,7 +421,7 @@ class PostProcessor(object): """ Look up the NZB name in the history and see if it contains a record for self.nzb_name - Returns a (indexer_id, season, [], quality, version) tuple. The first two may be None if none were found. + :return: A (indexer_id, season, [], quality, version) tuple. The first two may be None if none were found. """ to_return = (None, None, [], None, None) @@ -460,6 +470,11 @@ class PostProcessor(object): return to_return def _finalize(self, parse_result): + """ + Store parse result if it is complete and final + + :param parse_result: Result of parsers + """ self.release_group = parse_result.release_group # remember whether it's a proper @@ -487,9 +502,9 @@ class PostProcessor(object): """ Takes a name and tries to figure out a show, season, and episode from it. - name: A string which we want to analyze to determine show info from (unicode) + :param name: A string which we want to analyze to determine show info from (unicode) - Returns a (indexer_id, season, [episodes]) tuple. The first two may be None and episodes may be [] + :return: A (indexer_id, season, [episodes]) tuple. The first two may be None and episodes may be [] if none were found. """ @@ -522,6 +537,13 @@ class PostProcessor(object): return to_return def _build_anidb_episode(self, connection, filePath): + """ + Look up anidb properties for an episode + + :param connection: anidb connection handler + :param filePath: file to check + :return: episode object + """ ep = adba.Episode(connection, filePath=filePath, paramsF=["quality", "anidb_file_name", "crc32"], paramsA=["epno", "english_name", "short_name_list", "other_name", "synonym_list"]) @@ -529,8 +551,13 @@ class PostProcessor(object): return ep def _add_to_anidb_mylist(self, filePath): + """ + Adds an episode to anidb mylist + + :param filePath: file to add to mylist + """ if helpers.set_up_anidb_connection(): - if not self.anidbEpisode: # seams like we could parse the name before, now lets build the anidb object + if not self.anidbEpisode: # seems like we could parse the name before, now lets build the anidb object self.anidbEpisode = self._build_anidb_episode(sickbeard.ADBA_CONNECTION, filePath) self._log(u"Adding the file to the anidb mylist", logger.DEBUG) @@ -542,6 +569,8 @@ class PostProcessor(object): def _find_info(self): """ For a given file try to find the showid, season, and episode. + + :return: A (show, season, episodes, quality, version) tuple """ show = season = quality = version = None @@ -643,11 +672,11 @@ class PostProcessor(object): """ Retrieve the TVEpisode object requested. - show: The show object belonging to the show we want to process - season: The season of the episode (int) - episodes: A list of episodes to find (list of ints) + :param show: The show object belonging to the show we want to process + :param season: The season of the episode (int) + :param episodes: A list of episodes to find (list of ints) - If the episode(s) can be found then a TVEpisode object with the correct related eps will + :return: If the episode(s) can be found then a TVEpisode object with the correct related eps will be instantiated and returned. If the episode can't be found then None will be returned. """ @@ -678,9 +707,8 @@ class PostProcessor(object): Determines the quality of the file that is being post processed, first by checking if it is directly available in the TVEpisode's status or otherwise by parsing through the data available. - ep_obj: The TVEpisode object related to the file we are post processing - - Returns: A quality value found in common.Quality + :param ep_obj: The TVEpisode object related to the file we are post processing + :return: A quality value found in common.Quality """ ep_quality = common.Quality.UNKNOWN @@ -741,7 +769,7 @@ class PostProcessor(object): """ Executes any extra scripts defined in the config. - ep_obj: The object to use when calling the extra script + :param ep_obj: The object to use when calling the extra script """ for curScriptName in sickbeard.EXTRA_SCRIPTS: @@ -772,10 +800,9 @@ class PostProcessor(object): Determines if the episode is a priority download or not (if it is expected). Episodes which are expected (snatched) or larger than the existing episode are priority, others are not. - ep_obj: The TVEpisode object in question - new_ep_quality: The quality of the episode that is being processed - - Returns: True if the episode is priority, False otherwise. + :param ep_obj: The TVEpisode object in question + :param new_ep_quality: The quality of the episode that is being processed + :return: True if the episode is priority, False otherwise. """ if self.is_priority: @@ -816,6 +843,8 @@ class PostProcessor(object): 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) + ")") diff --git a/sickbeard/processTV.py b/sickbeard/processTV.py index 34c57e8128068f38f878e0a6a49a7e817d8e1f12..b4dfc42b4b7705d95c485e2c0644dc7f31e85418 100644 --- a/sickbeard/processTV.py +++ b/sickbeard/processTV.py @@ -1,5 +1,6 @@ # Author: Nic Wolfe <nic@wolfeden.ca> -# URL: http://code.google.com/p/sickbeard/ +# URL: https://sickrage.tv/ +# Git: https://github.com/SiCKRAGETV/SickRage.git # # This file is part of SickRage. # @@ -49,6 +50,13 @@ class ProcessResult: def delete_folder(folder, check_empty=True): + """ + Removes a folder from the filesystem + + :param folder: Path to folder to remove + :param check_empty: Boolean, check if the folder is empty before removing it, defaults to True + :return: True on success, False on failure + """ # check if it's a folder if not ek(os.path.isdir, folder): @@ -84,6 +92,14 @@ def delete_folder(folder, check_empty=True): def delete_files(processPath, notwantedFiles, result, force=False): + """ + Remove files from filesystem + + :param processPath: path to process + :param notwantedFiles: files we do not want + :param result: Processor results + :param force: Boolean, force deletion, defaults to false + """ if not result.result and force: result.output += logHelper(u"Forcing deletion of files, even though last result was not success", logger.DEBUG) @@ -125,11 +141,11 @@ def processDir(dirName, nzbName=None, process_method=None, force=False, is_prior """ Scans through the files in dirName and processes whatever media files it finds - dirName: The folder name to look in - nzbName: The NZB name which resulted in this folder being downloaded - force: True to postprocess already postprocessed files - failed: Boolean for whether or not the download failed - type: Type of postprocessing auto or manual + :param dirName: The folder name to look in + :param nzbName: The NZB name which resulted in this folder being downloaded + :param force: True to postprocess already postprocessed files + :param failed: Boolean for whether or not the download failed + :param type: Type of postprocessing auto or manual """ result = ProcessResult() @@ -284,6 +300,16 @@ def processDir(dirName, nzbName=None, process_method=None, force=False, is_prior def validateDir(path, dirName, nzbNameOriginal, failed, result): + """ + Check if directory is valid for processing + + :param path: Path to use + :param dirName: Directory to check + :param nzbNameOriginal: Original NZB name + :param failed: Previously failed objects + :param result: Previous results + :return: True if dir is valid for processing, False if not + """ result.output += logHelper(u"Processing folder " + dirName, logger.DEBUG) @@ -363,6 +389,15 @@ def validateDir(path, dirName, nzbNameOriginal, failed, result): return False def unRAR(path, rarFiles, force, result): + """ + Extracts RAR files + + :param path: Path to look for files in + :param rarFiles: Names of RAR files + :param force: process currently processing items + :param result: Previous results + :return: List of unpacked file names + """ unpacked_files = [] @@ -435,7 +470,15 @@ def unRAR(path, rarFiles, force, result): def already_postprocessed(dirName, videofile, force, result): + """ + Check if we already post processed a file + :param dirName: Directory a file resides in + :param videofile: File name + :param force: Force checking when already checking (currently unused) + :param result: True if file is already postprocessed, False if not + :return: + """ if force: return False @@ -478,6 +521,17 @@ def already_postprocessed(dirName, videofile, force, result): def process_media(processPath, videoFiles, nzbName, process_method, force, is_priority, result): + """ + Postprocess mediafiles + + :param processPath: Path to postprocess in + :param videoFiles: Filenames to look for and postprocess + :param nzbName: Name of NZB file related + :param process_method: auto/manual + :param force: Postprocess currently postprocessing file + :param is_priority: Boolean, is this a priority download + :param result: Previous results + """ processor = None for cur_video_file in videoFiles: @@ -508,6 +562,14 @@ def process_media(processPath, videoFiles, nzbName, process_method, force, is_pr def get_path_dir_files(dirName, nzbName, type): + """ + Get files in a path + + :param dirName: Directory to start in + :param nzbName: NZB file, if present + :param type: auto/manual + :return: a tuple of (path,dirs,files) + """ path = "" dirs = [] files = [] diff --git a/sickbeard/properFinder.py b/sickbeard/properFinder.py index 2b98188476a078c9ab0084ef7b5aeeae6cb2a231..9a8e11b987619028e375edc6c25ead2c76c93c52 100644 --- a/sickbeard/properFinder.py +++ b/sickbeard/properFinder.py @@ -1,5 +1,6 @@ # Author: Nic Wolfe <nic@wolfeden.ca> -# URL: http://code.google.com/p/sickbeard/ +# URL: https://sickrage.tv +# Git: https://github.com/SiCKRAGETV/SickRage.git # # This file is part of SickRage. # @@ -44,6 +45,11 @@ class ProperFinder(): self.amActive = False def run(self, force=False): + """ + Start looking for new propers + + :param force: Start even if already running (currently not used, defaults to False) + """ logger.log(u"Beginning the search for new propers") self.amActive = True @@ -68,6 +74,9 @@ class ProperFinder(): self.amActive = False def _getProperList(self): + """ + Walk providers for propers + """ propers = {} search_date = datetime.datetime.today() - datetime.timedelta(days=2) @@ -201,6 +210,11 @@ class ProperFinder(): return finalPropers def _downloadPropers(self, properList): + """ + Download proper (snatch it) + + :param properList: + """ for curProper in properList: @@ -256,6 +270,11 @@ class ProperFinder(): return name.replace(".", " ").replace("-", " ").replace("_", " ").lower() def _set_lastProperSearch(self, when): + """ + Record last propersearch in DB + + :param when: When was the last proper search + """ logger.log(u"Setting the last Proper search in the DB to " + str(when), logger.DEBUG) @@ -269,6 +288,9 @@ class ProperFinder(): myDB.action("UPDATE info SET last_proper_search=" + str(when)) def _get_lastProperSearch(self): + """ + Find last propersearch from DB + """ myDB = db.DBConnection() sqlResults = myDB.select("SELECT * FROM info") diff --git a/sickbeard/sab.py b/sickbeard/sab.py index 639dfe8095e36a936705025bb5c2592aa9a5ef86..458863987a03d2f8c302d22f688d9bf1bf79cae0 100644 --- a/sickbeard/sab.py +++ b/sickbeard/sab.py @@ -1,5 +1,6 @@ # Author: Nic Wolfe <nic@wolfeden.ca> -# URL: http://code.google.com/p/sickbeard/ +# URL: https://sickrage.tv +# Git: https://github.com/SiCKRAGETV/SickRage # # This file is part of SickRage. # @@ -39,7 +40,7 @@ def sendNZB(nzb): """ Sends an NZB to SABnzbd via the API. - nzb: The NZBSearchResult object to send to SAB + :param nzb: The NZBSearchResult object to send to SAB """ # set up a dict with the URL params in it @@ -146,6 +147,12 @@ def sendNZB(nzb): def _checkSabResponse(f): + """ + Check response from SAB + + :param f: Response from SAV + :return: a list of (Boolean, string) which is True if SAB is not reporting an error + """ try: result = f.readlines() except Exception, e: @@ -174,6 +181,12 @@ def _checkSabResponse(f): def _sabURLOpenSimple(url): + """ + Open a connection to SAB + + :param url: URL where SAB is at + :return: (boolean, string) list, True if connection can be made + """ try: f = urllib.urlopen(url) except (EOFError, IOError), e: @@ -190,6 +203,15 @@ def _sabURLOpenSimple(url): def getSabAccesMethod(host=None, username=None, password=None, apikey=None): + """ + Find out how we should connect to SAB + + :param host: hostname where SAB lives + :param username: username to use + :param password: password to use + :param apikey: apikey to use + :return: (boolean, string) with True if method was successful + """ url = host + "api?mode=auth" result, f = _sabURLOpenSimple(url) @@ -207,12 +229,11 @@ def testAuthentication(host=None, username=None, password=None, apikey=None): """ Sends a simple API request to SAB to determine if the given connection information is connect - host: The host where SAB is running (incl port) - username: The username to use for the HTTP request - password: The password to use for the HTTP request - apikey: The API key to provide to SAB - - Returns: A tuple containing the success boolean and a message + :param host: The host where SAB is running (incl port) + :param username: The username to use for the HTTP request + :param password: The password to use for the HTTP request + :param apikey: The API key to provide to SAB + :return: A tuple containing the success boolean and a message """ # build up the URL parameters diff --git a/sickbeard/sbdatetime.py b/sickbeard/sbdatetime.py index c3c34cdad68ef4575c84650607fb479adab8d57f..1240df9296e3e2ef98960c49294126f41efc58a4 100644 --- a/sickbeard/sbdatetime.py +++ b/sickbeard/sbdatetime.py @@ -1,5 +1,6 @@ # Author: Nic Wolfe <nic@wolfeden.ca> -# URL: http://code.google.com/p/sickbeard/ +# URL: https://sickrage.tv +# Git: https://github.com/SiCKRAGETV/SickRage.git # # This file is part of SickRage. # @@ -125,6 +126,15 @@ class sbdatetime(datetime.datetime): # display Time in SickRage Format @static_or_instance def sbftime(self, dt=None, show_seconds=False, t_preset=None): + """ + Display time in SR format + TODO: Rename this to srftime + + :param dt: datetime object + :param show_seconds: Boolean, show seconds + :param t_preset: Preset time format + :return: time string + """ try:locale.setlocale(locale.LC_TIME, '') except:pass @@ -168,6 +178,14 @@ class sbdatetime(datetime.datetime): # display Date in SickRage Format @static_or_instance def sbfdate(self, dt=None, d_preset=None): + """ + Display date in SR format + TODO: Rename this to srfdate + + :param dt: datetime object + :param d_preset: Preset date format + :return: date string + """ try: locale.setlocale(locale.LC_TIME, '') @@ -199,6 +217,16 @@ class sbdatetime(datetime.datetime): # display Datetime in SickRage Format @static_or_instance def sbfdatetime(self, dt=None, show_seconds=False, d_preset=None, t_preset=None): + """ + Show datetime in SR format + TODO: Rename this to srfdatetime + + :param dt: datetime object + :param show_seconds: Boolean, show seconds as well + :param d_preset: Preset date format + :param t_preset: Preset time format + :return: datetime string + """ try: locale.setlocale(locale.LC_TIME, '') diff --git a/sickbeard/scene_exceptions.py b/sickbeard/scene_exceptions.py index fc70d2f8539fd8d17431af033cd7f8061ab7ce4b..dbf7b665e4f78c736f72c4d791b0e8c53cfe73ea 100644 --- a/sickbeard/scene_exceptions.py +++ b/sickbeard/scene_exceptions.py @@ -1,5 +1,6 @@ # Author: Nic Wolfe <nic@wolfeden.ca> -# URL: http://code.google.com/p/sickbeard/ +# URL: https://sickrage.tv +# Git: https://github.com/SiCKRAGETV/SickRage.git # # This file is part of SickRage. # @@ -38,6 +39,12 @@ exceptionsSeasonCache = {} exceptionLock = threading.Lock() def shouldRefresh(list): + """ + Check if we should refresh cache for items in list + + :param list: list to check + :return: True if refresh is needed + """ MAX_REFRESH_AGE_SECS = 86400 # 1 day myDB = db.DBConnection('cache.db') @@ -49,6 +56,11 @@ def shouldRefresh(list): return True def setLastRefresh(list): + """ + Update last cache update time for shows in list + + :param list: list to check + """ myDB = db.DBConnection('cache.db') myDB.upsert("scene_exceptions_refresh", {'last_refreshed': int(time.mktime(datetime.datetime.today().timetuple()))}, @@ -81,6 +93,12 @@ def get_scene_exceptions(indexer_id, season=-1): def get_all_scene_exceptions(indexer_id): + """ + Get all scene exceptions for a show ID + + :param indexer_id: ID to check + :return: dict of exceptions + """ exceptionsDict = {} myDB = db.DBConnection('cache.db') @@ -316,8 +334,7 @@ def _xem_exceptions_fetcher(): def getSceneSeasons(indexer_id): - """get a list of season numbers that have scene exceptions - """ + """get a list of season numbers that have scene exceptions""" myDB = db.DBConnection('cache.db') seasons = myDB.select("SELECT DISTINCT season FROM scene_exceptions WHERE indexer_id = ?", [indexer_id]) return [cur_exception["season"] for cur_exception in seasons] diff --git a/sickbeard/scene_numbering.py b/sickbeard/scene_numbering.py index 11f7a9cd2b5776b7f50c873a1886f35901c78894..ffb3d0fdb2667b92b771ec4caa11577797636455 100644 --- a/sickbeard/scene_numbering.py +++ b/sickbeard/scene_numbering.py @@ -1,5 +1,6 @@ # Author: Nic Wolfe <nic@wolfeden.ca> -# URL: http://code.google.com/p/sickbeard/ +# URL: https://sickrage.tv +# Git: https://github.com/SiCKRAGETV/SickRage.git # # This file is part of SickRage. # @@ -39,11 +40,11 @@ def get_scene_numbering(indexer_id, indexer, season, episode, fallback_to_xem=Tr returns the TVDB numbering. (so the return values will always be set) - @param indexer_id: int - @param season: int - @param episode: int - @param fallback_to_xem: bool If set (the default), check xem for matches if there is no local scene numbering - @return: (int, int) a tuple with (season, episode) + :param indexer_id: int + :param season: int + :param episode: int + :param fallback_to_xem: bool If set (the default), check xem for matches if there is no local scene numbering + :return: (int, int) a tuple with (season, episode) """ if indexer_id is None or season is None or episode is None: return (season, episode) @@ -89,10 +90,10 @@ def get_scene_absolute_numbering(indexer_id, indexer, absolute_number, fallback_ returns the TVDB numbering. (so the return values will always be set) - @param indexer_id: int - @param absolute_number: int - @param fallback_to_xem: bool If set (the default), check xem for matches if there is no local scene numbering - @return: (int, int) a tuple with (season, episode) + :param indexer_id: int + ;param absolute_number: int + :param fallback_to_xem: bool If set (the default), check xem for matches if there is no local scene numbering + :return: (int, int) a tuple with (season, episode) """ if indexer_id is None or absolute_number is None: return absolute_number @@ -192,7 +193,6 @@ def set_scene_numbering(indexer_id, indexer, season=None, episode=None, absolute """ Set scene numbering for a season/episode. To clear the scene numbering, leave both sceneSeason and sceneEpisode as None. - """ if indexer_id is None: return @@ -228,10 +228,10 @@ def find_xem_numbering(indexer_id, indexer, season, episode): Returns the scene numbering, as retrieved from xem. Refreshes/Loads as needed. - @param indexer_id: int - @param season: int - @param episode: int - @return: (int, int) a tuple of scene_season, scene_episode, or None if there is no special mapping. + :param indexer_id: int + :param season: int + :param episode: int + :return: (int, int) a tuple of scene_season, scene_episode, or None if there is no special mapping. """ if indexer_id is None or season is None or episode is None: return (season, episode) @@ -255,9 +255,9 @@ def find_xem_absolute_numbering(indexer_id, indexer, absolute_number): Returns the scene numbering, as retrieved from xem. Refreshes/Loads as needed. - @param indexer_id: int - @param absolute_number: int - @return: int + :param indexer_id: int + :param absolute_number: int + :return: int """ if indexer_id is None or absolute_number is None: return absolute_number @@ -280,10 +280,10 @@ def get_indexer_numbering_for_xem(indexer_id, indexer, sceneSeason, sceneEpisode """ Reverse of find_xem_numbering: lookup a tvdb season and episode using scene numbering - @param indexer_id: int - @param sceneSeason: int - @param sceneEpisode: int - @return: (int, int) a tuple of (season, episode) + :param indexer_id: int + :param sceneSeason: int + :param sceneEpisode: int + :return: (int, int) a tuple of (season, episode) """ if indexer_id is None or sceneSeason is None or sceneEpisode is None: return (sceneSeason, sceneEpisode) @@ -308,9 +308,9 @@ def get_indexer_absolute_numbering_for_xem(indexer_id, indexer, sceneAbsoluteNum """ Reverse of find_xem_numbering: lookup a tvdb season and episode using scene numbering - @param indexer_id: int - @param sceneAbsoluteNumber: int - @return: int + :param indexer_id: int + :param sceneAbsoluteNumber: int + :return: int """ if indexer_id is None or sceneAbsoluteNumber is None: return sceneAbsoluteNumber @@ -456,7 +456,7 @@ def xem_refresh(indexer_id, indexer, force=False): """ Refresh data from xem for a tv show - @param indexer_id: int + :param indexer_id: int """ if not indexer_id or indexer_id < 1: return diff --git a/sickbeard/scheduler.py b/sickbeard/scheduler.py index 32c08ffa69b647ba386917d1a77f8b057cf95f8d..f95f1ecaa8465e57268ddd97a82479a411050044 100644 --- a/sickbeard/scheduler.py +++ b/sickbeard/scheduler.py @@ -1,5 +1,6 @@ # Author: Nic Wolfe <nic@wolfeden.ca> -# URL: http://code.google.com/p/sickbeard/ +# URL: https://sickrage.tv +# Git: https://github.com/SiCKRAGETV/SickRage.git # # This file is part of SickRage. # @@ -48,6 +49,10 @@ class Scheduler(threading.Thread): self.enable = False def timeLeft(self): + """ + Check how long we have until we run again + :return: timedelta + """ if self.isAlive(): if self.start_time is None: return self.cycleTime - (datetime.datetime.now() - self.lastRun) @@ -69,6 +74,9 @@ class Scheduler(threading.Thread): return False def run(self): + """ + Runs the thread + """ try: while not self.stop.is_set(): if self.enable: diff --git a/sickbeard/search.py b/sickbeard/search.py index d92c5acab708f8bbd6a541280598a479862d4bb6..b11bd24662322d5ed082e70def54bf5df992f68b 100644 --- a/sickbeard/search.py +++ b/sickbeard/search.py @@ -1,5 +1,6 @@ # Author: Nic Wolfe <nic@wolfeden.ca> -# URL: http://code.google.com/p/sickbeard/ +# URL: https://sickrage.tv +# Git: https://github.com/SiCKRAGETV/SickRage.git # # This file is part of SickRage. # @@ -47,9 +48,8 @@ def _downloadResult(result): """ Downloads a result to the appropriate black hole folder. - Returns a bool representing success. - - result: SearchResult instance to download. + :param result: SearchResult instance to download. + :return: boolean, True on success """ resProvider = result.provider @@ -93,10 +93,9 @@ def snatchEpisode(result, endStatus=SNATCHED): Contains the internal logic necessary to actually "snatch" a result that has been found. - Returns a bool representing success. - - result: SearchResult instance to be snatched. - endStatus: the episode status that should be used for the episode object once it's snatched. + :param result: SearchResult instance to be snatched. + :param endStatus: the episode status that should be used for the episode object once it's snatched. + :return: boolean, True on success """ if result is None: @@ -185,6 +184,13 @@ def snatchEpisode(result, endStatus=SNATCHED): def pickBestResult(results, show): + """ + Find the best result out of a list of search results for a show + + :param results: list of result objects + :param show: Shows we check for + :return: best result object + """ results = results if isinstance(results, list) else [results] logger.log(u"Picking the best result out of " + str([x.name for x in results]), logger.DEBUG) @@ -260,7 +266,6 @@ def isFinalResult(result): If the result is the highest quality in both the any/best quality lists then this function returns True, if not then it's False - """ logger.log(u"Checking if we should keep searching after we've found " + result.name, logger.DEBUG) @@ -309,6 +314,12 @@ def isFirstBestMatch(result): return False def wantedEpisodes(show, fromDate): + """ + Get a list of episodes that we want to download + :param show: Show these episodes are from + :param fromDate: Search from a certain date + :return: list of wanted episodes + """ anyQualities, bestQualities = common.Quality.splitQuality(show.quality) # @UnusedVariable allQualities = list(set(anyQualities + bestQualities)) @@ -339,6 +350,11 @@ def wantedEpisodes(show, fromDate): return wanted def searchForNeededEpisodes(): + """ + Check providers for details on wanted episodes + + :return: episodes we have a search hit for + """ foundResults = {} didSearch = False @@ -411,6 +427,15 @@ def searchForNeededEpisodes(): def searchProviders(show, episodes, manualSearch=False, downCurQuality=False): + """ + Walk providers for information on shows + + :param show: Show we are looking for + :param episodes: Episodes we hope to find + :param manualSearch: Boolean, is this a manual search? + :param downCurQuality: Boolean, should we redownload currently avaialble quality file + :return: results for search + """ foundResults = {} finalResults = [] @@ -593,7 +618,7 @@ def searchProviders(show, episodes, manualSearch=False, downCurQuality=False): logger.log(u"Seeing if we want to bother with multi-episode result " + _multiResult.name, logger.DEBUG) - # Filter result by ignore/required/whitelist/blacklist/quality, etc + # Filter result by ignore/required/whitelist/blacklist/quality, etc multiResult = pickBestResult(_multiResult, show) if not multiResult: continue