diff --git a/gui/slick/js/core.js b/gui/slick/js/core.js index ab4d37a3e534548f49d9295c99f69d06c4830a8d..90c927e06566a073bb6836d10f3b26d882345f87 100644 --- a/gui/slick/js/core.js +++ b/gui/slick/js/core.js @@ -159,6 +159,15 @@ var SICKRAGE = { $.get($(this).attr('href')); return false; }); + + $(document.body).on('click', '.bulkCheck', function(){ + var bulkCheck = this; + var whichBulkCheck = $(bulkCheck).attr('id'); + + $('.'+whichBulkCheck+':visible').each(function(){ + $(this).prop('checked', $(bulkCheck).prop('checked')); + }); + }); } }, config: { @@ -2389,7 +2398,7 @@ var SICKRAGE = { } row += '</td>'; } else { - row += '<td style="width: 8%;">None</td>'; + row += '<td style="width: 8%;">None</td>'; } row += '<td>' + name + '</td>'; row += '</tr>'; @@ -2480,15 +2489,6 @@ var SICKRAGE = { window.location.href = srRoot + '/manage/failedDownloads?toRemove='+removeArr.join('|'); }); - $('.bulkCheck').on('click', function(){ - var bulkCheck = this; - var whichBulkCheck = $(bulkCheck).attr('id'); - - $('.'+whichBulkCheck+':visible').each(function(){ - this.checked = bulkCheck.checked; - }); - }); - if($('.removeCheck').length){ $('.removeCheck').each(function(name) { var lastCheck = null; diff --git a/gui/slick/js/core.min.js b/gui/slick/js/core.min.js index fd0fb136dc61acbdbe7e384df326b3ae49cce5bd..3b682619b9711aad0da2b28bf9d569bff5ddc0e6 100644 Binary files a/gui/slick/js/core.min.js and b/gui/slick/js/core.min.js differ diff --git a/gui/slick/js/massUpdate.js b/gui/slick/js/massUpdate.js index 032c9256f367d4121c8bf4a389b5333ed7040b9b..9539cf53a482c5c05184e77a4fac3dd7e04c8714 100644 --- a/gui/slick/js/massUpdate.js +++ b/gui/slick/js/massUpdate.js @@ -65,15 +65,6 @@ $(document).ready(function(){ } }); - $('.bulkCheck').on('click', function(){ - var bulkCheck = this; - var whichBulkCheck = $(bulkCheck).attr('id'); - - $('.'+whichBulkCheck).each(function(){ - if(!this.disabled) { this.checked = !this.checked; } - }); - }); - ['.editCheck', '.updateCheck', '.refreshCheck', '.renameCheck', '.deleteCheck', '.removeCheck'].forEach(function(name) { var lastCheck = null; diff --git a/gui/slick/views/config_subtitles.mako b/gui/slick/views/config_subtitles.mako index df6c0246bc93ccd0f37616548a54ef0536a5d716..720b10cc23e5274acc53a981bf0a75dfaa3651bc 100644 --- a/gui/slick/views/config_subtitles.mako +++ b/gui/slick/views/config_subtitles.mako @@ -79,7 +79,7 @@ $('#subtitles_dir').fileBrowser({ title: 'Select Subtitles Download Directory' } <div class="field-pair"> <label> <span class="component-title">Subtitle Find Frequency</span> - <input type="number" name="subtitles_finder_frequency" value="${sickbeard.SUBTITLES_FINDER_FREQUENCY}" hours="1" class="form-control input-sm input75" /> + <input type="number" name="subtitles_finder_frequency" value="${sickbeard.SUBTITLES_FINDER_FREQUENCY}" hours="1" min="1" step="1" class="form-control input-sm input75" /> <span class="component-desc">time in hours between scans (default: 1)</span> </label> </div> @@ -100,6 +100,10 @@ $('#subtitles_dir').fileBrowser({ title: 'Select Subtitles Download Directory' } <p>Append language codes to subtitle filenames?</p> </span> </label> + <label> + <span class="component-title"> </span> + <span class="component-desc"><b>NOTE:</b> This option is required if you use multiple subtitle languages.</span> + </label> </div> <div class="field-pair"> <label class="clearfix" for="subtitles_download_in_pp"> diff --git a/gui/slick/views/displayShow.mako b/gui/slick/views/displayShow.mako index 8e78f7332e10a33aa9301dbcbdcb566d434e2e23..9b7515d7ff7681da6e9bb316a590fcb9c70c674d 100644 --- a/gui/slick/views/displayShow.mako +++ b/gui/slick/views/displayShow.mako @@ -519,8 +519,10 @@ <a class="epSearch" id="${str(show.indexerid)}x${str(epResult["season"])}x${str(epResult["episode"])}" name="${str(show.indexerid)}x${str(epResult["season"])}x${str(epResult["episode"])}" href="searchEpisode?show=${show.indexerid}&season=${epResult["season"]}&episode=${epResult["episode"]}"><img src="${srRoot}/images/search16.png" width="16" height="16" alt="search" title="Manual Search" /></a> % endif % endif - % if sickbeard.USE_SUBTITLES and show.subtitles and epResult["location"] and (len(epResult["subtitles"]) == 0 or subtitles.needs_subtitles(epResult['subtitles'])): - <a class="epSubtitlesSearch" href="searchEpisodeSubtitles?show=${show.indexerid}&season=${epResult["season"]}&episode=${epResult["episode"]}"><img src="${srRoot}/images/closed_captioning.png" height="16" alt="search subtitles" title="Search Subtitles" /></a> + % if sickbeard.USE_SUBTITLES and show.subtitles and epResult["location"]: + % if (sickbeard.SUBTITLES_MULTI and subtitles.needs_subtitles(epResult["subtitles"])) or (not sickbeard.SUBTITLES_MULTI and len(subtitles.wanted_languages()) > 0 and "und" not in epResult["subtitles"] and list(subtitles.wanted_languages())[0] not in epResult["subtitles"]): + <a class="epSubtitlesSearch" href="searchEpisodeSubtitles?show=${show.indexerid}&season=${epResult["season"]}&episode=${epResult["episode"]}"><img src="${srRoot}/images/closed_captioning.png" height="16" alt="search subtitles" title="Search Subtitles" /></a> + % endif % endif </td> </tr> diff --git a/gui/slick/views/manage.mako b/gui/slick/views/manage.mako index d667d1e3518ebdd79f39591b11ced9e399c991b1..710a003647ba02702fba8f8f8d3e0a93d6012d39 100644 --- a/gui/slick/views/manage.mako +++ b/gui/slick/views/manage.mako @@ -10,7 +10,7 @@ <%block name="content"> <%namespace file="/inc_defs.mako" import="renderQualityPill"/> - +<form name="massUpdateForm" method="post" action="massUpdate"> <table style="width: 100%;" class="home-header"> <tr> <td nowrap> @@ -35,8 +35,6 @@ </tr> </table> -<form name="massUpdateForm" method="post" action="massUpdate"> - <table id="massUpdateTable" class="tablesorter" cellspacing="1" border="0" cellpadding="0"> <thead> <tr> diff --git a/gui/slick/views/manage_subtitleMissed.mako b/gui/slick/views/manage_subtitleMissed.mako index 25d8442888f380f71b78b1f8afad353b544da957..41e8b838c69de444d242615f88c3c352e8b7a569 100644 --- a/gui/slick/views/manage_subtitleMissed.mako +++ b/gui/slick/views/manage_subtitleMissed.mako @@ -22,13 +22,25 @@ % endif <form action="${srRoot}/manage/subtitleMissed" method="get"> - Manage episodes without <select name="whichSubs" class="form-control form-control-inline input-sm"> - <option value="all">All</option> - % for sub_code in subtitles.wanted_languages(): - <option value="${sub_code}">${subtitles.name_from_code(sub_code)}</option> - % endfor + % if sickbeard.SUBTITLES_MULTI: + Manage episodes without <select name="whichSubs" class="form-control form-control-inline input-sm"> + <option value="all">All</option> + % for sub_code in subtitles.wanted_languages(): + <option value="${sub_code}">${subtitles.name_from_code(sub_code)}</option> + % endfor + % else: + Manage episodes without <select name="whichSubs" class="form-control form-control-inline input-sm"> + % if not subtitles.wanted_languages(): + <option value="all">All</option> + % else: + % for index, sub_code in enumerate(subtitles.wanted_languages()): + % if index == 0: + <option value="und">${subtitles.name_from_code(sub_code)}</option> + % endif + % endfor + % endif </select> - + % endif <input class="btn" type="submit" value="Manage" /> </form> @@ -36,7 +48,15 @@ ##Strange that this is used by js but is an input outside of any form? <input type="hidden" id="selectSubLang" name="selectSubLang" value="${whichSubs}" /> <form action="${srRoot}/manage/downloadSubtitleMissed" method="post"> - <h2>Episodes without ${subsLanguage} subtitles.</h2> + % if sickbeard.SUBTITLES_MULTI: + <h2>Episodes without ${subsLanguage} subtitles.</h2> + % else: + % for index, sub_code in enumerate(subtitles.wanted_languages()): + % if index == 0: + <h2>Episodes without ${subtitles.name_from_code(sub_code)} (undefined) subtitles.</h2> + % endif + % endfor + % endif <br> Download missed subtitles for selected episodes <input class="btn btn-inline" type="submit" value="Go" /> <div> @@ -47,8 +67,8 @@ <table class="sickbeardTable manageTable" cellspacing="1" border="0" cellpadding="0"> % for cur_indexer_id in sorted_show_ids: <tr id="${cur_indexer_id}"> - <th><input type="checkbox" class="allCheck" id="allCheck-${cur_indexer_id}" name="${cur_indexer_id}-all"checked="checked" /></th> - <th colspan="3" style="width: 100%; text-align: left;"><a class="whitelink" href="${srRoot}/home/displayShow?show=${cur_indexer_id}">${show_names[cur_indexer_id]}</a> (${ep_counts[cur_indexer_id]}) <input type="button" class="pull-right get_more_eps btn" id="${cur_indexer_id}" value="Expand" /></th> + <th style="width: 1%;"><input type="checkbox" class="allCheck" id="allCheck-${cur_indexer_id}" name="${cur_indexer_id}-all"checked="checked" /></th> + <th colspan="3" style="text-align: left;"><a class="whitelink" href="${srRoot}/home/displayShow?show=${cur_indexer_id}">${show_names[cur_indexer_id]}</a> (${ep_counts[cur_indexer_id]}) <input type="button" class="pull-right get_more_eps btn" id="${cur_indexer_id}" value="Expand" /></th> </tr> % endfor </table> diff --git a/sickbeard/helpers.py b/sickbeard/helpers.py index 4d86695a3450e09029aa603aaf8d3f43c9ed2814..e1e191302e6455060090e11b006be55d29df5fa2 100644 --- a/sickbeard/helpers.py +++ b/sickbeard/helpers.py @@ -168,25 +168,29 @@ def isMediaFile(filename): """ # ignore samples - if re.search(r'(^|[\W_])(?<!shomin.)(sample\d*)[\W_]', filename, re.I): - return False + try: + if re.search(r'(^|[\W_])(?<!shomin.)(sample\d*)[\W_]', filename, re.I): + return False - # ignore RARBG release intro - if re.search(r'^RARBG\.\w+\.(mp4|avi|txt)$', filename, re.I): - return False + # ignore RARBG release intro + if re.search(r'^RARBG\.\w+\.(mp4|avi|txt)$', filename, re.I): + return False - # ignore MAC OS's retarded "resource fork" files - if filename.startswith('._'): - return False + # ignore MAC OS's retarded "resource fork" files + if filename.startswith('._'): + return False - sepFile = filename.rpartition(".") + sepFile = filename.rpartition(".") - if re.search('extras?$', sepFile[0], re.I): - return False + if re.search('extras?$', sepFile[0], re.I): + return False - if sepFile[2].lower() in media_extensions: - return True - else: + if sepFile[2].lower() in media_extensions: + return True + else: + return False + except TypeError as error: # Not a string + logger.log('Invalid filename. Filename must be a string. %s' % error, logger.DEBUG) # pylint: disable=no-member return False @@ -1282,7 +1286,6 @@ def mapIndexersToShow(showObj): logger.log(u"Found indexer mapping in cache for show: " + showObj.name, logger.DEBUG) mapped[int(curResult['mindexer'])] = int(curResult['mindexer_id']) return mapped - break else: sql_l = [] for indexer in sickbeard.indexerApi().indexers: @@ -1758,6 +1761,7 @@ def getTVDBFromID(indexer_id, indexer): else: return tvdb_id + def is_ip_private(ip): priv_lo = re.compile("^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$") priv_24 = re.compile("^10\.\d{1,3}\.\d{1,3}\.\d{1,3}$") diff --git a/sickbeard/notifiers/pushover.py b/sickbeard/notifiers/pushover.py index a0914e7dd4b392738c51899a0f3d6b1c0952d70a..6c3e22a4ed2933c8290506b61d857b48a1ab9711 100644 --- a/sickbeard/notifiers/pushover.py +++ b/sickbeard/notifiers/pushover.py @@ -25,7 +25,7 @@ import time import sickbeard from sickbeard import logger -from sickbeard.common import notifyStrings, NOTIFY_SNATCH, NOTIFY_DOWNLOAD, NOTIFY_SUBTITLE_DOWNLOAD, NOTIFY_GIT_UPDATE, NOTIFY_GIT_UPDATE_TEXT +from sickbeard.common import notifyStrings, NOTIFY_SNATCH, NOTIFY_DOWNLOAD, NOTIFY_SUBTITLE_DOWNLOAD, NOTIFY_GIT_UPDATE, NOTIFY_GIT_UPDATE_TEXT, NOTIFY_LOGIN_TEXT, NOTIFY_LOGIN from sickrage.helper.exceptions import ex API_URL = "https://api.pushover.net/1/messages.json" @@ -153,8 +153,8 @@ class PushoverNotifier(object): def notify_login(self, ipaddress=""): if sickbeard.USE_PUSHOVER: - update_text = common.notifyStrings[common.NOTIFY_LOGIN_TEXT] - title = common.notifyStrings[common.NOTIFY_LOGIN] + update_text = notifyStrings[NOTIFY_LOGIN_TEXT] + title = notifyStrings[NOTIFY_LOGIN] self._notifyPushover(title, update_text.format(ipaddress)) def _notifyPushover(self, title, message, sound=None, userKey=None, apiKey=None, force=False): diff --git a/sickbeard/processTV.py b/sickbeard/processTV.py index 9e03f21d49f014feba9759be0836f75dfd426319..7943e1b53d226e67d7411af900b9d2a289b4867a 100644 --- a/sickbeard/processTV.py +++ b/sickbeard/processTV.py @@ -28,7 +28,7 @@ from sickbeard.name_parser.parser import NameParser, InvalidNameException, Inval from sickbeard import common from sickbeard import failedProcessor from sickrage.helper.common import is_sync_file, is_torrent_or_nzb_file, subtitle_extensions -from sickrage.helper.encoding import ek +from sickrage.helper.encoding import ek, ss from sickrage.helper.exceptions import EpisodePostProcessingFailedException, ex, FailedPostProcessingFailedException from unrar2 import RarFile @@ -44,7 +44,7 @@ import shutil_custom shutil.copyfile = shutil_custom.copyfile_custom -class ProcessResult(object): +class ProcessResult(object): # pylint: disable=too-few-public-methods def __init__(self): self.result = True self.output = '' @@ -139,7 +139,7 @@ def logHelper(logMessage, logLevel=logger.INFO): return logMessage + u"\n" -def processDir(dirName, nzbName=None, process_method=None, force=False, is_priority=None, delete_on=False, failed=False, proc_type="auto"): +def processDir(dirName, nzbName=None, process_method=None, force=False, is_priority=None, delete_on=False, failed=False, proc_type="auto"): # pylint: disable=too-many-arguments,too-many-branches,too-many-statements,too-many-locals """ Scans through the files in dirName and processes whatever media files it finds @@ -300,7 +300,7 @@ def processDir(dirName, nzbName=None, process_method=None, force=False, is_prior return result.output -def validateDir(path, dirName, nzbNameOriginal, failed, result): +def validateDir(path, dirName, nzbNameOriginal, failed, result): # pylint: disable=too-many-locals,too-many-branches,too-many-return-statements """ Check if directory is valid for processing @@ -312,6 +312,8 @@ def validateDir(path, dirName, nzbNameOriginal, failed, result): :return: True if dir is valid for processing, False if not """ + dirName = ss(dirName) + IGNORED_FOLDERS = ['.AppleDouble', '.@__thumb', '@eaDir'] folder_name = ek(os.path.basename, dirName) if folder_name in IGNORED_FOLDERS: @@ -392,7 +394,7 @@ def validateDir(path, dirName, nzbNameOriginal, failed, result): result.output += logHelper(dirName + " : No processable items found in folder", logger.DEBUG) return False -def unRAR(path, rarFiles, force, result): +def unRAR(path, rarFiles, force, result): # pylint: disable=too-many-branches,too-many-statements """ Extracts RAR files @@ -473,7 +475,7 @@ def unRAR(path, rarFiles, force, result): return unpacked_files -def already_postprocessed(dirName, videofile, force, result): +def already_postprocessed(dirName, videofile, force, result): # pylint: disable=unused-argument """ Check if we already post processed a file @@ -517,7 +519,7 @@ def already_postprocessed(dirName, videofile, force, result): return False -def process_media(processPath, videoFiles, nzbName, process_method, force, is_priority, result): +def process_media(processPath, videoFiles, nzbName, process_method, force, is_priority, result): # pylint: disable=too-many-arguments """ Postprocess mediafiles @@ -585,8 +587,7 @@ def get_path_dir_files(dirName, nzbName, proc_type): break else: path, dirs = ek(os.path.split, dirName) # Script Post Processing - if not nzbName is None and not nzbName.endswith('.nzb') and ek(os.path.isfile, - ek(os.path.join, dirName, nzbName)): # For single torrent file without Dir + if not nzbName is None and not nzbName.endswith('.nzb') and ek(os.path.isfile, ek(os.path.join, dirName, nzbName)): # For single torrent file without Dir dirs = [] files = [ek(os.path.join, dirName, nzbName)] else: diff --git a/sickbeard/providers/hounddawgs.py b/sickbeard/providers/hounddawgs.py index 8bb7c8a1454545e19c1f280317864277c2d00a8d..c5f1fb6202a7b083b2e74c3ac2fc792a455ca6f5 100644 --- a/sickbeard/providers/hounddawgs.py +++ b/sickbeard/providers/hounddawgs.py @@ -21,10 +21,11 @@ import traceback from sickbeard import logger from sickbeard import tvcache from sickbeard.bs4_parser import BS4Parser +from sickrage.helper.common import try_int from sickrage.providers.TorrentProvider import TorrentProvider -class HoundDawgsProvider(TorrentProvider): +class HoundDawgsProvider(TorrentProvider): # pylint: disable=too-many-instance-attributes def __init__(self): @@ -35,12 +36,15 @@ class HoundDawgsProvider(TorrentProvider): self.ratio = None self.minseed = None self.minleech = None + self.freeleech = None + self.ranked = None - self.cache = HoundDawgsCache(self) - self.urls = {'base_url': 'https://hounddawgs.org/', - 'search': 'https://hounddawgs.org/torrents.php', - 'login': 'https://hounddawgs.org/login.php'} + self.urls = { + 'base_url': 'https://hounddawgs.org/', + 'search': 'https://hounddawgs.org/torrents.php', + 'login': 'https://hounddawgs.org/login.php' + } self.url = self.urls['base_url'] @@ -60,12 +64,16 @@ class HoundDawgsProvider(TorrentProvider): "searchtags": '' } + self.cache = HoundDawgsCache(self) + def login(self): - login_params = {'username': self.username, - 'password': self.password, - 'keeplogged': 'on', - 'login': 'Login'} + login_params = { + 'username': self.username, + 'password': self.password, + 'keeplogged': 'on', + 'login': 'Login' + } self.get_url(self.urls['base_url'], timeout=30) response = self.get_url(self.urls['login'], post_data=login_params, timeout=30) @@ -81,7 +89,7 @@ class HoundDawgsProvider(TorrentProvider): return True - def search(self, search_strings, age=0, ep_obj=None): + def search(self, search_strings, age=0, ep_obj=None): # pylint: disable=too-many-locals,too-many-branches,too-many-statements results = [] items = {'Season': [], 'Episode': [], 'RSS': []} @@ -127,25 +135,20 @@ class HoundDawgsProvider(TorrentProvider): allAs = (torrent[1]).find_all('a') try: - # link = self.urls['base_url'] + allAs[2].attrs['href'] - # url = result.find('td', attrs={'class': 'quickdownload'}).find('a') + notinternal = result.find('img', src='/static//common/user_upload.png') + if self.ranked and notinternal: + logger.log(u"Found a user uploaded release, Ignoring it..", logger.DEBUG) + continue + freeleech = result.find('img', src='/static//common/browse/freeleech.png') + if self.freeleech and not freeleech: + continue title = allAs[2].string - # Trimming title so accepted by scene check(Feature has been rewuestet i forum) - title = title.replace("custom.", "") - title = title.replace("CUSTOM.", "") - title = title.replace("Custom.", "") - title = title.replace("dk", "") - title = title.replace("DK", "") - title = title.replace("Dk", "") - title = title.replace("subs.", "") - title = title.replace("SUBS.", "") - title = title.replace("Subs.", "") - download_url = self.urls['base_url']+allAs[0].attrs['href'] - # FIXME - size = -1 - seeders = 1 - leechers = 0 + torrent_size = result.find("td", class_="nobr").find_next_sibling("td").string + if torrent_size: + size = self._convertSize(torrent_size) + seeders = try_int((result.findAll('td')[6]).text) + leechers = try_int((result.findAll('td')[7]).text) except (AttributeError, TypeError): continue @@ -154,10 +157,10 @@ class HoundDawgsProvider(TorrentProvider): continue # Filter unseeded torrent - # if seeders < self.minseed or leechers < self.minleech: - # if mode != 'RSS': - # logger.log(u"Discarding torrent because it doesn't meet the minimum seeders or leechers: {0} (S:{1} L:{2})".format(title, seeders, leechers), logger.DEBUG) - # continue + if seeders < self.minseed or leechers < self.minleech: + if mode != 'RSS': + logger.log(u"Discarding torrent because it doesn't meet the minimum seeders or leechers: {0} (S:{1} L:{2})".format(title, seeders, leechers), logger.DEBUG) + continue item = title, download_url, size, seeders, leechers if mode != 'RSS': @@ -165,7 +168,7 @@ class HoundDawgsProvider(TorrentProvider): items[mode].append(item) - except Exception, e: + except Exception: logger.log(u"Failed parsing provider. Traceback: %s" % traceback.format_exc(), logger.ERROR) # For each search mode sort all the items by seeders if available @@ -175,6 +178,21 @@ class HoundDawgsProvider(TorrentProvider): return results + + @staticmethod + def _convertSize(size): + size = re.sub(r'[i, ]+', '', size) + matches = re.match(r'([\d.]+)([TGMK])', size.strip().upper()) + if not matches: + return -1 + + size = matches.group(1) + modifier = matches.group(2) + + mod = {'K': 1, 'M': 2, 'G': 3, 'T': 4} + return float(size) * 1024**mod[modifier] + + def seed_ratio(self): return self.ratio diff --git a/sickbeard/providers/tvchaosuk.py b/sickbeard/providers/tvchaosuk.py index 7542bb2c611f02b0fb53216185e435a3e46fee7b..f719454084ec318dcc8690702efca5e8d8b23c07 100644 --- a/sickbeard/providers/tvchaosuk.py +++ b/sickbeard/providers/tvchaosuk.py @@ -154,7 +154,7 @@ class TVChaosUKProvider(TorrentProvider): freeleech = torrent.find('img', alt=re.compile('Free Torrent')) if self.freeleech and not freeleech: continue - title = torrent.find(attrs={'class':'tooltip-target'}).text.strip() + title = (torrent.find(attrs={'class':'tooltip-target'}).text.strip()).replace("mp4", "x264") download_url = torrent.find(title="Click to Download this Torrent!").parent['href'].strip() seeders = int(torrent.find(title='Seeders').text.strip()) leechers = int(torrent.find(title='Leechers').text.strip()) diff --git a/sickbeard/subtitles.py b/sickbeard/subtitles.py index d568aa6d0e27abe3370753b7c9e823de401b135b..a7fab4ccdc17f97f44658ff57e6286aac2444dc8 100644 --- a/sickbeard/subtitles.py +++ b/sickbeard/subtitles.py @@ -32,6 +32,7 @@ from sickbeard import logger from sickbeard import history from sickbeard import db from sickbeard import processTV +from sickbeard.helpers import remove_non_release_groups from sickrage.helper.common import media_extensions, dateTimeFormat from sickrage.helper.encoding import ek from sickrage.helper.exceptions import ex @@ -123,13 +124,13 @@ def subtitle_code_filter(): def needs_subtitles(subtitles): - if isinstance(subtitles, basestring): + if isinstance(subtitles, basestring) and sickbeard.SUBTITLES_MULTI: subtitles = {subtitle.strip() for subtitle in subtitles.split(',')} if sickbeard.SUBTITLES_MULTI: return len(wanted_languages().difference(subtitles)) > 0 - else: - return len(subtitles) == 0 + elif 'und' not in subtitles: + return True # Hack around this for now. @@ -153,8 +154,8 @@ def download_subtitles(subtitles_info): existing_subtitles = subtitles_info['subtitles'] if not needs_subtitles(existing_subtitles): - logger.log(u'Episode already has all needed subtitles, skipping episode %dx%d of show %s' - % (subtitles_info['season'], subtitles_info['episode'], subtitles_info['show_name']), logger.DEBUG) + logger.log(u'Episode already has all needed subtitles, skipping %s S%02dE%02d' + % (subtitles_info['show_name'], subtitles_info['season'], subtitles_info['episode']), logger.DEBUG) return (existing_subtitles, None) # Check if we really need subtitles @@ -333,6 +334,14 @@ class SubtitlesFinder(object): if sickbeard.TV_DOWNLOAD_DIR and ek(os.path.isdir, sickbeard.TV_DOWNLOAD_DIR): for root, _, files in ek(os.walk, sickbeard.TV_DOWNLOAD_DIR, topdown=False): for video_filename in sorted(files): + try: + # Remove non release groups from video file. Needed to match subtitles + new_video_filename = remove_non_release_groups(video_filename) + if new_video_filename != video_filename: + os.rename(video_filename, new_video_filename) + video_filename = new_video_filename + except Exception as e: + logger.log(u'Could not remove non release groups from video file. Error: %r' % ex(e), logger.DEBUG) if video_filename.rsplit(".", 1)[1] in media_extensions: try: video = subliminal.scan_video(os.path.join(root, video_filename), @@ -429,13 +438,13 @@ class SubtitlesFinder(object): for ep_to_sub in sql_results: if not ek(os.path.isfile, ep_to_sub['location']): - logger.log(u'Episode file does not exist, cannot download subtitles for episode %dx%d of show %s' - % (ep_to_sub['season'], ep_to_sub['episode'], ep_to_sub['show_name']), logger.DEBUG) + logger.log(u'Episode file does not exist, cannot download subtitles for %s S%02dE%02d' + % (ep_to_sub['show_name'], ep_to_sub['season'], ep_to_sub['episode']), logger.DEBUG) continue if not needs_subtitles(ep_to_sub['subtitles']): - logger.log(u'Episode already has all needed subtitles, skipping episode %dx%d of show %s' - % (ep_to_sub['season'], ep_to_sub['episode'], ep_to_sub['show_name']), logger.DEBUG) + logger.log(u'Episode already has all needed subtitles, skipping %s S%02dE%02d' + % (ep_to_sub['show_name'], ep_to_sub['season'], ep_to_sub['episode']), logger.DEBUG) continue # http://bugs.python.org/issue7980#msg221094 @@ -448,18 +457,19 @@ class SubtitlesFinder(object): now - datetime.datetime.strptime(ep_to_sub['lastsearch'], dateTimeFormat) > datetime.timedelta(hours=rules['new'][ep_to_sub['searchcount']]))): - logger.log(u'Downloading subtitles for episode %dx%d of show %s' - % (ep_to_sub['season'], ep_to_sub['episode'], ep_to_sub['show_name']), logger.DEBUG) + logger.log(u'Downloading subtitles for %s S%02dE%02d' + % (ep_to_sub['show_name'], ep_to_sub['season'], ep_to_sub['episode']), logger.DEBUG) show_object = Show.find(sickbeard.showList, int(ep_to_sub['showid'])) if not show_object: - logger.log(u'Show not found', logger.DEBUG) + logger.log(u'Show with ID %s not found in the database' % ep_to_sub['showid'], logger.DEBUG) self.amActive = False return episode_object = show_object.getEpisode(int(ep_to_sub["season"]), int(ep_to_sub["episode"])) if isinstance(episode_object, str): - logger.log(u'Episode not found', logger.DEBUG) + logger.log(u'%s S%02dE%02d not found in the database' + % (ep_to_sub['show_name'], ep_to_sub['season'], ep_to_sub['episode']), logger.DEBUG) self.amActive = False return @@ -468,15 +478,16 @@ class SubtitlesFinder(object): try: episode_object.download_subtitles() except Exception as error: - logger.log(u'Unable to find subtitles', logger.DEBUG) + logger.log(u'Unable to find subtitles for %s S%02dE%02d' + % (ep_to_sub['show_name'], ep_to_sub['season'], ep_to_sub['episode']), logger.DEBUG) logger.log(str(error), logger.DEBUG) self.amActive = False return new_subtitles = frozenset(episode_object.subtitles).difference(existing_subtitles) if new_subtitles: - logger.log(u'Downloaded subtitles for S%02dE%02d in %s' - % (ep_to_sub["season"], ep_to_sub["episode"], ', '.join(new_subtitles))) + logger.log(u'Downloaded %s subtitles for %s S%02dE%02d' + % (', '.join(new_subtitles), ep_to_sub['show_name'], ep_to_sub["season"], ep_to_sub["episode"])) self.amActive = False diff --git a/tests/helpers_tests.py b/tests/helpers_tests.py index 85b3aac84369ea63070136f864bd727ea407885d..1ec1cb4605e726317f0c03b068c1dc6b1846f8a0 100755 --- a/tests/helpers_tests.py +++ b/tests/helpers_tests.py @@ -21,7 +21,7 @@ """ Test sickbeard.helpers -Methods: +Public Methods: fixGlob indentXML remove_non_release_groups @@ -60,7 +60,6 @@ Methods: encrypt decrypt full_sanitizeSceneName - _check_against_names get_show is_hidden_folder real_path @@ -72,8 +71,6 @@ Methods: restoreConfigZip mapIndexersToShow touchFile - _getTempDir - _setUpSession getURL download_file get_size @@ -84,8 +81,13 @@ Methods: pretty_time_delta isFileLocked getDiskSpaceUsage +Private Methods: + _check_against_names + _getTempDir + _setUpSession """ +from __future__ import print_function import os.path import sys import unittest @@ -93,7 +95,8 @@ import unittest sys.path.insert(1, os.path.abspath(os.path.join(os.path.dirname(__file__), '../lib'))) sys.path.insert(1, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) -from sickbeard.helpers import remove_non_release_groups +from sickbeard import helpers +from sickrage.helper.common import media_extensions, subtitle_extensions TEST_RESULT = 'Show.Name.S01E01.HDTV.x264-RLSGROUP' TEST_CASES = { @@ -163,7 +166,7 @@ def test_generator(test_strings): :return: test to run """ for test_string in test_strings: - self.assertEqual(remove_non_release_groups(test_string), TEST_RESULT) + self.assertEqual(helpers.remove_non_release_groups(test_string), TEST_RESULT) return _test @@ -258,12 +261,53 @@ class HelpersFileTests(unittest.TestCase): """ Test file helpers """ - @unittest.skip('Not yet implemented') + def test_is_media_file(self): """ Test isMediaFile """ - pass + # TODO: Add unicode tests + # TODO: Add MAC OS resource fork tests + # TODO: Add RARBG release tests + # RARBG release intros should be ignored + # MAC OS's "resource fork" files should be ignored + # Extras should be ignored + # and the file extension should be in the list of media extensions + + # Test all valid media extensions + temp_name = 'Show.Name.S01E01.HDTV.x264-RLSGROUP' + extension_tests = {'.'.join((temp_name, ext)): True for ext in media_extensions} + # ...and some invalid ones + other_extensions = ['txt', 'sfv', 'srr', 'rar', 'nfo', 'zip'] + extension_tests.update({'.'.join((temp_name, ext)): False for ext in other_extensions + subtitle_extensions}) + + # Samples should be ignored + sample_tests = { # Samples should be ignored, valid samples will return False + 'Show.Name.S01E01.HDTV.sample.mkv': False, # default case + 'Show.Name.S01E01.HDTV.sAmPle.mkv': False, # Ignore case + 'Show.Name.S01E01.HDTV.samples.mkv': True, # sample should not be plural + 'Show.Name.S01E01.HDTVsample.mkv': True, # no separation, can't identify as sample + 'Sample.Show.Name.S01E01.HDTV.mkv': False, # location doesn't matter + 'Show.Name.Sample.S01E01.HDTV.sample.mkv': False, # location doesn't matter + 'Show.Name.S01E01.HDTV.sample1.mkv': False, # numbered samples are ok + 'Show.Name.S01E01.HDTV.sample12.mkv': False, # numbered samples are ok + 'Show.Name.S01E01.HDTV.sampleA.mkv': True, # samples should not be indexed alphabetically + } + + edge_cases = { + None: False, + '': False, + 0: False, + 1: False, + 42: False, + 123189274981274: False, + 12.23: False, + ('this', 'is', 'a tuple'): False, + } + + for cur_test in extension_tests, sample_tests, edge_cases: + for cur_name, expected_result in cur_test.items(): + self.assertEqual(helpers.isMediaFile(cur_name), expected_result, cur_name) @unittest.skip('Not yet implemented') def test_is_file_locked(self): @@ -640,12 +684,11 @@ class HelpersMiscTests(unittest.TestCase): """ pass - if __name__ == '__main__': - print "==================" - print "STARTING - Helpers TESTS" - print "==================" - print "######################################################################" + print("==================") + print("STARTING - Helpers TESTS") + print("==================") + print("######################################################################") for name, test_data in TEST_CASES.items(): test_name = 'test_%s' % name test = test_generator(test_data)