1
0
mirror of https://github.com/moparisthebest/SickRage synced 2024-12-13 11:32:20 -05:00

Merge branch 'origin/dev'

This commit is contained in:
echel0n 2014-05-08 10:14:45 -07:00
commit 29fda0f7be
40 changed files with 554 additions and 309 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 571 B

View File

@ -799,6 +799,59 @@
</fieldset> </fieldset>
</div><!-- /boxcar component-group //--> </div><!-- /boxcar component-group //-->
<div class="component-group clearfix">
<div class="component-group-desc">
<h3><a href="https://new.boxcar.io/" rel="noreferrer" onclick="window.open('${sickbeard.ANON_REDIRECT}' + this.href, '_blank'); return false;"><img src="$sbRoot/images/notifiers/boxcar2.png" alt="" title="Boxcar2"/> Boxcar2 </a></h3>
<p>Read your messages where and when you want them!</p>
</div>
<fieldset class="component-group-list">
<div class="field-pair">
<input type="checkbox" class="enabler" name="use_boxcar2" id="use_boxcar2" #if $sickbeard.USE_BOXCAR2 then "checked=\"checked\"" else ""# />
<label class="clearfix" for="use_boxcar2">
<span class="component-title">Enable</span>
<span class="component-desc">Should Sick Beard send notifications through Boxcar2?</span>
</label>
</div>
<div id="content_use_boxcar2">
<div class="field-pair">
<input type="checkbox" name="boxcar2_notify_onsnatch" id="boxcar2_notify_onsnatch" #if $sickbeard.BOXCAR2_NOTIFY_ONSNATCH then "checked=\"checked\"" else ""# />
<label class="clearfix" for="boxcar2_notify_onsnatch">
<span class="component-title">Notify on Snatch</span>
<span class="component-desc">Send notification when we start a download?</span>
</label>
</div>
<div class="field-pair">
<input type="checkbox" name="boxcar2_notify_ondownload" id="boxcar2_notify_ondownload" #if $sickbeard.BOXCAR2_NOTIFY_ONDOWNLOAD then "checked=\"checked\"" else ""# />
<label class="clearfix" for="boxcar2_notify_ondownload">
<span class="component-title">Notify on Download</span>
<span class="component-desc">Send notification when we finish a download?</span>
</label>
</div>
<div class="field-pair">
<input type="checkbox" name="boxcar2_notify_onsubtitledownload" id="boxcar2_notify_onsubtitledownload" #if $sickbeard.BOXCAR2_NOTIFY_ONSUBTITLEDOWNLOAD then "checked=\"checked\"" else ""# />
<label class="clearfix" for="boxcar2_notify_onsubtitledownload">
<span class="component-title">Notify on Subtitle Download</span>
<span class="component-desc">Send notification when we finish a subtitle download?</span>
</label>
</div>
<div class="field-pair">
<label class="nocheck clearfix">
<span class="component-title">Boxcar2 Access Token</span>
<input type="text" name="boxcar2_accesstoken" id="boxcar2_accesstoken" value="$sickbeard.BOXCAR2_ACCESSTOKEN" size="35" />
</label>
<label class="nocheck clearfix">
<span class="component-title">&nbsp;</span>
<span class="component-desc">Access Token for your Boxcar2 account</span>
</label>
</div>
<div class="testNotification" id="testBoxcar2-result">Click below to test.</div>
<input class="btn" type="button" value="Test Boxcar2" id="testBoxcar2" />
<input type="submit" class="config_submitter btn" value="Save Changes" />
</div><!-- /content_use_boxcar2 //-->
</fieldset>
</div><!-- /boxcar2 component-group //-->
<div class="component-group clearfix"> <div class="component-group clearfix">
<div class="component-group-desc"> <div class="component-group-desc">

View File

@ -53,9 +53,11 @@
<a href="$sickbeard.indexerApi($show.indexer).config["show_url"]$show.indexerid" onclick="window.open(this.href, '_blank'); return false;" title="$sickbeard.indexerApi($show.indexer).config["show_url"]$show.indexerid"><img alt="$sickbeard.indexerApi($show.indexer).name" height="16" width="16" src="$sbRoot/images/$sickbeard.indexerApi($show.indexer).config["icon"] "style="margin-top: -1px;"/></a> <a href="$sickbeard.indexerApi($show.indexer).config["show_url"]$show.indexerid" onclick="window.open(this.href, '_blank'); return false;" title="$sickbeard.indexerApi($show.indexer).config["show_url"]$show.indexerid"><img alt="$sickbeard.indexerApi($show.indexer).name" height="16" width="16" src="$sbRoot/images/$sickbeard.indexerApi($show.indexer).config["icon"] "style="margin-top: -1px;"/></a>
</span> </span>
#else #else
#for $country in $show.imdb_info['country_codes'].split('|') #if 'country_codes' in $show.imdb_info:
<img src="$sbRoot/images/flags/${$country}.png" width="16" height="11" style="margin-top: 3px; margin-left: 3px" /> #for $country in $show.imdb_info['country_codes'].split('|')
#end for <img src="$sbRoot/images/flags/${$country}.png" width="16" height="11" style="margin-top: 3px; margin-left: 3px" />
#end for
#end if
($show.imdb_info['year']) - $show.imdb_info['runtimes'] min - $show.imdb_info['genres'].replace('|',' | ') ($show.imdb_info['year']) - $show.imdb_info['runtimes'] min - $show.imdb_info['genres'].replace('|',' | ')
<span class="tvshowLink" style="vertical-align: text-top"> <span class="tvshowLink" style="vertical-align: text-top">
<a href="http://www.imdb.com/title/$show.imdbid" rel="noreferrer" onclick="window.open('${sickbeard.ANON_REDIRECT}' + this.href, '_blank'); return false;" title="http://www.imdb.com/title/$show.imdbid"><img alt="[imdb]" height="16" width="16" src="$sbRoot/images/imdb.png" style="margin-top: -1px;"/> <a href="http://www.imdb.com/title/$show.imdbid" rel="noreferrer" onclick="window.open('${sickbeard.ANON_REDIRECT}' + this.href, '_blank'); return false;" title="http://www.imdb.com/title/$show.imdbid"><img alt="[imdb]" height="16" width="16" src="$sbRoot/images/imdb.png" style="margin-top: -1px;"/>

View File

@ -47,6 +47,13 @@ $(document).ready(function(){
function (data) { $('#testBoxcar-result').html(data); }); function (data) { $('#testBoxcar-result').html(data); });
}); });
$('#testBoxcar2').click(function() {
$('#testBoxcar2-result').html(loading);
var boxcar2_accesstoken = $("#boxcar2_accesstoken").val();
$.get(sbRoot + "/home/testBoxcar2", {'accesstoken': boxcar2_accesstoken},
function (data) { $('#testBoxcar2-result').html(data); });
});
$('#testPushover').click(function() { $('#testPushover').click(function() {
$('#testPushover-result').html(loading); $('#testPushover-result').html(loading);
var pushover_userkey = $("#pushover_userkey").val(); var pushover_userkey = $("#pushover_userkey").val();

View File

@ -79,6 +79,7 @@ showUpdateScheduler = None
versionCheckScheduler = None versionCheckScheduler = None
showQueueScheduler = None showQueueScheduler = None
searchQueueScheduler = None searchQueueScheduler = None
snatchQueueScheduler = None
properFinderScheduler = None properFinderScheduler = None
autoPostProcesserScheduler = None autoPostProcesserScheduler = None
subtitlesFinderScheduler = None subtitlesFinderScheduler = None
@ -342,6 +343,12 @@ BOXCAR_USERNAME = None
BOXCAR_PASSWORD = None BOXCAR_PASSWORD = None
BOXCAR_PREFIX = None BOXCAR_PREFIX = None
USE_BOXCAR2 = False
BOXCAR2_NOTIFY_ONSNATCH = False
BOXCAR2_NOTIFY_ONDOWNLOAD = False
BOXCAR2_NOTIFY_ONSUBTITLEDOWNLOAD = False
BOXCAR2_ACCESSTOKEN = None
USE_PUSHOVER = False USE_PUSHOVER = False
PUSHOVER_NOTIFY_ONSNATCH = False PUSHOVER_NOTIFY_ONSNATCH = False
PUSHOVER_NOTIFY_ONDOWNLOAD = False PUSHOVER_NOTIFY_ONDOWNLOAD = False
@ -489,12 +496,13 @@ def initialize(consoleLogging=True):
USE_PUSHBULLET, PUSHBULLET_NOTIFY_ONSNATCH, PUSHBULLET_NOTIFY_ONDOWNLOAD, PUSHBULLET_NOTIFY_ONSUBTITLEDOWNLOAD, PUSHBULLET_API, PUSHBULLET_DEVICE, \ USE_PUSHBULLET, PUSHBULLET_NOTIFY_ONSNATCH, PUSHBULLET_NOTIFY_ONDOWNLOAD, PUSHBULLET_NOTIFY_ONSUBTITLEDOWNLOAD, PUSHBULLET_API, PUSHBULLET_DEVICE, \
versionCheckScheduler, VERSION_NOTIFY, AUTO_UPDATE, PROCESS_AUTOMATICALLY, UNPACK, \ versionCheckScheduler, VERSION_NOTIFY, AUTO_UPDATE, PROCESS_AUTOMATICALLY, UNPACK, \
KEEP_PROCESSED_DIR, PROCESS_METHOD, TV_DOWNLOAD_DIR, MIN_SEARCH_FREQUENCY, DEFAULT_UPDATE_FREQUENCY,MIN_UPDATE_FREQUENCY,UPDATE_FREQUENCY,\ KEEP_PROCESSED_DIR, PROCESS_METHOD, TV_DOWNLOAD_DIR, MIN_SEARCH_FREQUENCY, DEFAULT_UPDATE_FREQUENCY,MIN_UPDATE_FREQUENCY,UPDATE_FREQUENCY,\
showQueueScheduler, searchQueueScheduler, ROOT_DIRS, CACHE_DIR, ACTUAL_CACHE_DIR, \ showQueueScheduler, searchQueueScheduler, snatchQueueScheduler, ROOT_DIRS, CACHE_DIR, ACTUAL_CACHE_DIR, \
NAMING_PATTERN, NAMING_MULTI_EP, NAMING_FORCE_FOLDERS, NAMING_ABD_PATTERN, NAMING_CUSTOM_ABD, NAMING_SPORTS_PATTERN, NAMING_CUSTOM_SPORTS, NAMING_STRIP_YEAR, \ NAMING_PATTERN, NAMING_MULTI_EP, NAMING_FORCE_FOLDERS, NAMING_ABD_PATTERN, NAMING_CUSTOM_ABD, NAMING_SPORTS_PATTERN, NAMING_CUSTOM_SPORTS, NAMING_STRIP_YEAR, \
RENAME_EPISODES, properFinderScheduler, PROVIDER_ORDER, autoPostProcesserScheduler, \ RENAME_EPISODES, properFinderScheduler, PROVIDER_ORDER, autoPostProcesserScheduler, \
WOMBLE, OMGWTFNZBS, OMGWTFNZBS_USERNAME, OMGWTFNZBS_APIKEY, providerList, newznabProviderList, torrentRssProviderList, \ WOMBLE, OMGWTFNZBS, OMGWTFNZBS_USERNAME, OMGWTFNZBS_APIKEY, providerList, newznabProviderList, torrentRssProviderList, \
EXTRA_SCRIPTS, USE_TWITTER, TWITTER_USERNAME, TWITTER_PASSWORD, TWITTER_PREFIX, \ EXTRA_SCRIPTS, USE_TWITTER, TWITTER_USERNAME, TWITTER_PASSWORD, TWITTER_PREFIX, \
USE_BOXCAR, BOXCAR_USERNAME, BOXCAR_PASSWORD, BOXCAR_NOTIFY_ONDOWNLOAD, BOXCAR_NOTIFY_ONSUBTITLEDOWNLOAD, BOXCAR_NOTIFY_ONSNATCH, \ USE_BOXCAR, BOXCAR_USERNAME, BOXCAR_PASSWORD, BOXCAR_NOTIFY_ONDOWNLOAD, BOXCAR_NOTIFY_ONSUBTITLEDOWNLOAD, BOXCAR_NOTIFY_ONSNATCH, \
USE_BOXCAR2, BOXCAR2_ACCESSTOKEN, BOXCAR2_NOTIFY_ONDOWNLOAD, BOXCAR2_NOTIFY_ONSUBTITLEDOWNLOAD, BOXCAR2_NOTIFY_ONSNATCH, \
USE_PUSHOVER, PUSHOVER_USERKEY, PUSHOVER_NOTIFY_ONDOWNLOAD, PUSHOVER_NOTIFY_ONSUBTITLEDOWNLOAD, PUSHOVER_NOTIFY_ONSNATCH, \ USE_PUSHOVER, PUSHOVER_USERKEY, PUSHOVER_NOTIFY_ONDOWNLOAD, PUSHOVER_NOTIFY_ONSUBTITLEDOWNLOAD, PUSHOVER_NOTIFY_ONSNATCH, \
USE_LIBNOTIFY, LIBNOTIFY_NOTIFY_ONSNATCH, LIBNOTIFY_NOTIFY_ONDOWNLOAD, LIBNOTIFY_NOTIFY_ONSUBTITLEDOWNLOAD, USE_NMJ, NMJ_HOST, NMJ_DATABASE, NMJ_MOUNT, USE_NMJv2, NMJv2_HOST, NMJv2_DATABASE, NMJv2_DBLOC, USE_SYNOINDEX, \ USE_LIBNOTIFY, LIBNOTIFY_NOTIFY_ONSNATCH, LIBNOTIFY_NOTIFY_ONDOWNLOAD, LIBNOTIFY_NOTIFY_ONSUBTITLEDOWNLOAD, USE_NMJ, NMJ_HOST, NMJ_DATABASE, NMJ_MOUNT, USE_NMJv2, NMJv2_HOST, NMJv2_DATABASE, NMJv2_DBLOC, USE_SYNOINDEX, \
USE_SYNOLOGYNOTIFIER, SYNOLOGYNOTIFIER_NOTIFY_ONSNATCH, SYNOLOGYNOTIFIER_NOTIFY_ONDOWNLOAD, SYNOLOGYNOTIFIER_NOTIFY_ONSUBTITLEDOWNLOAD, \ USE_SYNOLOGYNOTIFIER, SYNOLOGYNOTIFIER_NOTIFY_ONSNATCH, SYNOLOGYNOTIFIER_NOTIFY_ONDOWNLOAD, SYNOLOGYNOTIFIER_NOTIFY_ONSUBTITLEDOWNLOAD, \
@ -519,6 +527,8 @@ def initialize(consoleLogging=True):
CheckSection(CFG, 'Growl') CheckSection(CFG, 'Growl')
CheckSection(CFG, 'Prowl') CheckSection(CFG, 'Prowl')
CheckSection(CFG, 'Twitter') CheckSection(CFG, 'Twitter')
CheckSection(CFG, 'Boxcar')
CheckSection(CFG, 'Boxcar2')
CheckSection(CFG, 'NMJ') CheckSection(CFG, 'NMJ')
CheckSection(CFG, 'NMJv2') CheckSection(CFG, 'NMJv2')
CheckSection(CFG, 'Synology') CheckSection(CFG, 'Synology')
@ -821,6 +831,12 @@ def initialize(consoleLogging=True):
BOXCAR_NOTIFY_ONSUBTITLEDOWNLOAD = bool(check_setting_int(CFG, 'Boxcar', 'boxcar_notify_onsubtitledownload', 0)) BOXCAR_NOTIFY_ONSUBTITLEDOWNLOAD = bool(check_setting_int(CFG, 'Boxcar', 'boxcar_notify_onsubtitledownload', 0))
BOXCAR_USERNAME = check_setting_str(CFG, 'Boxcar', 'boxcar_username', '') BOXCAR_USERNAME = check_setting_str(CFG, 'Boxcar', 'boxcar_username', '')
USE_BOXCAR2 = bool(check_setting_int(CFG, 'Boxcar2', 'use_boxcar2', 0))
BOXCAR2_NOTIFY_ONSNATCH = bool(check_setting_int(CFG, 'Boxcar2', 'boxcar2_notify_onsnatch', 0))
BOXCAR2_NOTIFY_ONDOWNLOAD = bool(check_setting_int(CFG, 'Boxcar2', 'boxcar2_notify_ondownload', 0))
BOXCAR2_NOTIFY_ONSUBTITLEDOWNLOAD = bool(check_setting_int(CFG, 'Boxcar2', 'boxcar2_notify_onsubtitledownload', 0))
BOXCAR2_ACCESSTOKEN = check_setting_str(CFG, 'Boxcar2', 'boxcar2_accesstoken', '')
USE_PUSHOVER = bool(check_setting_int(CFG, 'Pushover', 'use_pushover', 0)) USE_PUSHOVER = bool(check_setting_int(CFG, 'Pushover', 'use_pushover', 0))
PUSHOVER_NOTIFY_ONSNATCH = bool(check_setting_int(CFG, 'Pushover', 'pushover_notify_onsnatch', 0)) PUSHOVER_NOTIFY_ONSNATCH = bool(check_setting_int(CFG, 'Pushover', 'pushover_notify_onsnatch', 0))
PUSHOVER_NOTIFY_ONDOWNLOAD = bool(check_setting_int(CFG, 'Pushover', 'pushover_notify_ondownload', 0)) PUSHOVER_NOTIFY_ONDOWNLOAD = bool(check_setting_int(CFG, 'Pushover', 'pushover_notify_ondownload', 0))
@ -1124,7 +1140,7 @@ def start():
def halt(): def halt():
global __INITIALIZED__, currentSearchScheduler, backlogSearchScheduler, showUpdateScheduler, \ global __INITIALIZED__, currentSearchScheduler, backlogSearchScheduler, showUpdateScheduler, \
showQueueScheduler, properFinderScheduler, autoPostProcesserScheduler, searchQueueScheduler, \ showQueueScheduler, properFinderScheduler, autoPostProcesserScheduler, searchQueueScheduler, \
subtitlesFinderScheduler, started, \ subtitlesFinderScheduler, snatchQueueScheduler, started, \
traktWatchListCheckerSchedular traktWatchListCheckerSchedular
with INIT_LOCK: with INIT_LOCK:
@ -1177,6 +1193,13 @@ def halt():
except: except:
pass pass
snatchQueueScheduler.abort = True
logger.log(u"Waiting for the SNATCHQUEUE thread to exit")
try:
snatchQueueScheduler.thread.join(10)
except:
pass
autoPostProcesserScheduler.abort = True autoPostProcesserScheduler.abort = True
logger.log(u"Waiting for the POSTPROCESSER thread to exit") logger.log(u"Waiting for the POSTPROCESSER thread to exit")
try: try:
@ -1335,6 +1358,7 @@ def save_config():
new_config['General']['download_propers'] = int(DOWNLOAD_PROPERS) new_config['General']['download_propers'] = int(DOWNLOAD_PROPERS)
new_config['General']['prefer_episode_releases'] = int(PREFER_EPISODE_RELEASES) new_config['General']['prefer_episode_releases'] = int(PREFER_EPISODE_RELEASES)
new_config['General']['allow_high_priority'] = int(ALLOW_HIGH_PRIORITY) new_config['General']['allow_high_priority'] = int(ALLOW_HIGH_PRIORITY)
new_config['General']['backlog_startup'] = int(BACKLOG_STARTUP)
new_config['General']['quality_default'] = int(QUALITY_DEFAULT) new_config['General']['quality_default'] = int(QUALITY_DEFAULT)
new_config['General']['status_default'] = int(STATUS_DEFAULT) new_config['General']['status_default'] = int(STATUS_DEFAULT)
new_config['General']['flatten_folders_default'] = int(FLATTEN_FOLDERS_DEFAULT) new_config['General']['flatten_folders_default'] = int(FLATTEN_FOLDERS_DEFAULT)
@ -1563,6 +1587,13 @@ def save_config():
new_config['Boxcar']['boxcar_notify_onsubtitledownload'] = int(BOXCAR_NOTIFY_ONSUBTITLEDOWNLOAD) new_config['Boxcar']['boxcar_notify_onsubtitledownload'] = int(BOXCAR_NOTIFY_ONSUBTITLEDOWNLOAD)
new_config['Boxcar']['boxcar_username'] = BOXCAR_USERNAME new_config['Boxcar']['boxcar_username'] = BOXCAR_USERNAME
new_config['Boxcar2'] = {}
new_config['Boxcar2']['use_boxcar2'] = int(USE_BOXCAR2)
new_config['Boxcar2']['boxcar2_notify_onsnatch'] = int(BOXCAR2_NOTIFY_ONSNATCH)
new_config['Boxcar2']['boxcar2_notify_ondownload'] = int(BOXCAR2_NOTIFY_ONDOWNLOAD)
new_config['Boxcar2']['boxcar2_notify_onsubtitledownload'] = int(BOXCAR2_NOTIFY_ONSUBTITLEDOWNLOAD)
new_config['Boxcar2']['boxcar2_accesstoken'] = BOXCAR2_ACCESSTOKEN
new_config['Pushover'] = {} new_config['Pushover'] = {}
new_config['Pushover']['use_pushover'] = int(USE_PUSHOVER) new_config['Pushover']['use_pushover'] = int(USE_PUSHOVER)
new_config['Pushover']['pushover_notify_onsnatch'] = int(PUSHOVER_NOTIFY_ONSNATCH) new_config['Pushover']['pushover_notify_onsnatch'] = int(PUSHOVER_NOTIFY_ONSNATCH)

View File

@ -56,7 +56,6 @@ def foldersAtPath(path, includeParent=False):
# walk up the tree until we find a valid path # walk up the tree until we find a valid path
while path and not os.path.isdir(path): while path and not os.path.isdir(path):
time.sleep(0.01)
if path == os.path.dirname(path): if path == os.path.dirname(path):
path = '' path = ''
break break

View File

@ -399,7 +399,6 @@ class ConfigMigrator():
sickbeard.CONFIG_VERSION = self.config_version sickbeard.CONFIG_VERSION = self.config_version
while self.config_version < self.expected_config_version: while self.config_version < self.expected_config_version:
time.sleep(0.01)
next_version = self.config_version + 1 next_version = self.config_version + 1
if next_version in self.migration_names: if next_version in self.migration_names:

View File

@ -79,7 +79,6 @@ class DBConnection:
attempt = 0 attempt = 0
while attempt < 5: while attempt < 5:
time.sleep(0.01)
try: try:
if args == None: if args == None:
logger.log(self.filename + ": " + query, logger.DB) logger.log(self.filename + ": " + query, logger.DB)
@ -119,7 +118,6 @@ class DBConnection:
attempt = 0 attempt = 0
while attempt < 5: while attempt < 5:
time.sleep(0.01)
try: try:
for qu in querylist: for qu in querylist:
if len(qu) == 1: if len(qu) == 1:
@ -164,7 +162,6 @@ class DBConnection:
attempt = 0 attempt = 0
while attempt < 5: while attempt < 5:
time.sleep(0.01)
try: try:
if args == None: if args == None:
logger.log(self.filename + ": " + query, logger.DB) logger.log(self.filename + ": " + query, logger.DB)

View File

@ -18,30 +18,22 @@
import datetime import datetime
import threading import threading
import Queue
from sickbeard import logger from sickbeard import logger
class QueuePriorities: class QueuePriorities:
LOW = 10 LOW = 10
NORMAL = 20 NORMAL = 20
HIGH = 30 HIGH = 30
class GenericQueue:
class GenericQueue(object):
def __init__(self): def __init__(self):
self.currentItem = None self.currentItem = None
self.queue = []
self.thread = None self.thread = None
self.queue_name = "QUEUE" self.queue_name = "QUEUE"
self.min_priority = 0 self.min_priority = 0
self.queue = Queue.PriorityQueue()
self.currentItem = None
def pause(self): def pause(self):
logger.log(u"Pausing queue") logger.log(u"Pausing queue")
@ -53,11 +45,13 @@ class GenericQueue(object):
def add_item(self, item): def add_item(self, item):
item.added = datetime.datetime.now() item.added = datetime.datetime.now()
self.queue.append(item) self.queue.put(item, item.priority)
return item return item
def run(self): def run(self, queue=None):
# dynamically set queue
if queue:
self.queue = queue
# only start a new task if one isn't already going # only start a new task if one isn't already going
if self.thread == None or self.thread.isAlive() == False: if self.thread == None or self.thread.isAlive() == False:
@ -67,55 +61,25 @@ class GenericQueue(object):
self.currentItem.finish() self.currentItem.finish()
self.currentItem = None self.currentItem = None
# if there's something in the queue then run it in a thread and take it out of the queue if not self.queue.empty():
if len(self.queue) > 0: queueItem = self.queue.get()
# sort by priority
def sorter(x, y):
"""
Sorts by priority descending then time ascending
"""
if x.priority == y.priority:
if y.added == x.added:
return 0
elif y.added < x.added:
return 1
elif y.added > x.added:
return -1
else:
return y.priority - x.priority
self.queue.sort(cmp=sorter)
queueItem = self.queue[0]
if queueItem.priority < self.min_priority: if queueItem.priority < self.min_priority:
return return
# launch the queue item in a thread
# TODO: improve thread name
threadName = self.queue_name + '-' + queueItem.get_thread_name() threadName = self.queue_name + '-' + queueItem.get_thread_name()
self.thread = threading.Thread(None, queueItem.execute, threadName) self.thread = threading.Thread(None, queueItem.execute, threadName)
self.thread.start() self.thread.start()
self.currentItem = queueItem self.currentItem = queueItem
# take it out of the queue
del self.queue[0]
class QueueItem: class QueueItem:
def __init__(self, name, action_id=0): def __init__(self, name, action_id=0):
self.name = name self.name = name
self.inProgress = False self.inProgress = False
self.priority = QueuePriorities.NORMAL self.priority = QueuePriorities.NORMAL
self.thread_name = None self.thread_name = None
self.action_id = action_id self.action_id = action_id
self.added = None self.added = None
def get_thread_name(self): def get_thread_name(self):
@ -133,5 +97,3 @@ class QueueItem:
"""Implementing Classes should call this""" """Implementing Classes should call this"""
self.inProgress = False self.inProgress = False

View File

@ -165,7 +165,7 @@ def sanitizeFileName(name):
return name return name
def getURL(url, post_data=None, headers=None, params=None, timeout=30, json=False): def getURL(url, post_data=None, headers=None, params=None, timeout=30, json=False, use_proxy=False):
""" """
Returns a byte-string retrieved from the url provider. Returns a byte-string retrieved from the url provider.
""" """
@ -187,7 +187,9 @@ def getURL(url, post_data=None, headers=None, params=None, timeout=30, json=Fals
it = iter(req_headers) it = iter(req_headers)
if sickbeard.PROXY_SETTING:
if use_proxy and sickbeard.PROXY_SETTING:
logger.log("Using proxy for url: " + url, logger.DEBUG)
proxies = { proxies = {
"http": sickbeard.PROXY_SETTING, "http": sickbeard.PROXY_SETTING,
"https": sickbeard.PROXY_SETTING, "https": sickbeard.PROXY_SETTING,
@ -549,8 +551,6 @@ def delete_empty_folders(check_empty_dir, keep_dir=None):
# as long as the folder exists and doesn't contain any files, delete it # as long as the folder exists and doesn't contain any files, delete it
while ek.ek(os.path.isdir, check_empty_dir) and check_empty_dir != keep_dir: while ek.ek(os.path.isdir, check_empty_dir) and check_empty_dir != keep_dir:
time.sleep(0.01)
check_files = ek.ek(os.listdir, check_empty_dir) check_files = ek.ek(os.listdir, check_empty_dir)
if not check_files or (len(check_files) <= len(ignore_items) and all( if not check_files or (len(check_files) <= len(ignore_items) and all(
@ -792,7 +792,6 @@ def backupVersionedFile(old_file, version):
new_file = old_file + '.' + 'v' + str(version) new_file = old_file + '.' + 'v' + str(version)
while not ek.ek(os.path.isfile, new_file): while not ek.ek(os.path.isfile, new_file):
time.sleep(0.01)
if not ek.ek(os.path.isfile, old_file): if not ek.ek(os.path.isfile, old_file):
logger.log(u"Not creating backup, " + old_file + " doesn't exist", logger.DEBUG) logger.log(u"Not creating backup, " + old_file + " doesn't exist", logger.DEBUG)
break break

View File

@ -18,6 +18,7 @@
from __future__ import with_statement from __future__ import with_statement
import time
import os import os
import sys import sys
import threading import threading

View File

@ -212,8 +212,8 @@ class NameParser(object):
i = result = 0 i = result = 0
for integer, numeral in numeral_map: for integer, numeral in numeral_map:
time.sleep(0.01)
while n[i:i + len(numeral)] == numeral: while n[i:i + len(numeral)] == numeral:
time.sleep(1)
result += integer result += integer
i += len(numeral) i += len(numeral)

View File

@ -194,7 +194,7 @@ sports_regexs = [
''' '''
^(?P<series_name>(UEFA|MLB|ESPN|WWE|MMA|UFC|TNA|EPL|NASCAR|NBA|NFL|NHL|NRL|PGA|SUPER LEAGUE|FORMULA|FIFA|NETBALL|MOTOGP))[. _-]+ ^(?P<series_name>(UEFA|MLB|ESPN|WWE|MMA|UFC|TNA|EPL|NASCAR|NBA|NFL|NHL|NRL|PGA|SUPER LEAGUE|FORMULA|FIFA|NETBALL|MOTOGP))[. _-]+
((?P<sports_event_id>\d{3})[. _-]+)? ((?P<sports_event_id>\d{3})[. _-]+)?
((?P<sports_event_name>\w+)[. _-]+)? ((?P<sports_event_name>\.+)[. _-]+)?
(?P<sports_event_date>(\d{4}[. _-]+\d{1,2}[. _-]+\d{1,2})|(\d{1,2}\w{2}[. _-]+\w+[. _-]+\d{4})) (?P<sports_event_date>(\d{4}[. _-]+\d{1,2}[. _-]+\d{1,2})|(\d{1,2}\w{2}[. _-]+\w+[. _-]+\d{4}))
[. _-]*((?P<extra_info>.+?)((?<![. _-])(?<!WEB) [. _-]*((?P<extra_info>.+?)((?<![. _-])(?<!WEB)
-(?P<release_group>[^- ]+))?)?$ -(?P<release_group>[^- ]+))?)?$

View File

@ -170,7 +170,6 @@ def update_network_dict():
# list of sql commands to update the network_timezones table # list of sql commands to update the network_timezones table
ql = [] ql = []
for cur_d, cur_t in d.iteritems(): for cur_d, cur_t in d.iteritems():
time.sleep(0.01)
h_k = old_d.has_key(cur_d) h_k = old_d.has_key(cur_d)
if h_k and cur_t != old_d[cur_d]: if h_k and cur_t != old_d[cur_d]:
# update old record # update old record

View File

@ -31,6 +31,7 @@ import prowl
from . import libnotify from . import libnotify
import pushover import pushover
import boxcar import boxcar
import boxcar2
import nma import nma
import pushalot import pushalot
import pushbullet import pushbullet
@ -55,6 +56,7 @@ prowl_notifier = prowl.ProwlNotifier()
libnotify_notifier = libnotify.LibnotifyNotifier() libnotify_notifier = libnotify.LibnotifyNotifier()
pushover_notifier = pushover.PushoverNotifier() pushover_notifier = pushover.PushoverNotifier()
boxcar_notifier = boxcar.BoxcarNotifier() boxcar_notifier = boxcar.BoxcarNotifier()
boxcar2_notifier = boxcar2.Boxcar2Notifier()
nma_notifier = nma.NMA_Notifier() nma_notifier = nma.NMA_Notifier()
pushalot_notifier = pushalot.PushalotNotifier() pushalot_notifier = pushalot.PushalotNotifier()
pushbullet_notifier = pushbullet.PushbulletNotifier() pushbullet_notifier = pushbullet.PushbulletNotifier()
@ -76,6 +78,7 @@ notifiers = [
prowl_notifier, prowl_notifier,
pushover_notifier, pushover_notifier,
boxcar_notifier, boxcar_notifier,
boxcar2_notifier,
nma_notifier, nma_notifier,
pushalot_notifier, pushalot_notifier,
pushbullet_notifier, pushbullet_notifier,

123
sickbeard/notifiers/boxcar2.py Executable file
View File

@ -0,0 +1,123 @@
# Author: Rafael Silva <rpluto@gmail.com>
# Author: Marvin Pinto <me@marvinp.ca>
# Author: Dennis Lutter <lad1337@gmail.com>
# URL: http://code.google.com/p/sickbeard/
#
# This file is part of Sick Beard.
#
# Sick Beard is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Sick Beard is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Sick Beard. If not, see <http://www.gnu.org/licenses/>.
import urllib, urllib2
import time
import sickbeard
from sickbeard import logger
from sickbeard.common import notifyStrings, NOTIFY_SNATCH, NOTIFY_DOWNLOAD, NOTIFY_SUBTITLE_DOWNLOAD
from sickbeard.exceptions import ex
API_URL = "https://new.boxcar.io/api/notifications"
class Boxcar2Notifier:
def test_notify(self, accesstoken, title="SickRage : Test"):
return self._sendBoxcar2("This is a test notification from SickBeard", title, accesstoken)
def _sendBoxcar2(self, msg, title, accesstoken):
"""
Sends a boxcar2 notification to the address provided
msg: The message to send
title: The title of the message
accesstoken: to send to this device
returns: True if the message succeeded, False otherwise
"""
# build up the URL and parameters
#more info goes here - https://boxcar.uservoice.com/knowledgebase/articles/306788-how-to-send-your-boxcar-account-a-notification
msg = msg.strip()
curUrl = API_URL
data = urllib.urlencode({
'user_credentials': accesstoken,
'notification[title]': "SickRage : " + title,
'notification[long_message]': msg,
'notification[sound]': "bird-1"
})
# send the request to boxcar2
try:
req = urllib2.Request(curUrl)
handle = urllib2.urlopen(req, data)
handle.close()
except urllib2.URLError, e:
# if we get an error back that doesn't have an error code then who knows what's really happening
if not hasattr(e, 'code'):
logger.log("Boxcar2 notification failed." + ex(e), logger.ERROR)
return False
else:
logger.log("Boxcar2 notification failed. Error code: " + str(e.code), logger.WARNING)
# HTTP status 404
if e.code == 404:
logger.log("Access token is invalid. Check it.", logger.WARNING)
return False
# If you receive an HTTP status code of 400, it is because you failed to send the proper parameters
elif e.code == 400:
logger.log("Wrong data send to boxcar2", logger.ERROR)
return False
logger.log("Boxcar2 notification successful.", logger.DEBUG)
return True
def notify_snatch(self, ep_name, title=notifyStrings[NOTIFY_SNATCH]):
if sickbeard.BOXCAR2_NOTIFY_ONSNATCH:
self._notifyBoxcar2(title, ep_name)
def notify_download(self, ep_name, title=notifyStrings[NOTIFY_DOWNLOAD]):
if sickbeard.BOXCAR2_NOTIFY_ONDOWNLOAD:
self._notifyBoxcar2(title, ep_name)
def notify_subtitle_download(self, ep_name, lang, title=notifyStrings[NOTIFY_SUBTITLE_DOWNLOAD]):
if sickbeard.BOXCAR2_NOTIFY_ONSUBTITLEDOWNLOAD:
self._notifyBoxcar2(title, ep_name + ": " + lang)
def _notifyBoxcar2(self, title, message, accesstoken=None):
"""
Sends a boxcar2 notification based on the provided info or SB config
title: The title of the notification to send
message: The message string to send
accesstoken: to send to this device
"""
if not sickbeard.USE_BOXCAR2:
logger.log("Notification for Boxcar2 not enabled, skipping this notification", logger.DEBUG)
return False
# if no username was given then use the one from the config
if not accesstoken:
accesstoken = sickbeard.BOXCAR2_ACCESSTOKEN
logger.log("Sending notification for " + message, logger.DEBUG)
self._sendBoxcar2(message, title, accesstoken)
return True
notifier = Boxcar2Notifier

View File

@ -486,7 +486,7 @@ class PostProcessor(object):
np = NameParser(file) np = NameParser(file)
parse_result = np.parse(name) parse_result = np.parse(name)
self._log("Parsed " + name + " into " + str(parse_result).decode('utf-8'), logger.DEBUG) self._log(u"Parsed " + name + " into " + str(parse_result).decode('utf-8', 'xmlcharrefreplace'), logger.DEBUG)
if parse_result.air_by_date: if parse_result.air_by_date:
season = -1 season = -1
@ -915,6 +915,7 @@ class PostProcessor(object):
# find the destination folder # find the destination folder
try: try:
proper_path = ep_obj.proper_path() proper_path = ep_obj.proper_path()
test = proper_path
proper_absolute_path = ek.ek(os.path.join, ep_obj.show.location, proper_path) proper_absolute_path = ek.ek(os.path.join, ep_obj.show.location, proper_path)
dest_path = ek.ek(os.path.dirname, proper_absolute_path) dest_path = ek.ek(os.path.dirname, proper_absolute_path)

View File

@ -85,10 +85,9 @@ class ProperFinder():
# if they haven't been added by a different provider than add the proper to the list # if they haven't been added by a different provider than add the proper to the list
for x in curPropers: for x in curPropers:
time.sleep(0.01)
showObj = helpers.findCertainShow(sickbeard.showList, x.indexerid) showObj = helpers.findCertainShow(sickbeard.showList, x.indexerid)
if not showObj: if not showObj:
logger.log(u"Unable to find the show we watch with indexerID " + str(x.indexerid), logger.ERROR) logger.log(u"Unable to find the show in our watch list " + str(x.name), logger.DEBUG)
continue continue
name = self._genericName(x.name) name = self._genericName(x.name)

View File

@ -321,7 +321,7 @@ class BTNCache(tvcache.TVCache):
# By now we know we've got data and no auth errors, all we need to do is put it in the database # By now we know we've got data and no auth errors, all we need to do is put it in the database
cl = [] cl = []
for item in data: for item in data:
time.sleep(0.01)
ci = self._parseItem(item) ci = self._parseItem(item)
if ci is not None: if ci is not None:
cl.append(ci) cl.append(ci)

View File

@ -240,18 +240,15 @@ class GenericProvider:
self.cache.updateCache() self.cache.updateCache()
for epObj in episodes: for epObj in episodes:
time.sleep(0.01)
cacheResult = self.cache.searchCache(epObj, manualSearch) cacheResult = self.cache.searchCache(epObj, manualSearch)
if len(cacheResult): if len(cacheResult):
results.update(cacheResult) results.update(cacheResult)
continue continue
if epObj.show.air_by_date: logger.log(
logger.log(u'Searching "%s" for "%s"' % (self.name, epObj.prettyABDName())) u'Searching "%s" for "%s" as "%s"' % (self.name, epObj.prettyName(), epObj.prettySceneName()))
else:
logger.log(
u'Searching "%s" for "%s" as "%s"' % (self.name, epObj.prettyName(), epObj.prettySceneName()))
if seasonSearch: if seasonSearch:
for curString in self._get_season_search_strings(epObj): for curString in self._get_season_search_strings(epObj):
@ -273,7 +270,7 @@ class GenericProvider:
for episode, items in searchItems.items(): for episode, items in searchItems.items():
for item in items: for item in items:
time.sleep(0.01)
(title, url) = self._get_title_and_url(item) (title, url) = self._get_title_and_url(item)
@ -335,7 +332,7 @@ class GenericProvider:
logger.log( logger.log(
u"Ignoring result " + title + " because we don't want an episode that is " + Quality.qualityStrings[ u"Ignoring result " + title + " because we don't want an episode that is " + Quality.qualityStrings[
quality], logger.DEBUG) quality], logger.DEBUG)
time.sleep(0.01)
continue continue
logger.log(u"Found result " + title + " at " + url, logger.DEBUG) logger.log(u"Found result " + title + " at " + url, logger.DEBUG)
@ -351,6 +348,7 @@ class GenericProvider:
result.quality = quality result.quality = quality
result.provider = self result.provider = self
result.content = None result.content = None
result.extraInfo = [show]
if len(epObj) == 1: if len(epObj) == 1:
epNum = epObj[0].episode epNum = epObj[0].episode
@ -361,7 +359,6 @@ class GenericProvider:
parse_result.episode_numbers), logger.DEBUG) parse_result.episode_numbers), logger.DEBUG)
elif len(epObj) == 0: elif len(epObj) == 0:
epNum = SEASON_RESULT epNum = SEASON_RESULT
result.extraInfo = [show]
logger.log(u"Separating full season result to check for later", logger.DEBUG) logger.log(u"Separating full season result to check for later", logger.DEBUG)
if not result: if not result:

View File

@ -207,7 +207,7 @@ class HDBitsCache(tvcache.TVCache):
ql = [] ql = []
for item in items: for item in items:
time.sleep(0.01)
ci = self._parseItem(item) ci = self._parseItem(item)
if ci is not None: if ci is not None:
ql.append(ci) ql.append(ci)

View File

@ -349,7 +349,7 @@ class HDTorrentsCache(tvcache.TVCache):
cl = [] cl = []
for result in rss_results: for result in rss_results:
time.sleep(0.01)
item = (result[0], result[1]) item = (result[0], result[1])
ci = self._parseItem(item) ci = self._parseItem(item)
if ci is not None: if ci is not None:
@ -366,7 +366,7 @@ class HDTorrentsCache(tvcache.TVCache):
if not title or not url: if not title or not url:
return None return None
logger.log(u"Attempting to cache item:" + str(title), logger.DEBUG) logger.log(u"Attempting to cache item:" + title, logger.DEBUG)
return self._addCacheEntry(title, url) return self._addCacheEntry(title, url)

View File

@ -295,7 +295,7 @@ class IPTorrentsCache(tvcache.TVCache):
cl = [] cl = []
for result in rss_results: for result in rss_results:
time.sleep(0.01)
item = (result[0], result[1]) item = (result[0], result[1])
ci = self._parseItem(item) ci = self._parseItem(item)
if ci is not None: if ci is not None:
@ -312,7 +312,7 @@ class IPTorrentsCache(tvcache.TVCache):
if not title or not url: if not title or not url:
return None return None
logger.log(u"Attempting to cache item:" + str(title), logger.DEBUG) logger.log(u"Attempting to cache item:" + title, logger.DEBUG)
return self._addCacheEntry(title, url) return self._addCacheEntry(title, url)

View File

@ -424,7 +424,7 @@ class KATCache(tvcache.TVCache):
cl = [] cl = []
for result in rss_results: for result in rss_results:
time.sleep(0.01)
item = (result[0], result[1]) item = (result[0], result[1])
ci = self._parseItem(item) ci = self._parseItem(item)
if ci is not None: if ci is not None:
@ -441,7 +441,7 @@ class KATCache(tvcache.TVCache):
if not title or not url: if not title or not url:
return None return None
logger.log(u"Attempting to cache item:" + str(title), logger.DEBUG) logger.log(u"Attempting to cache item:" + title, logger.DEBUG)
return self._addCacheEntry(title, url) return self._addCacheEntry(title, url)

View File

@ -173,7 +173,7 @@ class NewznabProvider(generic.NZBProvider):
return True return True
def _doSearch(self, search_params, show=None, max_age=0): def _doSearch(self, search_params, epcount=0, age=0):
self._checkAuth() self._checkAuth()
@ -183,8 +183,8 @@ class NewznabProvider(generic.NZBProvider):
"cat": self.catIDs} "cat": self.catIDs}
# if max_age is set, use it, don't allow it to be missing # if max_age is set, use it, don't allow it to be missing
if max_age or not params['maxage']: if age or not params['maxage']:
params['maxage'] = max_age params['maxage'] = age
if search_params: if search_params:
params.update(search_params) params.update(search_params)
@ -229,7 +229,7 @@ class NewznabProvider(generic.NZBProvider):
cache_results] cache_results]
for term in search_terms: for term in search_terms:
for item in self._doSearch({'q': term}, max_age=4): for item in self._doSearch({'q': term}, age=4):
(title, url) = self._get_title_and_url(item) (title, url) = self._get_title_and_url(item)

View File

@ -344,7 +344,7 @@ class NextGenCache(tvcache.TVCache):
cl = [] cl = []
for result in rss_results: for result in rss_results:
time.sleep(0.01)
item = (result[0], result[1]) item = (result[0], result[1])
ci = self._parseItem(item) ci = self._parseItem(item)
if ci is not None: if ci is not None:
@ -361,7 +361,7 @@ class NextGenCache(tvcache.TVCache):
if not title or not url: if not title or not url:
return None return None
logger.log(u"Attempting to cache item:" + str(title), logger.DEBUG) logger.log(u"Attempting to cache item:" + title, logger.DEBUG)
return self._addCacheEntry(title, url) return self._addCacheEntry(title, url)

View File

@ -316,7 +316,7 @@ class PublicHDCache(tvcache.TVCache):
ql = [] ql = []
for result in rss_results: for result in rss_results:
time.sleep(0.01)
item = (result[0], result[1]) item = (result[0], result[1])
ci = self._parseItem(item) ci = self._parseItem(item)
if ci is not None: if ci is not None:
@ -332,7 +332,7 @@ class PublicHDCache(tvcache.TVCache):
if not title or not url: if not title or not url:
return None return None
logger.log(u"Attempting to cache item:" + str(title), logger.DEBUG) logger.log(u"Attempting to cache item:" + title, logger.DEBUG)
return self._addCacheEntry(title, url) return self._addCacheEntry(title, url)

View File

@ -143,6 +143,13 @@ class SCCProvider(generic.TorrentProvider):
return [search_string] return [search_string]
def _isSection(section, text):
title = '<title>.+? \| %s</title>' % section
if re.search(title, text, re.IGNORECASE):
return True
else:
return False
def _doSearch(self, search_params, epcount=0, age=0): def _doSearch(self, search_params, epcount=0, age=0):
results = [] results = []
@ -182,7 +189,7 @@ class SCCProvider(generic.TorrentProvider):
torrent_table = html.find('table', attrs={'id': 'torrents-table'}) torrent_table = html.find('table', attrs={'id': 'torrents-table'})
torrent_rows = torrent_table.find_all('tr') if torrent_table else [] torrent_rows = torrent_table.find_all('tr') if torrent_table else []
#Continue only if one Release is found #Continue only if at least one Release is found
if len(torrent_rows) < 2: if len(torrent_rows) < 2:
if html.title: if html.title:
source = self.name + " (" + html.title.string + ")" source = self.name + " (" + html.title.string + ")"
@ -195,7 +202,12 @@ class SCCProvider(generic.TorrentProvider):
try: try:
link = result.find('td', attrs={'class': 'ttr_name'}).find('a') link = result.find('td', attrs={'class': 'ttr_name'}).find('a')
url = result.find('td', attrs={'class': 'td_dl'}).find('a') all_urls = result.find('td', attrs={'class': 'td_dl'}).find_all('a', limit=2)
# Foreign section contain two links, the others one
if self._isSection('Foreign', dataItem):
url = all_urls[1]
else:
url = all_urls[0]
title = link.string title = link.string
if re.search('\.\.\.', title): if re.search('\.\.\.', title):
details_html = BeautifulSoup(self.getURL(self.url + "/" + link['href'])) details_html = BeautifulSoup(self.getURL(self.url + "/" + link['href']))
@ -215,9 +227,9 @@ class SCCProvider(generic.TorrentProvider):
item = title, download_url, id, seeders, leechers item = title, download_url, id, seeders, leechers
if re.search('<title>SceneAccess \| Non-Scene</title>', dataItem): if self._isSection('Non-Scene', dataItem):
logger.log(u"Found result: " + title + "(" + nonsceneSearchURL + ")", logger.DEBUG) logger.log(u"Found result: " + title + "(" + nonsceneSearchURL + ")", logger.DEBUG)
elif re.search('<title>SceneAccess \| Foreign</title>', dataItem): elif self._isSection('Foreign', dataItem):
logger.log(u"Found result: " + title + "(" + foreignSearchURL + ")", logger.DEBUG) logger.log(u"Found result: " + title + "(" + foreignSearchURL + ")", logger.DEBUG)
else: else:
logger.log(u"Found result: " + title + "(" + searchURL + ")", logger.DEBUG) logger.log(u"Found result: " + title + "(" + searchURL + ")", logger.DEBUG)
@ -322,7 +334,7 @@ class SCCCache(tvcache.TVCache):
cl = [] cl = []
for result in rss_results: for result in rss_results:
time.sleep(0.01)
item = (result[0], result[1]) item = (result[0], result[1])
ci = self._parseItem(item) ci = self._parseItem(item)
if ci is not None: if ci is not None:
@ -339,7 +351,7 @@ class SCCCache(tvcache.TVCache):
if not title or not url: if not title or not url:
return None return None
logger.log(u"Attempting to cache item:" + str(title), logger.DEBUG) logger.log(u"Attempting to cache item:" + title, logger.DEBUG)
return self._addCacheEntry(title, url) return self._addCacheEntry(title, url)

View File

@ -274,7 +274,7 @@ class SpeedCDCache(tvcache.TVCache):
ql = [] ql = []
for result in rss_results: for result in rss_results:
time.sleep(0.01)
item = (result[0], result[1]) item = (result[0], result[1])
ci = self._parseItem(item) ci = self._parseItem(item)
if ci is not None: if ci is not None:
@ -290,7 +290,7 @@ class SpeedCDCache(tvcache.TVCache):
if not title or not url: if not title or not url:
return None return None
logger.log(u"Attempting to cache item:" + str(title), logger.DEBUG) logger.log(u"Attempting to cache item:" + title, logger.DEBUG)
return self._addCacheEntry(title, url) return self._addCacheEntry(title, url)

View File

@ -415,7 +415,7 @@ class ThePirateBayCache(tvcache.TVCache):
cl = [] cl = []
for result in rss_results: for result in rss_results:
time.sleep(0.01)
item = (result[0], result[1]) item = (result[0], result[1])
ci = self._parseItem(item) ci = self._parseItem(item)
if ci is not None: if ci is not None:
@ -432,7 +432,7 @@ class ThePirateBayCache(tvcache.TVCache):
if not title or not url: if not title or not url:
return None return None
logger.log(u"Attempting to cache item:" + str(title), logger.DEBUG) logger.log(u"Attempting to cache item:" + title, logger.DEBUG)
return self._addCacheEntry(title, url) return self._addCacheEntry(title, url)

View File

@ -296,7 +296,7 @@ class TorrentDayCache(tvcache.TVCache):
cl = [] cl = []
for result in rss_results: for result in rss_results:
time.sleep(0.01)
item = (result[0], result[1]) item = (result[0], result[1])
ci = self._parseItem(item) ci = self._parseItem(item)
if ci is not None: if ci is not None:
@ -313,7 +313,7 @@ class TorrentDayCache(tvcache.TVCache):
if not title or not url: if not title or not url:
return None return None
logger.log(u"Attempting to cache item:" + str(title), logger.DEBUG) logger.log(u"Attempting to cache item:" + title, logger.DEBUG)
return self._addCacheEntry(title, url) return self._addCacheEntry(title, url)

View File

@ -295,7 +295,7 @@ class TorrentLeechCache(tvcache.TVCache):
cl = [] cl = []
for result in rss_results: for result in rss_results:
time.sleep(0.01)
item = (result[0], result[1]) item = (result[0], result[1])
ci = self._parseItem(item) ci = self._parseItem(item)
if ci is not None: if ci is not None:
@ -312,7 +312,7 @@ class TorrentLeechCache(tvcache.TVCache):
if not title or not url: if not title or not url:
return None return None
logger.log(u"Attempting to cache item:" + str(title), logger.DEBUG) logger.log(u"Attempting to cache item:" + title, logger.DEBUG)
return self._addCacheEntry(title, url) return self._addCacheEntry(title, url)

View File

@ -27,7 +27,7 @@ from sickbeard.exceptions import ex
class Scheduler: class Scheduler:
def __init__(self, action, cycleTime=datetime.timedelta(minutes=10), runImmediately=True, def __init__(self, action, cycleTime=datetime.timedelta(minutes=10), runImmediately=True,
threadName="ScheduledThread", silent=False): threadName="ScheduledThread", silent=False, runOnce=False, queue=None):
if runImmediately: if runImmediately:
self.lastRun = datetime.datetime.fromordinal(1) self.lastRun = datetime.datetime.fromordinal(1)
@ -44,6 +44,8 @@ class Scheduler:
self.initThread() self.initThread()
self.abort = False self.abort = False
self.runOnce = runOnce
self.queue = queue
def initThread(self): def initThread(self):
if self.thread == None or not self.thread.isAlive(): if self.thread == None or not self.thread.isAlive():
@ -61,8 +63,7 @@ class Scheduler:
def runAction(self): def runAction(self):
while True: while True:
time.sleep(0.01) time.sleep(1)
currentTime = datetime.datetime.now() currentTime = datetime.datetime.now()
if currentTime - self.lastRun > self.cycleTime: if currentTime - self.lastRun > self.cycleTime:
@ -70,12 +71,17 @@ class Scheduler:
try: try:
if not self.silent: if not self.silent:
logger.log(u"Starting new thread: " + self.threadName, logger.DEBUG) logger.log(u"Starting new thread: " + self.threadName, logger.DEBUG)
self.action.run()
# check if we want to pass in our queue dynamically
if self.queue:
self.action.run(self.queue)
else:
self.action.run()
except Exception, e: except Exception, e:
logger.log(u"Exception generated in thread " + self.threadName + ": " + ex(e), logger.ERROR) logger.log(u"Exception generated in thread " + self.threadName + ": " + ex(e), logger.ERROR)
logger.log(repr(traceback.format_exc()), logger.DEBUG) logger.log(repr(traceback.format_exc()), logger.DEBUG)
if self.abort: if self.abort or self.runOnce:
self.abort = False self.abort = False
self.thread = None self.thread = None
return return

View File

@ -21,6 +21,7 @@ from __future__ import with_statement
import os import os
import re import re
import threading import threading
import Queue
import traceback import traceback
import datetime import datetime
@ -111,11 +112,6 @@ def snatchEpisode(result, endStatus=SNATCHED):
if result is None: return False if result is None: return False
# don't notify when we re-download an episode
for curEpObj in result.episodes:
if curEpObj.status in Quality.SNATCHED + Quality.SNATCHED_PROPER + Quality.SNATCHED_BEST:
return 2
result.priority = 0 # -1 = low, 0 = normal, 1 = high result.priority = 0 # -1 = low, 0 = normal, 1 = high
if sickbeard.ALLOW_HIGH_PRIORITY: if sickbeard.ALLOW_HIGH_PRIORITY:
# if it aired recently make it high priority # if it aired recently make it high priority
@ -363,8 +359,9 @@ def filterSearchResults(show, results):
return foundResults return foundResults
def searchProviders(show, season, episodes, curProvider, seasonSearch=False, manualSearch=False): def searchProviders(queueItem, show, season, episodes, curProvider, seasonSearch=False, manualSearch=False):
threading.currentThread().name = curProvider.name thread_name = str(curProvider.name).upper() + '-' + str(show.indexerid)
threading.currentThread().name = thread_name
logger.log(u"Searching for stuff we need from " + show.name + " season " + str(season)) logger.log(u"Searching for stuff we need from " + show.name + " season " + str(season))
foundResults = {} foundResults = {}
@ -392,7 +389,7 @@ def searchProviders(show, season, episodes, curProvider, seasonSearch=False, man
curResults = filterSearchResults(show, curResults) curResults = filterSearchResults(show, curResults)
if len(curResults): if len(curResults):
foundResults.update(curResults) foundResults.update(curResults)
logger.log(u"Provider search results: " + str(foundResults), logger.DEBUG) logger.log(u"Provider search results: " + repr(foundResults), logger.DEBUG)
if not len(foundResults): if not len(foundResults):
return [] return []
@ -407,6 +404,7 @@ def searchProviders(show, season, episodes, curProvider, seasonSearch=False, man
highest_quality_overall = 0 highest_quality_overall = 0
for cur_episode in foundResults: for cur_episode in foundResults:
for cur_result in foundResults[cur_episode]: for cur_result in foundResults[cur_episode]:
cur_result.queue_item = queueItem
if cur_result.quality != Quality.UNKNOWN and cur_result.quality > highest_quality_overall: if cur_result.quality != Quality.UNKNOWN and cur_result.quality > highest_quality_overall:
highest_quality_overall = cur_result.quality highest_quality_overall = cur_result.quality
logger.log(u"The highest quality of any match is " + Quality.qualityStrings[highest_quality_overall], logger.DEBUG) logger.log(u"The highest quality of any match is " + Quality.qualityStrings[highest_quality_overall], logger.DEBUG)

View File

@ -125,7 +125,8 @@ class BacklogSearcher:
backlog_queue_item = search_queue.BacklogQueueItem(curShow, cur_segment) backlog_queue_item = search_queue.BacklogQueueItem(curShow, cur_segment)
if backlog_queue_item.wantedEpisodes: if backlog_queue_item.wantedEpisodes:
sickbeard.searchQueueScheduler.action.add_item(backlog_queue_item) #@UndefinedVariable backlog_queue_item = search_queue.BacklogQueueItem(curShow, cur_segment)
#sickbeard.searchQueueScheduler.action.add_item(backlog_queue_item) #@UndefinedVariable
else: else:
logger.log( logger.log(
u"Nothing in season " + str(cur_segment) + " needs to be downloaded, skipping this season", u"Nothing in season " + str(cur_segment) + " needs to be downloaded, skipping this season",

View File

@ -19,36 +19,114 @@
from __future__ import with_statement from __future__ import with_statement
import datetime import datetime
from threading import Thread import Queue
import threading
import time import time
import traceback
import sickbeard import sickbeard
from sickbeard import db, logger, common, exceptions, helpers from sickbeard import db, logger, common, exceptions, helpers
from sickbeard import generic_queue from sickbeard import generic_queue, scheduler
from sickbeard import search, failed_history, history from sickbeard import search, failed_history, history
from sickbeard import ui from sickbeard import ui
from lib.concurrent import futures from lib.concurrent import futures
from lib.concurrent.futures.thread import ThreadPoolExecutor
BACKLOG_SEARCH = 10 BACKLOG_SEARCH = 10
RSS_SEARCH = 20 RSS_SEARCH = 20
FAILED_SEARCH = 30 FAILED_SEARCH = 30
MANUAL_SEARCH = 30 MANUAL_SEARCH = 30
SNATCH = 40
# snatch queues
ManualSnatchQueue = Queue.PriorityQueue()
RSSSnatchQueue = Queue.PriorityQueue()
BacklogSnatchQueue = Queue.PriorityQueue()
FailedSnatchQueue = Queue.PriorityQueue()
SearchItemQueue = Queue.PriorityQueue()
class SnatchQueue(generic_queue.GenericQueue):
def __init__(self):
generic_queue.GenericQueue.__init__(self)
self.queue_name = "SNATCHQUEUE"
def is_in_queue(self, show, episodes, quality):
for cur_item in self.queue.queue:
if cur_item.results.extraInfo[0] == show \
and cur_item.results.episodes.sort() == episodes.sort() \
and cur_item.results.quality >= quality:
return True
return False
def add_item(self, item):
# dynamically select our snatch queue
if item.type == 'RSSSearchQueueItem':
self.queue = RSSSnatchQueue
elif item.type == 'ManualSearchQueueItem':
self.queue = ManualSnatchQueue
elif item.type == 'BacklogQueueItem':
self.queue = BacklogSnatchQueue
elif item.type == 'FailedQueueItem':
self.queue = FailedSnatchQueue
else:
return
# check if we already have a item ready to snatch with same or better quality score
if not self.is_in_queue(item.results.extraInfo[0], item.results.episodes, item.results.quality):
generic_queue.GenericQueue.add_item(self, item)
else:
logger.log(
u"Not adding item [" + item.results.name + "] it's already in the queue with same or higher quality",
logger.DEBUG)
class SnatchQueueItem(generic_queue.QueueItem):
def __init__(self, results, queue_item):
generic_queue.QueueItem.__init__(self, 'Snatch', SNATCH)
self.priority = generic_queue.QueuePriorities.HIGH
self.thread_name = 'SNATCH-' + str(results.extraInfo[0].indexerid)
self.results = results
self.success = None
self.queue_item = queue_item
self.type = queue_item.type
def execute(self):
generic_queue.QueueItem.execute(self)
# just use the first result for now
logger.log(u"Downloading " + self.results.name + " from " + self.results.provider.name)
result = search.snatchEpisode(self.results)
if self.type == "ManualSearchQueueItem":
providerModule = self.results.provider
if not result:
ui.notifications.error(
'Error while attempting to snatch ' + self.results.name + ', check your logs')
elif providerModule == None:
ui.notifications.error('Provider is configured incorrectly, unable to download')
self.success = result
self.queue_item.success = result
generic_queue.QueueItem.finish(self.queue_item)
generic_queue.QueueItem.finish(self)
class SearchQueue(generic_queue.GenericQueue): class SearchQueue(generic_queue.GenericQueue):
def __init__(self): def __init__(self):
generic_queue.GenericQueue.__init__(self) generic_queue.GenericQueue.__init__(self)
self.queue_name = "SEARCHQUEUE" self.queue_name = "SEARCHQUEUE"
self.queue = SearchItemQueue
def is_in_queue(self, show, segment): def is_in_queue(self, show, segment):
for cur_item in self.queue: for cur_item in self.queue.queue:
if isinstance(cur_item, BacklogQueueItem) and cur_item.show == show and cur_item.segment == segment: if isinstance(cur_item, BacklogQueueItem) and cur_item.show == show and cur_item.segment == segment:
return True return True
return False return False
def is_ep_in_queue(self, ep_obj): def is_ep_in_queue(self, ep_obj):
for cur_item in self.queue: for cur_item in self.queue.queue:
if isinstance(cur_item, ManualSearchQueueItem) and cur_item.ep_obj == ep_obj: if isinstance(cur_item, ManualSearchQueueItem) and cur_item.ep_obj == ep_obj:
return True return True
return False return False
@ -70,6 +148,7 @@ class SearchQueue(generic_queue.GenericQueue):
return False return False
def add_item(self, item): def add_item(self, item):
if isinstance(item, RSSSearchQueueItem): if isinstance(item, RSSSearchQueueItem):
generic_queue.GenericQueue.add_item(self, item) generic_queue.GenericQueue.add_item(self, item)
elif isinstance(item, BacklogQueueItem) and not self.is_in_queue(item.show, item.segment): elif isinstance(item, BacklogQueueItem) and not self.is_in_queue(item.show, item.segment):
@ -86,124 +165,85 @@ class ManualSearchQueueItem(generic_queue.QueueItem):
def __init__(self, ep_obj): def __init__(self, ep_obj):
generic_queue.QueueItem.__init__(self, 'Manual Search', MANUAL_SEARCH) generic_queue.QueueItem.__init__(self, 'Manual Search', MANUAL_SEARCH)
self.priority = generic_queue.QueuePriorities.HIGH self.priority = generic_queue.QueuePriorities.HIGH
self.ep_obj = ep_obj self.type = self.__class__.__name__
self.thread_name = 'MANUAL-' + str(ep_obj.show.indexerid)
self.success = None self.success = None
self.show = ep_obj.show
self.ep_obj = ep_obj
def execute(self): def execute(self):
generic_queue.QueueItem.execute(self) generic_queue.QueueItem.execute(self)
foundResults = [] fs = []
didSearch = False didSearch = False
providers = [x for x in sickbeard.providers.sortedProviderList() if x.isActive()] providers = [x for x in sickbeard.providers.sortedProviderList() if x.isActive()]
try: try:
with futures.ThreadPoolExecutor(sickbeard.NUM_OF_THREADS) as executor: with ThreadPoolExecutor(sickbeard.NUM_OF_THREADS) as executor:
foundResults = list( for provider in providers:
executor.map(self.process, providers)) didSearch = True
didSearch = True logger.log("Beginning manual search for [" + self.ep_obj.prettyName() + "] on " + provider.name)
executor.submit(
search.searchProviders, self, self.show, self.ep_obj.season, [self.ep_obj], provider, False,
True).add_done_callback(snatch_results)
executor.shutdown(wait=True)
except Exception, e: except Exception, e:
pass logger.log(traceback.format_exc(), logger.DEBUG)
if not didSearch: if not didSearch:
logger.log( logger.log(
u"No NZB/Torrent providers found or enabled in your SickRage config. Please check your settings.", u"No NZB/Torrent providers found or enabled in your SickRage config. Please check your settings.",
logger.ERROR) logger.ERROR)
result = False if ManualSnatchQueue.empty():
if not len(foundResults): ui.notifications.message('No downloads were found',
if self.ep_obj.show.air_by_date: "Couldn't find a download for <i>%s</i>" % self.ep_obj.prettyName())
ui.notifications.message('No downloads were found ...', logger.log(u"Unable to find a download for " + self.ep_obj.prettyName())
"Couldn't find a download for <i>%s</i>" % self.ep_obj.prettyABName())
logger.log(u"Unable to find a download for " + self.ep_obj.prettyABDName())
else:
ui.notifications.message('No downloads were found ...',
"Couldn't find a download for <i>%s</i>" % self.ep_obj.prettyName())
logger.log(u"Unable to find a download for " + self.ep_obj.prettyName())
self.success = result
else: else:
for foundResult in [item for sublist in foundResults for item in sublist]: # snatch all items in queue
time.sleep(0.01) scheduler.Scheduler(SnatchQueue(), silent=True, runOnce=True, queue=ManualSnatchQueue).thread.start()
result = search.snatchEpisode(foundResult) generic_queue.QueueItem.finish(self)
# duplicate snatch detected due to multithreading
if result == 2:
continue
providerModule = foundResult.provider
if not result:
ui.notifications.error(
'Error while attempting to snatch ' + foundResult.name + ', check your logs')
elif providerModule == None:
ui.notifications.error('Provider is configured incorrectly, unable to download')
# just use the first result for now
logger.log(u"Downloading " + foundResult.name + " from " + foundResult.provider.name)
self.success = result
def process(self, curProvider):
if self.ep_obj.show.air_by_date:
logger.log("Beginning manual search for " + self.ep_obj.prettyABDName())
else:
logger.log("Beginning manual search for " + self.ep_obj.prettyName())
return search.searchProviders(self.ep_obj.show, self.ep_obj.season, [self.ep_obj], curProvider, False, True)
def finish(self):
# don't let this linger if something goes wrong
if self.success == None:
self.success = False
else:
generic_queue.QueueItem.finish(self)
class RSSSearchQueueItem(generic_queue.QueueItem): class RSSSearchQueueItem(generic_queue.QueueItem):
def __init__(self): def __init__(self):
generic_queue.QueueItem.__init__(self, 'RSS Search', RSS_SEARCH) generic_queue.QueueItem.__init__(self, 'RSS Search', RSS_SEARCH)
self.thread_name = 'RSSFEED'
self.type = self.__class__.__name__
def execute(self): def execute(self):
generic_queue.QueueItem.execute(self) generic_queue.QueueItem.execute(self)
foundResults = [] results = False
didSearch = False didSearch = False
self._changeMissingEpisodes()
providers = [x for x in sickbeard.providers.sortedProviderList() if x.isActive()] providers = [x for x in sickbeard.providers.sortedProviderList() if x.isActive()]
try: try:
with futures.ThreadPoolExecutor(sickbeard.NUM_OF_THREADS) as executor: with ThreadPoolExecutor(sickbeard.NUM_OF_THREADS) as executor:
foundResults = list( for provider in providers:
executor.map(self.process, providers)) didSearch = True
logger.log("Beginning RSS Feed search on " + provider.name)
didSearch = True executor.submit(search.searchForNeededEpisodes, provider).add_done_callback(snatch_results)
executor.shutdown(wait=True)
except: except:
pass logger.log(traceback.format_exc(), logger.DEBUG)
if not didSearch: if not didSearch:
logger.log( logger.log(
u"No NZB/Torrent providers found or enabled in the sickbeard config. Please check your settings.", u"No NZB/Torrent providers found or enabled in your SickRage config. Please check your settings.",
logger.ERROR) logger.ERROR)
if len(foundResults): if RSSSnatchQueue.empty():
for curResult in [item for sublist in foundResults for item in sublist]: logger.log(u"No needed episodes found on the RSS feeds")
time.sleep(0.01)
result = search.snatchEpisode(curResult)
# duplicate snatch detected due to multithreading
if result == 2:
continue
else: else:
logger.log(u"RSS Feed search found nothing to snatch ...") # snatch all items in queue
scheduler.Scheduler(SnatchQueue(), silent=True, runOnce=True, queue=RSSSnatchQueue).thread.start()
generic_queue.QueueItem.finish(self) generic_queue.QueueItem.finish(self)
def process(self, curProvider):
self._changeMissingEpisodes()
logger.log(u"Beginning search for new episodes on RSS feeds and in cache")
return search.searchForNeededEpisodes(curProvider)
def _changeMissingEpisodes(self): def _changeMissingEpisodes(self):
logger.log(u"Changing all old missing episodes to status WANTED") logger.log(u"Changing all old missing episodes to status WANTED")
@ -240,6 +280,7 @@ class BacklogQueueItem(generic_queue.QueueItem):
def __init__(self, show, segment): def __init__(self, show, segment):
generic_queue.QueueItem.__init__(self, 'Backlog', BACKLOG_SEARCH) generic_queue.QueueItem.__init__(self, 'Backlog', BACKLOG_SEARCH)
self.priority = generic_queue.QueuePriorities.LOW self.priority = generic_queue.QueuePriorities.LOW
self.type = self.__class__.__name__
self.thread_name = 'BACKLOG-' + str(show.indexerid) self.thread_name = 'BACKLOG-' + str(show.indexerid)
self.show = show self.show = show
@ -274,53 +315,48 @@ class BacklogQueueItem(generic_queue.QueueItem):
def execute(self): def execute(self):
generic_queue.QueueItem.execute(self) generic_queue.QueueItem.execute(self)
foundResults = [] results = False
didSearch = False didSearch = False
providers = [x for x in sickbeard.providers.sortedProviderList() if x.isActive()]
try:
with futures.ThreadPoolExecutor(sickbeard.NUM_OF_THREADS) as executor:
foundResults = list(executor.map(self.process,providers))
didSearch = True
except:
pass
if not didSearch:
logger.log(
u"No NZB/Torrent providers found or enabled in the sickbeard config. Please check your settings.",
logger.ERROR)
if len(foundResults):
for curResult in [item for sublist in foundResults for item in sublist]:
time.sleep(0.01)
result = search.snatchEpisode(curResult)
# duplicate snatch detected due to multithreading
if result == 2:
continue
else:
logger.log(u"Backlog search found nothing to snatch ...")
self.finish()
def process(self, curProvider):
# check if we want to search for season packs instead of just season/episode # check if we want to search for season packs instead of just season/episode
seasonSearch = False seasonSearch = False
seasonEps = self.show.getAllEpisodes(self.segment) seasonEps = self.show.getAllEpisodes(self.segment)
if len(seasonEps) == len(self.wantedEpisodes): if len(seasonEps) == len(self.wantedEpisodes):
seasonSearch = True seasonSearch = True
return search.searchProviders(self.show, self.segment, self.wantedEpisodes, curProvider, seasonSearch, False) providers = [x for x in sickbeard.providers.sortedProviderList() if x.isActive()]
try:
with ThreadPoolExecutor(sickbeard.NUM_OF_THREADS) as executor:
for provider in providers:
didSearch = True
logger.log("Beginning backlog search for [" + self.segment + "] on " + provider.name)
executor.submit(
search.searchProviders, self, self.show, self.segment, self.wantedEpisodes, provider,
seasonSearch, False).add_done_callback(snatch_results)
executor.shutdown(wait=True)
except Exception, e:
logger.log(traceback.format_exc(), logger.DEBUG)
if not didSearch:
logger.log(
u"No NZB/Torrent providers found or enabled in your SickRage config. Please check your settings.",
logger.ERROR)
if BacklogSnatchQueue.empty():
logger.log(u"No needed episodes found during backlog search")
else:
# snatch all items in queue
scheduler.Scheduler(SnatchQueue(), silent=True, runOnce=True, queue=BacklogSnatchQueue).thread.start()
self.finish()
def _need_any_episodes(self, statusResults, bestQualities): def _need_any_episodes(self, statusResults, bestQualities):
wantedEpisodes = [] wantedEpisodes = []
# check through the list of statuses to see if we want any # check through the list of statuses to see if we want any
for curStatusResult in statusResults: for curStatusResult in statusResults:
time.sleep(0.01) time.sleep(1)
curCompositeStatus = int(curStatusResult["status"]) curCompositeStatus = int(curStatusResult["status"])
curStatus, curQuality = common.Quality.splitCompositeStatus(curCompositeStatus) curStatus, curQuality = common.Quality.splitCompositeStatus(curCompositeStatus)
@ -344,61 +380,24 @@ class FailedQueueItem(generic_queue.QueueItem):
def __init__(self, show, episodes): def __init__(self, show, episodes):
generic_queue.QueueItem.__init__(self, 'Retry', FAILED_SEARCH) generic_queue.QueueItem.__init__(self, 'Retry', FAILED_SEARCH)
self.priority = generic_queue.QueuePriorities.HIGH self.priority = generic_queue.QueuePriorities.HIGH
self.type = self.__class__.__name__
self.thread_name = 'RETRY-' + str(show.indexerid) self.thread_name = 'RETRY-' + str(show.indexerid)
self.show = show self.show = show
self.episodes = episodes self.episodes = episodes
self.success = None self.success = None
def execute(self): def execute(self):
generic_queue.QueueItem.execute(self) generic_queue.QueueItem.execute(self)
foundResults = [] results = False
didSearch = False didSearch = False
providers = [x for x in sickbeard.providers.sortedProviderList() if x.isActive()]
try:
with futures.ThreadPoolExecutor(sickbeard.NUM_OF_THREADS) as executor:
foundResults = list(
executor.map(self.process, providers))
didSearch = True
except:
pass
if not didSearch:
logger.log(
u"No NZB/Torrent providers found or enabled in the sickbeard config. Please check your settings.",
logger.ERROR)
if len(foundResults):
for curResult in [item for sublist in foundResults for item in sublist]:
time.sleep(0.01)
result = search.snatchEpisode(curResult)
# duplicate snatch detected due to multithreading
if result == 2:
continue
self.success = result
else:
logger.log(u"Retry failed download search found nothing to snatch ...")
self.finish()
def process(self, curProvider):
episodes = [] episodes = []
for i, epObj in enumerate(episodes): for i, epObj in enumerate(episodes):
time.sleep(0.01) time.sleep(1)
logger.log(
if epObj.show.air_by_date: "Beginning failed download search for " + epObj.prettyName())
logger.log("Beginning manual search for " + epObj.prettyABDName())
else:
logger.log(
"Beginning failed download search for " + epObj.prettyName())
(release, provider) = failed_history.findRelease(self.show, epObj.season, epObj.episode) (release, provider) = failed_history.findRelease(self.show, epObj.season, epObj.episode)
if release: if release:
@ -410,4 +409,36 @@ class FailedQueueItem(generic_queue.QueueItem):
failed_history.revertEpisode(self.show, epObj.season, epObj.episode) failed_history.revertEpisode(self.show, epObj.season, epObj.episode)
episodes.append(epObj) episodes.append(epObj)
return search.searchProviders(self.show, self.episodes[0].season, self.episodes, curProvider, False, False) providers = [x for x in sickbeard.providers.sortedProviderList() if x.isActive()]
try:
with ThreadPoolExecutor(sickbeard.NUM_OF_THREADS) as executor:
for provider in providers:
didSearch = True
executor.submit(
search.searchProviders, self, self.show, self.episodes[0].season, self.episodes, provider,
False,
True).add_done_callback(snatch_results)
executor.shutdown(wait=True)
except Exception, e:
logger.log(traceback.format_exc(), logger.DEBUG)
if not didSearch:
logger.log(
u"No NZB/Torrent providers found or enabled in your SickRage config. Please check your settings.",
logger.ERROR)
if FailedSnatchQueue.empty():
logger.log(u"No needed episodes found on the RSS feeds")
else:
# snatch all items in queue
scheduler.Scheduler(SnatchQueue(), silent=True, runOnce=True, queue=FailedSnatchQueue).thread.start()
self.finish()
# send to snatch queue
def snatch_results(f):
for result in f.result():
snatch_queue_item = SnatchQueueItem(result, result.queue_item)
SnatchQueue().add_item(snatch_queue_item)

View File

@ -19,6 +19,7 @@
from __future__ import with_statement from __future__ import with_statement
import traceback import traceback
import Queue
import sickbeard import sickbeard
@ -30,16 +31,18 @@ from sickbeard import generic_queue
from sickbeard import name_cache from sickbeard import name_cache
from sickbeard.exceptions import ex from sickbeard.exceptions import ex
ShowItemQueue = Queue.PriorityQueue()
class ShowQueue(generic_queue.GenericQueue): class ShowQueue(generic_queue.GenericQueue):
def __init__(self): def __init__(self):
generic_queue.GenericQueue.__init__(self) generic_queue.GenericQueue.__init__(self)
self.queue_name = "SHOWQUEUE" self.queue_name = "SHOWQUEUE"
self.queue = ShowItemQueue
def _isInQueue(self, show, actions): def _isInQueue(self, show, actions):
return show in [x.show for x in self.queue if x.action_id in actions] return show in [x.show for x in self.queue.queue if x.action_id in actions] if self.queue.qsize() > 0 else []
def _isBeingSomethinged(self, show, actions): def _isBeingSomethinged(self, show, actions):
return self.currentItem != None and show == self.currentItem.show and \ return self.currentItem != None and show == self.currentItem.show and \
@ -73,7 +76,7 @@ class ShowQueue(generic_queue.GenericQueue):
return self._isBeingSomethinged(show, (ShowQueueActions.SUBTITLE,)) return self._isBeingSomethinged(show, (ShowQueueActions.SUBTITLE,))
def _getLoadingShowList(self): def _getLoadingShowList(self):
return [x for x in self.queue + [self.currentItem] if x != None and x.isLoading] return [x for x in self.queue.queue + [self.currentItem] if x != None and x.isLoading] if self.queue.qsize() > 0 else []
loadingShowList = property(_getLoadingShowList) loadingShowList = property(_getLoadingShowList)

View File

@ -441,7 +441,6 @@ class TVShow(object):
sql_l = [] sql_l = []
for season in showObj: for season in showObj:
time.sleep(0.01)
scannedEps[season] = {} scannedEps[season] = {}
for episode in showObj[season]: for episode in showObj[season]:
# need some examples of wtf episode 0 means to decide if we want it or not # need some examples of wtf episode 0 means to decide if we want it or not
@ -1732,18 +1731,10 @@ class TVEpisode(object):
Returns: A string representing the episode's name and season/ep numbers Returns: A string representing the episode's name and season/ep numbers
""" """
if self.show.air_by_date:
return self._format_pattern('%SN - %Sx%0E - %EN') return self._format_pattern('%SN - %AD - %EN')
else:
def prettyABDName(self): return self._format_pattern('%SN - %Sx%0E - %EN')
"""
Returns the name of this episode in a "pretty" human-readable format. Used for logging
and notifications and such.
Returns: A string representing the episode's name and season/ep numbers
"""
return self._format_pattern('%SN - %AD - %EN')
def prettySceneName(self): def prettySceneName(self):
""" """
@ -1752,8 +1743,10 @@ class TVEpisode(object):
Returns: A string representing the episode's name and season/ep numbers Returns: A string representing the episode's name and season/ep numbers
""" """
if self.show.air_by_date:
return self._format_pattern('%SN - %XSx%0XE - %EN') return self._format_pattern('%SN - %AD - %EN')
else:
return self._format_pattern('%SN - %XSx%0XE - %EN')
def _ep_name(self): def _ep_name(self):
""" """
@ -1851,13 +1844,13 @@ class TVEpisode(object):
'%Q.N': dot(Quality.qualityStrings[epQual]), '%Q.N': dot(Quality.qualityStrings[epQual]),
'%Q_N': us(Quality.qualityStrings[epQual]), '%Q_N': us(Quality.qualityStrings[epQual]),
'%S': str(self.season), '%S': str(self.season),
'%0S': '%02d' % int(self.season) if not self.show.air_by_date else self.season, '%0S': '%02d' % self.season,
'%E': str(self.episode), '%E': str(self.episode),
'%0E': '%02d' % int(self.episode)if not self.show.air_by_date else self.episode, '%0E': '%02d' % self.episode,
'%XS': str(self.scene_season), '%XS': str(self.scene_season),
'%0XS': '%02d' % int(self.scene_season), '%0XS': '%02d' % self.scene_season,
'%XE': str(self.scene_episode), '%XE': str(self.scene_episode),
'%0XE': '%02d' % int(self.scene_episode), '%0XE': '%02d' % self.scene_episode,
'%RN': release_name(self.release_name), '%RN': release_name(self.release_name),
'%RG': release_group(self.release_name), '%RG': release_group(self.release_name),
'%AD': str(self.airdate).replace('-', ' '), '%AD': str(self.airdate).replace('-', ' '),
@ -2062,10 +2055,13 @@ class TVEpisode(object):
else: else:
pattern = sickbeard.NAMING_PATTERN pattern = sickbeard.NAMING_PATTERN
# split off the filename only, if they exist # split off the dirs only, if they exist
name_groups = re.split(r'[\\/]', pattern) name_groups = re.split(r'[\\/]', pattern)
return self._format_pattern(name_groups[-1], multi) if len(name_groups) == 1:
return ''
else:
return self._format_pattern(os.sep.join(name_groups[:-1]), multi)
def rename(self): def rename(self):
""" """

View File

@ -143,7 +143,6 @@ class TVCache():
items = data.entries items = data.entries
ql = [] ql = []
for item in items: for item in items:
time.sleep(0.01)
qi = self._parseItem(item) qi = self._parseItem(item)
if qi is not None: if qi is not None:
ql.append(qi) ql.append(qi)

View File

@ -1665,6 +1665,8 @@ class ConfigNotifications:
twitter_notify_onsubtitledownload=None, twitter_notify_onsubtitledownload=None,
use_boxcar=None, boxcar_notify_onsnatch=None, boxcar_notify_ondownload=None, use_boxcar=None, boxcar_notify_onsnatch=None, boxcar_notify_ondownload=None,
boxcar_notify_onsubtitledownload=None, boxcar_username=None, boxcar_notify_onsubtitledownload=None, boxcar_username=None,
use_boxcar2=None, boxcar2_notify_onsnatch=None, boxcar2_notify_ondownload=None,
boxcar2_notify_onsubtitledownload=None, boxcar2_accesstoken=None,
use_pushover=None, pushover_notify_onsnatch=None, pushover_notify_ondownload=None, use_pushover=None, pushover_notify_onsnatch=None, pushover_notify_ondownload=None,
pushover_notify_onsubtitledownload=None, pushover_userkey=None, pushover_notify_onsubtitledownload=None, pushover_userkey=None,
use_libnotify=None, libnotify_notify_onsnatch=None, libnotify_notify_ondownload=None, use_libnotify=None, libnotify_notify_onsnatch=None, libnotify_notify_ondownload=None,
@ -1739,6 +1741,12 @@ class ConfigNotifications:
sickbeard.BOXCAR_NOTIFY_ONSUBTITLEDOWNLOAD = config.checkbox_to_value(boxcar_notify_onsubtitledownload) sickbeard.BOXCAR_NOTIFY_ONSUBTITLEDOWNLOAD = config.checkbox_to_value(boxcar_notify_onsubtitledownload)
sickbeard.BOXCAR_USERNAME = boxcar_username sickbeard.BOXCAR_USERNAME = boxcar_username
sickbeard.USE_BOXCAR2 = config.checkbox_to_value(use_boxcar2)
sickbeard.BOXCAR2_NOTIFY_ONSNATCH = config.checkbox_to_value(boxcar2_notify_onsnatch)
sickbeard.BOXCAR2_NOTIFY_ONDOWNLOAD = config.checkbox_to_value(boxcar2_notify_ondownload)
sickbeard.BOXCAR2_NOTIFY_ONSUBTITLEDOWNLOAD = config.checkbox_to_value(boxcar2_notify_onsubtitledownload)
sickbeard.BOXCAR2_ACCESSTOKEN = boxcar2_accesstoken
sickbeard.USE_PUSHOVER = config.checkbox_to_value(use_pushover) sickbeard.USE_PUSHOVER = config.checkbox_to_value(use_pushover)
sickbeard.PUSHOVER_NOTIFY_ONSNATCH = config.checkbox_to_value(pushover_notify_onsnatch) sickbeard.PUSHOVER_NOTIFY_ONSNATCH = config.checkbox_to_value(pushover_notify_onsnatch)
sickbeard.PUSHOVER_NOTIFY_ONDOWNLOAD = config.checkbox_to_value(pushover_notify_ondownload) sickbeard.PUSHOVER_NOTIFY_ONDOWNLOAD = config.checkbox_to_value(pushover_notify_ondownload)
@ -1925,12 +1933,22 @@ def havePLEX():
return sickbeard.USE_PLEX and sickbeard.PLEX_UPDATE_LIBRARY return sickbeard.USE_PLEX and sickbeard.PLEX_UPDATE_LIBRARY
def haveTORRENT():
if sickbeard.USE_TORRENTS and sickbeard.TORRENT_METHOD != 'blackhole' \
and (sickbeard.ENABLE_HTTPS and sickbeard.TORRENT_HOST[:5] == 'https'
or not sickbeard.ENABLE_HTTPS and sickbeard.TORRENT_HOST[:5] == 'http:'):
return True
else:
return False
def HomeMenu(): def HomeMenu():
return [ return [
{'title': 'Add Shows', 'path': 'home/addShows/', }, {'title': 'Add Shows', 'path': 'home/addShows/', },
{'title': 'Manual Post-Processing', 'path': 'home/postprocess/'}, {'title': 'Manual Post-Processing', 'path': 'home/postprocess/'},
{'title': 'Update XBMC', 'path': 'home/updateXBMC/', 'requires': haveXBMC}, {'title': 'Update XBMC', 'path': 'home/updateXBMC/', 'requires': haveXBMC},
{'title': 'Update Plex', 'path': 'home/updatePLEX/', 'requires': havePLEX}, {'title': 'Update Plex', 'path': 'home/updatePLEX/', 'requires': havePLEX},
{'title': 'Manage Torrents', 'path': 'manage/manageTorrents', 'requires': haveTORRENT},
{'title': 'Restart', 'path': 'home/restart/?pid=' + str(sickbeard.PID), 'confirm': True}, {'title': 'Restart', 'path': 'home/restart/?pid=' + str(sickbeard.PID), 'confirm': True},
{'title': 'Shutdown', 'path': 'home/shutdown/?pid=' + str(sickbeard.PID), 'confirm': True}, {'title': 'Shutdown', 'path': 'home/shutdown/?pid=' + str(sickbeard.PID), 'confirm': True},
] ]
@ -2501,6 +2519,16 @@ class Home:
else: else:
return "Error sending Boxcar notification" return "Error sending Boxcar notification"
@cherrypy.expose
def testBoxcar2(self, accesstoken=None):
cherrypy.response.headers['Cache-Control'] = "max-age=0,no-cache,no-store"
result = notifiers.boxcar2_notifier.test_notify(accesstoken)
if result:
return "Boxcar2 notification succeeded. Check your Boxcar2 clients to make sure it worked"
else:
return "Error sending Boxcar2 notification"
@cherrypy.expose @cherrypy.expose
def testPushover(self, userKey=None): def testPushover(self, userKey=None):
cherrypy.response.headers['Cache-Control'] = "max-age=0,no-cache,no-store" cherrypy.response.headers['Cache-Control'] = "max-age=0,no-cache,no-store"
@ -3153,7 +3181,6 @@ class Home:
sql_l = [] sql_l = []
for curEp in eps.split('|'): for curEp in eps.split('|'):
time.sleep(0.01)
logger.log(u"Attempting to set status on episode " + curEp + " to " + status, logger.DEBUG) logger.log(u"Attempting to set status on episode " + curEp + " to " + status, logger.DEBUG)