Commit 9f3e3b8c authored by Alexandre Beloin's avatar Alexandre Beloin

Merge branch 'develop'

parents 3b399f16 1ec2c573
......@@ -110,9 +110,9 @@ class SickRage(object):
help_msg += " -q --quiet Disables logging to console\n"
help_msg += " --nolaunch Suppress launching web browser on startup\n"
if sys.platform == 'win32':
if sys.platform == 'win32' or sys.platform == 'darwin':
help_msg += " -d --daemon Running as real daemon is not supported on Windows\n"
help_msg += " On Windows, --daemon is substituted with: --quiet --nolaunch\n"
help_msg += " On Windows and MAC, --daemon is substituted with: --quiet --nolaunch\n"
else:
help_msg += " -d --daemon Run as double forked daemon (includes options --quiet --nolaunch)\n"
help_msg += " --pidfile=<path> Combined with --daemon creates a pidfile (full path including filename)\n"
......@@ -208,7 +208,7 @@ class SickRage(object):
self.consoleLogging = False
self.noLaunch = True
if sys.platform == 'win32':
if sys.platform == 'win32' or sys.platform == 'darwin':
self.runAsDaemon = False
# Write a pidfile if requested
......@@ -521,9 +521,11 @@ class SickRage(object):
if '--nolaunch' not in popen_list:
popen_list += ['--nolaunch']
logger.log(u"Restarting SickRage with " + str(popen_list))
logger.shutdown() #shutdown the logger to make sure it's released the logfile BEFORE it restarts SR.
subprocess.Popen(popen_list, cwd=os.getcwd())
# system exit
logger.shutdown() #Make sure the logger has stopped, just in case
os._exit(0)
......
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
......@@ -502,11 +502,15 @@
#set $airday = $cur_result['localtime'].date()
#if $airday == $day:
#set $day_has_show = True
#set $airtime = $sbdatetime.sbdatetime.fromtimestamp($time.mktime($cur_result['localtime'].timetuple())).sbftime().decode($sickbeard.SYS_ENCODING)
#if $sickbeard.TRIM_ZERO:
#set $airtime = re.sub(r'0(\d:\d\d)', r'\1', $airtime, 0, re.IGNORECASE | re.MULTILINE)
#end if
#try
#set $day_has_show = True
#set $airtime = $sbdatetime.sbdatetime.fromtimestamp($time.mktime($cur_result['localtime'].timetuple())).sbftime().decode($sickbeard.SYS_ENCODING)
#if $sickbeard.TRIM_ZERO:
#set $airtime = re.sub(r'0(\d:\d\d)', r'\1', $airtime, 0, re.IGNORECASE | re.MULTILINE)
#end if
#except OverflowError
#set $airtime = "Invalid"
#end try
<tr>
<td class="calendarShow">
......
#compiler-settings
useLegacyImportMode = False
#end compiler-settings
#import sickbeard
#from sickbeard import db
#from sickbeard.helpers import anon_url
......@@ -38,6 +42,27 @@
You don't have version checking turned on. Please turn on "Check for Update" in Config > General.<br />
#end if
</td></tr>
#set $sr_user = None
#try
#import pwd
#set $sr_user = $pwd.getpwuid(os.getuid()).pw_name
#except ImportError
#import getpass
#set $sr_user = $getpass.getuser()
#end try
#if $sr_user:
<tr><td class="infoTableHeader">SR User:</td><td class="infoTableCell">$sr_user</td></tr>
#end if
#try
#import locale
#set $sr_locale = $locale.getdefaultlocale()
<tr><td class="infoTableHeader">SR Locale:</td><td class="infoTableCell">$sr_locale</td></tr>
#except
#pass
#end try
<tr><td class="infoTableHeader">SR Config file:</td><td class="infoTableCell">$sickbeard.CONFIG_FILE</td></tr>
<tr><td class="infoTableHeader">SR Database file:</td><td class="infoTableCell">$db.dbFilename()</td></tr>
<tr><td class="infoTableHeader">SR Cache Dir:</td><td class="infoTableCell">$sickbeard.CACHE_DIR</td></tr>
......
......@@ -49,7 +49,11 @@
</label>
<label class="nocheck">
<span class="component-title">&nbsp;</span>
<span class="component-desc">The folder where your download client puts TV downloads.</span>
<span class="component-desc">The folder where your download client puts the completed TV downloads.</span>
</label>
<label class="nocheck">
<span class="component-title">&nbsp;</span>
<span class="component-desc"><b>NOTE:</b> Please use seperate downloading and completed folders in your download client if possible. Also, if you keep seeding torrents after they finish, please set Process Method to 'copy' instead of move to prevent errors while moving files.</span>
</label>
<label class="nocheck">
<span class="component-title">&nbsp;</span>
......
......@@ -122,7 +122,9 @@
<span class="component-title">Ignore words</span>
<span class="component-desc">
<input type="text" name="ignore_words" value="$sickbeard.IGNORE_WORDS" class="form-control input-sm input350" />
<div class="clear-left">results containing any word in the comma separated word list will be ignored</div>
<div class="clear-left">results with one or more word from this list will be ignored<br />
separate words with a comma, e.g. "word1,word2,word3"
</div>
</span>
</label>
</div>
......@@ -132,7 +134,9 @@
<span class="component-title">Require words</span>
<span class="component-desc">
<input type="text" name="require_words" value="$sickbeard.REQUIRE_WORDS" class="form-control input-sm input350" />
<div class="clear-left">results not containing all words in the comma separated word list will be ignored</div>
<div class="clear-left">results with no word from this list will be ignored<br />
separate words with a comma, e.g. "word1,word2,word3"
</div>
</span>
</label>
</div>
......@@ -493,7 +497,7 @@
</label>
</div>
<div class="field-pair" id="torrent_auth_type">
<div class="field-pair" id="torrent_auth_type_option">
<label>
<span class="component-title">Http Authentication</span>
<span class="component-desc">
......@@ -501,7 +505,7 @@
#set $http_authtype = {'none': "None", 'basic': "Basic", 'digest': "Digest"}
#for $authvalue,$authname in $http_authtype.items():
#set $selected = $html_selected if $sickbeard.TORRENT_AUTH_TYPE == $authvalue else ''
<option value="$authvalue"$selected>$authname</option>
<option id="torrent_auth_type_value" value="$authvalue"$selected>$authname</option>
#end for
</select>
<p></p>
......@@ -520,7 +524,7 @@
</label>
</div>
<div class="field-pair">
<div class="field-pair" id="torrent_username_option">
<label>
<span class="component-title" id="username_title">Client username</span>
<span class="component-desc">
......@@ -530,7 +534,7 @@
</label>
</div>
<div class="field-pair">
<div class="field-pair" id="torrent_password_option">
<label>
<span class="component-title" id="password_title">Client password</span>
<span class="component-desc">
......@@ -578,8 +582,8 @@
<div class="field-pair" id="torrent_seed_time_option">
<label>
<span class="component-title">Minimum seeding time is</span>
<span class="component-desc"><input type="number" step="0.1" name="torrent_seed_time" id="torrent_seed_time" value="$sickbeard.TORRENT_SEED_TIME" class="form-control input-sm input100" />
<span class="component-title" id="torrent_seed_time_label">Minimum seeding time is</span>
<span class="component-desc"><input type="number" step="1" name="torrent_seed_time" id="torrent_seed_time" value="$sickbeard.TORRENT_SEED_TIME" class="form-control input-sm input100" />
<p>hours. (default:'0' passes blank to client and '-1' passes nothing)</p></span>
</label>
</div>
......
......@@ -120,13 +120,13 @@
<b>Ignored Words:</b></br>
<input type="text" name="rls_ignore_words" id="rls_ignore_words" value="$show.rls_ignore_words" class="form-control form-control-inline input-sm input350" /><br />
Results with any of these words in the title will be filtered out<br />
Results with one or more word from this list will be ignored<br />
Separate words with a comma, e.g. "word1,word2,word3"<br />
<br />
<b>Required Words:</b></br>
<input type="text" name="rls_require_words" id="rls_require_words" value="$show.rls_require_words" class="form-control form-control-inline input-sm input350" /><br />
Results without one of these words in the title will be filtered out <br />
Results with no word from this list will be ignored<br />
Separate words with a comma, e.g. "word1,word2,word3"<br />
<br />
......
......@@ -16,9 +16,11 @@
#end if
<div class="align-left"><pre>
#if $classes.ErrorViewer.errors:
#for $curError in sorted($classes.ErrorViewer.errors, key=lambda error: error.time, reverse=True)[:500]:
#for $curError in sorted($classes.ErrorViewer.errors, key=lambda error: error.time, reverse=True)[:500]:
#filter WebSafe
$curError.time $curError.message
#end for
#end filter
#end for
#end if
</pre>
</div>
......
......@@ -16,7 +16,7 @@
<div class="field-pair">
<label for="statusSelect">
<span class="component-title">Set the initial status<br /> of missing episodes</span>
<span class="component-title">Set the initial status<br /> of already aired episodes</span>
<span class="component-desc">
<select name="defaultStatus" id="statusSelect" class="form-control form-control-inline input-sm">
#for $curStatus in [$SKIPPED, $WANTED, $ARCHIVED, $IGNORED]:
......
......@@ -83,11 +83,13 @@ Filter log by: <select name="logFilter" id="logFilter" class="form-control form-
#end for
</select>
Search log by:
<input type="text" name="logSearch" id="logSearch" value="#if $logSearch then $logSearch else ""#" class="form-control form-control-inline input-sm" />
<input type="text" name="logSearch" placeholder="clear to reset" id="logSearch" value="#if $logSearch then $logSearch else ""#" class="form-control form-control-inline input-sm" />
</div>
<br />
<div class="align-left"><pre>
#filter WebSafe
$logLines
#end filter
</pre>
</div>
<br />
......
......@@ -34,6 +34,28 @@ $(document).ready(function(){
}
}
$.fn.rtorrent_scgi = function(){
var selectedProvider = $('#torrent_method :selected').val();
if ('rtorrent' == selectedProvider) {
var hostname = $('#torrent_host').prop('value');
var isMatch = hostname.substr(0, 7) == "scgi://";
if (isMatch) {
$('#torrent_username_option').hide();
$('#torrent_username').prop('value', '');
$('#torrent_password_option').hide();
$('#torrent_password').prop('value', '');
$('#torrent_auth_type_option').hide();
$("#torrent_auth_type option[value=none]").attr('selected', 'selected');
} else {
$('#torrent_username_option').show();
$('#torrent_password_option').show();
$('#torrent_auth_type_option').show();
}
}
}
$.fn.torrent_method_handler = function() {
$('#options_torrent_clients').hide();
......@@ -71,7 +93,7 @@ $(document).ready(function(){
$(torrent_verify_cert_option).hide();
$(torrent_verify_deluge).hide();
$(torrent_verify_rtorrent).hide();
$(torrent_auth_type).hide();
$(torrent_auth_type_option).hide();
$(torrent_path_option).show();
$(torrent_path_option).find('.fileBrowser').show();
$(torrent_seed_time_option).hide();
......@@ -81,14 +103,17 @@ $(document).ready(function(){
$(path_synology).hide();
$(torrent_paused_option).show();
$(torrent_rpcurl_option).hide();
$(this).rtorrent_scgi
if ('utorrent' == selectedProvider) {
client = 'uTorrent';
$(torrent_path_option).hide();
$('#torrent_seed_time_label').text('Minimum seeding time is');
$(torrent_seed_time_option).show();
$('#host_desc_torrent').text('URL to your uTorrent client (e.g. http://localhost:8000)');
} else if ('transmission' == selectedProvider){
client = 'Transmission';
$('#torrent_seed_time_label').text('Stop seeding when inactive for');
$(torrent_seed_time_option).show();
$(torrent_high_bandwidth_option).show();
$(torrent_label_option).hide();
......@@ -103,6 +128,8 @@ $(document).ready(function(){
$(torrent_verify_rtorrent).hide();
$(label_warning_deluge).show();
$(label_anime_warning_deluge).show();
$('#torrent_username_option').hide();
$('#torrent_username').prop('value', '');
$('#host_desc_torrent').text('URL to your Deluge client (e.g. http://localhost:8112)');
//$('#directory_title').text(client + directory);
} else if ('download_station' == selectedProvider){
......@@ -117,11 +144,11 @@ $(document).ready(function(){
} else if ('rtorrent' == selectedProvider){
client = 'rTorrent';
$(torrent_paused_option).hide();
$('#host_desc_torrent').text('URL to your rTorrent client (e.g. scgi://localhost:5000 </br> or https://localhost/rutorrent/plugins/httprpc/action.php)');
$('#host_desc_torrent').text('URL to your rTorrent client (e.g. scgi://localhost:5000 <br/> or https://localhost/rutorrent/plugins/httprpc/action.php)');
$(torrent_verify_cert_option).show();
$(torrent_verify_deluge).hide();
$(torrent_verify_rtorrent).show();
$(torrent_auth_type).show();
$(torrent_auth_type_option).show();
//$('#directory_title').text(client + directory);
}
$('#host_title').text(client + host);
......@@ -168,5 +195,6 @@ $(document).ready(function(){
$.get(sbRoot + '/home/testTorrent', {'torrent_method': torrent_method, 'host': torrent_host, 'username': torrent_username, 'password': torrent_password},
function (data){ $('#test_torrent_result').html(data); });
});
$('#torrent_host').change($(this).rtorrent_scgi);
});
......@@ -98,7 +98,7 @@ class Cache:
del self.storage[url]
return
def fetch(self, url, force_update=False, offline=False, request_headers=None, referrer=None):
def fetch(self, url, force_update=False, offline=False, request_headers=None, referrer=None, handlers=[]):
"""Return the feed at url.
url - The URL of the feed.
......@@ -116,6 +116,8 @@ class Cache:
referrer=None - Added a referrer to request
handlers=None - Urllib2 handlers
If there is data for that feed in the cache already, check
the expiration date before accessing the server. If the
cached data has not expired, return it without accessing the
......@@ -180,7 +182,8 @@ class Cache:
modified=modified,
etag=etag,
referrer=referrer,
request_headers=request_headers)
request_headers=request_headers,
handlers = handlers)
status = parsed_result.get('status', None)
logger.debug('HTTP status=%s' % status)
......
......@@ -43,7 +43,7 @@ def copyfile_custom(src, dst):
for x in iter(lambda: os.read(fin, BUFFER_SIZE), ""):
os.write(fout, x)
except Exception as e:
raise e
raise
finally:
try:
os.close(fin)
......
......@@ -1469,7 +1469,7 @@ def halt():
ADBA_CONNECTION.join(10)
except:
pass
__INITIALIZED__ = False
started = False
......
......@@ -17,7 +17,7 @@
# along with SickRage. If not, see <http://www.gnu.org/licenses/>.
import os.path
import threading
import sickbeard
from sickbeard import logger
......@@ -26,19 +26,30 @@ from sickbeard import processTV
class PostProcesser():
def __init__(self):
self.lock = threading.Lock()
self.amActive = False
def run(self, force=False):
self.amActive = True
if not ek.ek(os.path.isdir, sickbeard.TV_DOWNLOAD_DIR):
logger.log(u"Automatic post-processing attempted but dir " + sickbeard.TV_DOWNLOAD_DIR + " doesn't exist",
logger.ERROR)
self.amActive = False
return
if not ek.ek(os.path.isabs, sickbeard.TV_DOWNLOAD_DIR):
logger.log(
u"Automatic post-processing attempted but dir " + sickbeard.TV_DOWNLOAD_DIR + " is relative (and probably not what you really want to process)",
logger.ERROR)
self.amActive = False
return
processTV.processDir(sickbeard.TV_DOWNLOAD_DIR)
self.amActive = False
def __del__(self):
pass
......@@ -97,8 +97,7 @@ class DelugeAPI(GenericClient):
def _add_torrent_uri(self, result):
post_data = json.dumps({"method": "core.add_torrent_magnet",
"params": [result.url, {"move_completed": "true",
"move_completed_path": sickbeard.TV_DOWNLOAD_DIR}],
"params": [result.url, {}],
"id": 2
})
self._request(method='post', data=post_data)
......@@ -110,9 +109,7 @@ class DelugeAPI(GenericClient):
def _add_torrent_file(self, result):
post_data = json.dumps({"method": "core.add_torrent_file",
"params": [result.name + '.torrent', b64encode(result.content),
{"move_completed": "true",
"move_completed_path": sickbeard.TV_DOWNLOAD_DIR}],
"params": [result.name + '.torrent', b64encode(result.content), {}],
"id": 2
})
self._request(method='post', data=post_data)
......
......@@ -10,6 +10,7 @@ from sickbeard.clients import http_error_code
from lib.bencode import bencode, bdecode
from lib import requests
from lib.requests import exceptions
from lib.bencode.BTL import BTFailure
class GenericClient(object):
def __init__(self, name, host=None, username=None, password=None):
......@@ -148,7 +149,17 @@ class GenericClient(object):
if len(result.hash) == 32:
result.hash = b16encode(b32decode(result.hash)).lower()
else:
info = bdecode(result.content)["info"]
try:
torrent_bdecode = bdecode(result.content)
except BTFailure as e:
logger.log('Unable to bdecode torrent', logger.ERROR)
logger.log('Torrent bencoded data: {0}'.format(str(result.content)), logger.DEBUG)
raise
try:
info = torrent_bdecode["info"]
except Exception as e:
logger.log('Unable to find info field in torrent', logger.ERROR)
raise
result.hash = sha1(bencode(info)).hexdigest()
return result
......
......@@ -78,7 +78,7 @@ class DBConnection(object):
return self.connection.cursor().execute(query)
return self.connection.cursor().execute(query, args)
except Exception as e:
raise e
raise
def execute(self, query, args=None, fetchall=False, fetchone=False):
try:
......@@ -89,7 +89,7 @@ class DBConnection(object):
else:
return self._execute(query, args)
except Exception as e:
raise e
raise
def checkDBVersion(self):
......
......@@ -36,6 +36,8 @@ import base64
import zipfile
import datetime
import errno
import ast
import operator
import sickbeard
import subliminal
......@@ -353,8 +355,8 @@ def listMediaFiles(path):
return files
def copyFile(srcFile, destFile):
ek.ek(shutil.copyfile, srcFile, destFile)
def copyFile(srcFile, destFile):
ek.ek(shutil.copyfile, srcFile, destFile)
try:
ek.ek(shutil.copymode, srcFile, destFile)
except OSError:
......@@ -705,6 +707,38 @@ def sanitizeSceneName(name, ezrss=False, anime=False):
return ''
_binOps = {
ast.Add: operator.add,
ast.Sub: operator.sub,
ast.Mult: operator.mul,
ast.Div: operator.div,
ast.Mod: operator.mod
}
def arithmeticEval(s):
"""
A safe eval supporting basic arithmetic operations.
:param s: expression to evaluate
:return: value
"""
node = ast.parse(s, mode='eval')
def _eval(node):
if isinstance(node, ast.Expression):
return _eval(node.body)
elif isinstance(node, ast.Str):
return node.s
elif isinstance(node, ast.Num):
return node.n
elif isinstance(node, ast.BinOp):
return _binOps[type(node.op)](_eval(node.left), _eval(node.right))
else:
raise Exception('Unsupported type {}'.format(node))
return _eval(node.body)
def create_https_certificates(ssl_cert, ssl_key):
"""
Create self-signed HTTPS certificares and store in paths 'ssl_cert' and 'ssl_key'
......
......@@ -24,6 +24,7 @@ import logging
import logging.handlers
import threading
import platform
import locale
import sickbeard
from sickbeard import classes, encodingKludge as ek
......@@ -120,7 +121,11 @@ class Logger(object):
for logger in self.loggers:
logger.addHandler(rfh)
def shutdown(self):
logging.shutdown()
def log(self, msg, level=INFO, *args, **kwargs):
meThread = threading.currentThread().getName()
message = meThread + u" :: " + msg
......@@ -156,16 +161,28 @@ class Logger(object):
try:
# read log file
log_data = None
if self.logFile and os.path.isfile(self.logFile):
if os.path.isfile(self.logFile):
with ek.ek(codecs.open, *[self.logFile, 'r', 'utf-8']) as f:
log_data = f.readlines()
log_data = [line for line in reversed(log_data)]
for i in range (1 , int(sickbeard.LOG_NR)):
if os.path.isfile(self.logFile + "." + str(i)) and (len(log_data) <= 500):
with ek.ek(codecs.open, *[self.logFile + "." + str(i), 'r', 'utf-8']) as f:
log_data += f.readlines()
log_data = [line for line in reversed(log_data)]
# parse and submit errors to issue tracker
for curError in sorted(classes.ErrorViewer.errors, key=lambda error: error.time, reverse=True)[:500]:
if not curError.title:
continue
if len(curError.title) > 1024:
title_Error = str(curError.title[0:1024])
else:
title_Error = str(curError.title)
gist = None
regex = "^(%s)\s*([A-Z]+)\s*(.+?)\s*\:\:\s*(.*)$" % curError.time
for i, x in enumerate(log_data):
......@@ -178,14 +195,23 @@ class Logger(object):
if paste_data:
gist = gh.get_user().create_gist(True, {"sickrage.log": InputFileContent(paste_data)})
break
else:
gist = 'No ERROR found'
message = u"### INFO\n"
message += u"Python Version: **" + sys.version[:120] + "**\n"
message += u"Operating System: **" + platform.platform() + "**\n"
if not 'Windows' in platform.platform():
try: