mirror of
https://github.com/rembo10/headphones.git
synced 2026-05-16 00:25:31 +01:00
Merged in initscript changes from brunnels, added option to modify album
search term, added nzbx.co
This commit is contained in:
6
.gitignore
vendored
6
.gitignore
vendored
@@ -50,4 +50,8 @@ Thumbs.db
|
||||
obj/
|
||||
[Rr]elease*/
|
||||
_ReSharper*/
|
||||
[Tt]est[Rr]esult*
|
||||
[Tt]est[Rr]esult*
|
||||
/cache
|
||||
/logs
|
||||
.project
|
||||
.pydevproject
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
import os, sys, locale
|
||||
import time
|
||||
import signal
|
||||
|
||||
from lib.configobj import ConfigObj
|
||||
|
||||
@@ -27,7 +28,10 @@ try:
|
||||
import argparse
|
||||
except ImportError:
|
||||
import lib.argparse as argparse
|
||||
|
||||
|
||||
signal.signal(signal.SIGINT, headphones.sig_handler)
|
||||
signal.signal(signal.SIGTERM, headphones.sig_handler)
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
@@ -36,10 +40,10 @@ def main():
|
||||
headphones.FULL_PATH = os.path.abspath(sys.executable)
|
||||
else:
|
||||
headphones.FULL_PATH = os.path.abspath(__file__)
|
||||
|
||||
|
||||
headphones.PROG_DIR = os.path.dirname(headphones.FULL_PATH)
|
||||
headphones.ARGS = sys.argv[1:]
|
||||
|
||||
|
||||
# From sickbeard
|
||||
headphones.SYS_PLATFORM = sys.platform
|
||||
headphones.SYS_ENCODING = None
|
||||
@@ -53,7 +57,7 @@ def main():
|
||||
# for OSes that are poorly configured I'll just force UTF-8
|
||||
if not headphones.SYS_ENCODING or headphones.SYS_ENCODING in ('ANSI_X3.4-1968', 'US-ASCII', 'ASCII'):
|
||||
headphones.SYS_ENCODING = 'UTF-8'
|
||||
|
||||
|
||||
# Set up and gather command line arguments
|
||||
parser = argparse.ArgumentParser(description='Music add-on for SABnzbd+')
|
||||
|
||||
@@ -65,55 +69,73 @@ def main():
|
||||
parser.add_argument('--config', help='Specify a config file to use')
|
||||
parser.add_argument('--nolaunch', action='store_true', help='Prevent browser from launching on startup')
|
||||
parser.add_argument('--pidfile', help='Create a pid file (only relevant when running as a daemon)')
|
||||
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.verbose:
|
||||
headphones.VERBOSE = 2
|
||||
elif args.quiet:
|
||||
headphones.VERBOSE = 0
|
||||
|
||||
|
||||
if args.daemon:
|
||||
headphones.DAEMON=True
|
||||
headphones.VERBOSE = 0
|
||||
if args.pidfile :
|
||||
headphones.PIDFILE = args.pidfile
|
||||
if sys.platform == 'win32':
|
||||
print "Daemonize not supported under Windows, starting normally"
|
||||
else:
|
||||
headphones.DAEMON=True
|
||||
headphones.VERBOSE = False
|
||||
|
||||
if args.pidfile:
|
||||
headphones.PIDFILE = str(args.pidfile)
|
||||
|
||||
# If the pidfile already exists, headphones may still be running, so exit
|
||||
if os.path.exists(headphones.PIDFILE):
|
||||
sys.exit("PID file '" + headphones.PIDFILE + "' already exists. Exiting.")
|
||||
|
||||
# The pidfile is only useful in daemon mode, make sure we can write the file properly
|
||||
if headphones.DAEMON:
|
||||
headphones.CREATEPID = True
|
||||
try:
|
||||
file(headphones.PIDFILE, 'w').write("pid\n")
|
||||
except IOError, e:
|
||||
raise SystemExit("Unable to write PID file: %s [%d]" % (e.strerror, e.errno))
|
||||
else:
|
||||
logger.warn("Not running in daemon mode. PID file creation disabled.")
|
||||
|
||||
if args.datadir:
|
||||
headphones.DATA_DIR = args.datadir
|
||||
else:
|
||||
headphones.DATA_DIR = headphones.PROG_DIR
|
||||
|
||||
|
||||
if args.config:
|
||||
headphones.CONFIG_FILE = args.config
|
||||
else:
|
||||
headphones.CONFIG_FILE = os.path.join(headphones.DATA_DIR, 'config.ini')
|
||||
|
||||
|
||||
# Try to create the DATA_DIR if it doesn't exist
|
||||
if not os.path.exists(headphones.DATA_DIR):
|
||||
try:
|
||||
os.makedirs(headphones.DATA_DIR)
|
||||
except OSError:
|
||||
raise SystemExit('Could not create data directory: ' + headphones.DATA_DIR + '. Exiting....')
|
||||
|
||||
|
||||
# Make sure the DATA_DIR is writeable
|
||||
if not os.access(headphones.DATA_DIR, os.W_OK):
|
||||
raise SystemExit('Cannot write to the data directory: ' + headphones.DATA_DIR + '. Exiting...')
|
||||
|
||||
|
||||
# Put the database in the DATA_DIR
|
||||
headphones.DB_FILE = os.path.join(headphones.DATA_DIR, 'headphones.db')
|
||||
|
||||
|
||||
headphones.CFG = ConfigObj(headphones.CONFIG_FILE, encoding='utf-8')
|
||||
|
||||
|
||||
# Read config & start logging
|
||||
headphones.initialize()
|
||||
|
||||
|
||||
if headphones.DAEMON:
|
||||
if sys.platform == "win32":
|
||||
print "Daemonize not supported under Windows, starting normally"
|
||||
else:
|
||||
headphones.daemonize()
|
||||
|
||||
|
||||
#configure the connection to the musicbrainz database
|
||||
headphones.mb.startmb()
|
||||
|
||||
@@ -123,8 +145,8 @@ def main():
|
||||
logger.info('Starting Headphones on forced port: %i' % http_port)
|
||||
else:
|
||||
http_port = int(headphones.HTTP_PORT)
|
||||
|
||||
# Try to start the server.
|
||||
|
||||
# Try to start the server.
|
||||
webstart.initialize({
|
||||
'http_port': http_port,
|
||||
'http_host': headphones.HTTP_HOST,
|
||||
@@ -133,15 +155,15 @@ def main():
|
||||
'http_username': headphones.HTTP_USERNAME,
|
||||
'http_password': headphones.HTTP_PASSWORD,
|
||||
})
|
||||
|
||||
|
||||
logger.info('Starting Headphones on port: %i' % http_port)
|
||||
|
||||
|
||||
if headphones.LAUNCH_BROWSER and not args.nolaunch:
|
||||
headphones.launch_browser(headphones.HTTP_HOST, http_port, headphones.HTTP_ROOT)
|
||||
|
||||
|
||||
# Start the background threads
|
||||
headphones.start()
|
||||
|
||||
|
||||
while True:
|
||||
if not headphones.SIGNAL:
|
||||
try:
|
||||
@@ -156,9 +178,9 @@ def main():
|
||||
headphones.shutdown(restart=True)
|
||||
else:
|
||||
headphones.shutdown(restart=True, update=True)
|
||||
|
||||
|
||||
headphones.SIGNAL = None
|
||||
|
||||
|
||||
return
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -41,6 +41,23 @@
|
||||
%endfor
|
||||
%endif
|
||||
</div>
|
||||
</div>
|
||||
<a class="menu_link_edit" id="edit_search_term" href="#">Edit Search Term</a>
|
||||
<div id="dialog2" title="Enter your own search term for this album" style="display:none" class="configtable">
|
||||
<form action="editSearchTerm" method="GET" id="editSearchTerm">
|
||||
<input type="hidden" name="AlbumID" value="${album['AlbumID']}">
|
||||
<div class="row">
|
||||
<%
|
||||
if not album['SearchTerm']:
|
||||
search_term = ""
|
||||
else:
|
||||
search_term = album['SearchTerm']
|
||||
|
||||
%>
|
||||
<input type="text" value="${search_term}" name="SearchTerm" size="40" />
|
||||
</div>
|
||||
<input type="button" value="Save changes" onclick="doAjaxCall('editSearchTerm',$(this),'tabs',true);return false;" data-success="Search term updated"/>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -162,6 +179,10 @@
|
||||
$('#dialog').dialog({ width: "500px" });
|
||||
return false;
|
||||
});
|
||||
$('#edit_search_term').click(function() {
|
||||
$('#dialog2').dialog({ width: "500px" });
|
||||
return false;
|
||||
});
|
||||
$('#refresh_artist').click(function() {
|
||||
$('#dialog').dialog("close");
|
||||
});
|
||||
|
||||
@@ -11,19 +11,19 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
|
||||
|
||||
<title>Headphones - ${title}</title>
|
||||
<meta name="description" content="Headphones 'default' interface - made by Elmar Kouwenhoven">
|
||||
<meta name="author" content="Elmar Kouwenhoven">
|
||||
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
|
||||
<link rel="shortcut icon" href="images/favicon.ico">
|
||||
<link rel="apple-touch-icon" href="images/headphoneslogo.png">
|
||||
<link rel="stylesheet" href="interfaces/default/css/style.css">
|
||||
<link rel="stylesheet" href="interfaces/default/css/jquery-ui.css">
|
||||
${next.headIncludes()}
|
||||
|
||||
|
||||
<script src="js/libs/modernizr-1.7.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
@@ -31,16 +31,16 @@
|
||||
<div id="ajaxMsg"></div>
|
||||
% if not headphones.CURRENT_VERSION:
|
||||
<div id="updatebar">
|
||||
You're running an unknown version of Headphones. <a href="update">Update</a> or
|
||||
You're running an unknown version of Headphones. <a href="update">Update</a> or
|
||||
<a href="#" onclick="$('#updatebar').slideUp('slow');">Close</a>
|
||||
</div>
|
||||
% elif headphones.CURRENT_VERSION != headphones.LATEST_VERSION and headphones.INSTALL_TYPE != 'win':
|
||||
<div id="updatebar">
|
||||
A <a href="https://github.com/AdeHub/headphones/compare/${headphones.CURRENT_VERSION}...${headphones.LATEST_VERSION}"> newer version</a> is available. You're ${headphones.COMMITS_BEHIND} commits behind. <a href="update">Update</a> or <a href="#" onclick="$('#updatebar').slideUp('slow');">Close</a>
|
||||
A <a href="https://github.com/${headphones.GIT_USER}/headphones/compare/${headphones.CURRENT_VERSION}...${headphones.LATEST_VERSION}"> newer version</a> is available. You're ${headphones.COMMITS_BEHIND} commits behind. <a href="update">Update</a> or <a href="#" onclick="$('#updatebar').slideUp('slow');">Close</a>
|
||||
</div>
|
||||
% endif
|
||||
|
||||
<header>
|
||||
|
||||
<header>
|
||||
<div class="wrapper">
|
||||
<div id="logo">
|
||||
<a href="home"><img src="images/headphoneslogo.png" alt="headphones"></a>
|
||||
@@ -64,7 +64,7 @@
|
||||
<input type="submit" value="Add"/>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</header>
|
||||
|
||||
@@ -91,16 +91,16 @@
|
||||
%if version.HEADPHONES_VERSION != 'master':
|
||||
(${version.HEADPHONES_VERSION})
|
||||
%endif
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
<a href="#main" id="toTop"><span>Back to top</span></a>
|
||||
</div>
|
||||
|
||||
<script src="js/libs/jquery-1.7.2.min.js"></script>
|
||||
<script src="js/libs/jquery-ui.min.js"></script>
|
||||
|
||||
|
||||
${next.javascriptIncludes()}
|
||||
|
||||
|
||||
<script src="js/plugins.js"></script>
|
||||
<script src="interfaces/default/js/script.js"></script>
|
||||
<!--[if lt IE 7 ]>
|
||||
|
||||
@@ -271,6 +271,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>nzbX</legend>
|
||||
<div class="row checkbox">
|
||||
<input id="usenzbx" type="checkbox" name="nzbx" onclick="initConfigCheckbox($(this));" value="1" ${config['use_nzbx']} /><label>Use nzbX</label>
|
||||
</div>
|
||||
</fieldset>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
@@ -1029,6 +1035,7 @@
|
||||
initConfigCheckbox("#usenewznab");
|
||||
initConfigCheckbox("#usenzbsrus");
|
||||
initConfigCheckbox("#usenzbsorg");
|
||||
initConfigCheckbox("#usenzbx");
|
||||
initConfigCheckbox("#usewaffles");
|
||||
initConfigCheckbox("#userutracker");
|
||||
initConfigCheckbox("#usewhatcd");
|
||||
|
||||
@@ -41,6 +41,7 @@ SYS_ENCODING = None
|
||||
|
||||
VERBOSE = 1
|
||||
DAEMON = False
|
||||
CREATEPID = False
|
||||
PIDFILE= None
|
||||
|
||||
SCHED = Scheduler()
|
||||
@@ -74,6 +75,8 @@ API_ENABLED = False
|
||||
API_KEY = None
|
||||
|
||||
GIT_PATH = None
|
||||
GIT_USER = None
|
||||
GIT_BRANCH =None
|
||||
INSTALL_TYPE = None
|
||||
CURRENT_VERSION = None
|
||||
LATEST_VERSION = None
|
||||
@@ -145,6 +148,8 @@ NZBSRUS = False
|
||||
NZBSRUS_UID = None
|
||||
NZBSRUS_APIKEY = None
|
||||
|
||||
NZBX = False
|
||||
|
||||
LASTFM_USERNAME = None
|
||||
|
||||
LOSSY_MEDIA_FORMATS = ["mp3", "aac", "ogg", "ape", "m4a"]
|
||||
@@ -262,9 +267,9 @@ def check_setting_str(config, cfg_name, item_name, def_val, log=True):
|
||||
def initialize():
|
||||
|
||||
with INIT_LOCK:
|
||||
|
||||
|
||||
global __INITIALIZED__, FULL_PATH, PROG_DIR, VERBOSE, DAEMON, SYS_PLATFORM, DATA_DIR, CONFIG_FILE, CFG, CONFIG_VERSION, LOG_DIR, CACHE_DIR, \
|
||||
HTTP_PORT, HTTP_HOST, HTTP_USERNAME, HTTP_PASSWORD, HTTP_ROOT, HTTP_PROXY, LAUNCH_BROWSER, API_ENABLED, API_KEY, GIT_PATH, \
|
||||
HTTP_PORT, HTTP_HOST, HTTP_USERNAME, HTTP_PASSWORD, HTTP_ROOT, HTTP_PROXY, LAUNCH_BROWSER, API_ENABLED, API_KEY, GIT_PATH, GIT_USER, GIT_BRANCH, \
|
||||
CURRENT_VERSION, LATEST_VERSION, CHECK_GITHUB, CHECK_GITHUB_ON_STARTUP, CHECK_GITHUB_INTERVAL, MUSIC_DIR, DESTINATION_DIR, \
|
||||
LOSSLESS_DESTINATION_DIR, PREFERRED_QUALITY, PREFERRED_BITRATE, DETECT_BITRATE, ADD_ARTISTS, CORRECT_METADATA, MOVE_FILES, \
|
||||
RENAME_FILES, FOLDER_FORMAT, FILE_FORMAT, CLEANUP_FILES, INCLUDE_EXTRAS, EXTRAS, AUTOWANT_UPCOMING, AUTOWANT_ALL, KEEP_TORRENT_FILES, \
|
||||
@@ -272,18 +277,18 @@ def initialize():
|
||||
TORRENTBLACKHOLE_DIR, NUMBEROFSEEDERS, ISOHUNT, KAT, MININOVA, WAFFLES, WAFFLES_UID, WAFFLES_PASSKEY, \
|
||||
RUTRACKER, RUTRACKER_USER, RUTRACKER_PASSWORD, WHATCD, WHATCD_USERNAME, WHATCD_PASSWORD, DOWNLOAD_TORRENT_DIR, \
|
||||
LIBRARYSCAN, LIBRARYSCAN_INTERVAL, DOWNLOAD_SCAN_INTERVAL, SAB_HOST, SAB_USERNAME, SAB_PASSWORD, SAB_APIKEY, SAB_CATEGORY, \
|
||||
NZBMATRIX, NZBMATRIX_USERNAME, NZBMATRIX_APIKEY, NEWZNAB, NEWZNAB_HOST, NEWZNAB_APIKEY, NEWZNAB_ENABLED, EXTRA_NEWZNABS,\
|
||||
NZBSORG, NZBSORG_UID, NZBSORG_HASH, NEWZBIN, NEWZBIN_UID, NEWZBIN_PASSWORD, NZBSRUS, NZBSRUS_UID, NZBSRUS_APIKEY, LASTFM_USERNAME, INTERFACE, FOLDER_PERMISSIONS, \
|
||||
ENCODERFOLDER, ENCODER_PATH, ENCODER, XLDPROFILE, BITRATE, SAMPLINGFREQUENCY, MUSIC_ENCODER, ADVANCEDENCODER, ENCODEROUTPUTFORMAT, ENCODERQUALITY, \
|
||||
ENCODERVBRCBR, ENCODERLOSSLESS, DELETE_LOSSLESS_FILES, PROWL_ENABLED, PROWL_PRIORITY, PROWL_KEYS, PROWL_ONSNATCH, \
|
||||
PUSHOVER_ENABLED, PUSHOVER_PRIORITY, PUSHOVER_KEYS, PUSHOVER_ONSNATCH, MIRRORLIST, \
|
||||
NZBMATRIX, NZBMATRIX_USERNAME, NZBMATRIX_APIKEY, NEWZNAB, NEWZNAB_HOST, NEWZNAB_APIKEY, NEWZNAB_ENABLED, EXTRA_NEWZNABS, \
|
||||
NZBSORG, NZBSORG_UID, NZBSORG_HASH, NEWZBIN, NEWZBIN_UID, NEWZBIN_PASSWORD, NZBSRUS, NZBSRUS_UID, NZBSRUS_APIKEY, NZBX, \
|
||||
LASTFM_USERNAME, INTERFACE, FOLDER_PERMISSIONS, ENCODERFOLDER, ENCODER_PATH, ENCODER, XLDPROFILE, BITRATE, SAMPLINGFREQUENCY, \
|
||||
MUSIC_ENCODER, ADVANCEDENCODER, ENCODEROUTPUTFORMAT, ENCODERQUALITY, ENCODERVBRCBR, ENCODERLOSSLESS, DELETE_LOSSLESS_FILES, \
|
||||
PROWL_ENABLED, PROWL_PRIORITY, PROWL_KEYS, PROWL_ONSNATCH, PUSHOVER_ENABLED, PUSHOVER_PRIORITY, PUSHOVER_KEYS, PUSHOVER_ONSNATCH, MIRRORLIST, \
|
||||
MIRROR, CUSTOMHOST, CUSTOMPORT, CUSTOMSLEEP, HPUSER, HPPASS, XBMC_ENABLED, XBMC_HOST, XBMC_USERNAME, XBMC_PASSWORD, XBMC_UPDATE, \
|
||||
XBMC_NOTIFY, NMA_ENABLED, NMA_APIKEY, NMA_PRIORITY, NMA_ONSNATCH, SYNOINDEX_ENABLED, ALBUM_COMPLETION_PCT, PREFERRED_BITRATE_HIGH_BUFFER, \
|
||||
PREFERRED_BITRATE_LOW_BUFFER,CACHE_SIZEMB
|
||||
|
||||
|
||||
if __INITIALIZED__:
|
||||
return False
|
||||
|
||||
|
||||
# Make sure all the config sections exist
|
||||
CheckSection('General')
|
||||
CheckSection('SABnzbd')
|
||||
@@ -291,6 +296,7 @@ def initialize():
|
||||
CheckSection('Newznab')
|
||||
CheckSection('NZBsorg')
|
||||
CheckSection('NZBsRus')
|
||||
CheckSection('nzbX')
|
||||
CheckSection('Newzbin')
|
||||
CheckSection('Waffles')
|
||||
CheckSection('Rutracker')
|
||||
@@ -301,18 +307,18 @@ def initialize():
|
||||
CheckSection('NMA')
|
||||
CheckSection('Synoindex')
|
||||
CheckSection('Advanced')
|
||||
|
||||
|
||||
# Set global variables based on config file or use defaults
|
||||
CONFIG_VERSION = check_setting_str(CFG, 'General', 'config_version', '0')
|
||||
|
||||
|
||||
try:
|
||||
HTTP_PORT = check_setting_int(CFG, 'General', 'http_port', 8181)
|
||||
except:
|
||||
HTTP_PORT = 8181
|
||||
|
||||
|
||||
if HTTP_PORT < 21 or HTTP_PORT > 65535:
|
||||
HTTP_PORT = 8181
|
||||
|
||||
|
||||
HTTP_HOST = check_setting_str(CFG, 'General', 'http_host', '0.0.0.0')
|
||||
HTTP_USERNAME = check_setting_str(CFG, 'General', 'http_username', '')
|
||||
HTTP_PASSWORD = check_setting_str(CFG, 'General', 'http_password', '')
|
||||
@@ -322,13 +328,15 @@ def initialize():
|
||||
API_ENABLED = bool(check_setting_int(CFG, 'General', 'api_enabled', 0))
|
||||
API_KEY = check_setting_str(CFG, 'General', 'api_key', '')
|
||||
GIT_PATH = check_setting_str(CFG, 'General', 'git_path', '')
|
||||
GIT_USER = check_setting_str(CFG, 'General', 'git_user', 'rembo10')
|
||||
GIT_BRANCH = check_setting_str(CFG, 'General', 'git_branch', 'master')
|
||||
LOG_DIR = check_setting_str(CFG, 'General', 'log_dir', '')
|
||||
CACHE_DIR = check_setting_str(CFG, 'General', 'cache_dir', '')
|
||||
|
||||
|
||||
CHECK_GITHUB = bool(check_setting_int(CFG, 'General', 'check_github', 1))
|
||||
CHECK_GITHUB_ON_STARTUP = bool(check_setting_int(CFG, 'General', 'check_github_on_startup', 1))
|
||||
CHECK_GITHUB_INTERVAL = check_setting_int(CFG, 'General', 'check_github_interval', 360)
|
||||
|
||||
|
||||
MUSIC_DIR = check_setting_str(CFG, 'General', 'music_dir', '')
|
||||
DESTINATION_DIR = check_setting_str(CFG, 'General', 'destination_dir', '')
|
||||
LOSSLESS_DESTINATION_DIR = check_setting_str(CFG, 'General', 'lossless_destination_dir', '')
|
||||
@@ -356,12 +364,12 @@ def initialize():
|
||||
AUTOWANT_UPCOMING = bool(check_setting_int(CFG, 'General', 'autowant_upcoming', 1))
|
||||
AUTOWANT_ALL = bool(check_setting_int(CFG, 'General', 'autowant_all', 0))
|
||||
KEEP_TORRENT_FILES = bool(check_setting_int(CFG, 'General', 'keep_torrent_files', 0))
|
||||
|
||||
|
||||
SEARCH_INTERVAL = check_setting_int(CFG, 'General', 'search_interval', 1440)
|
||||
LIBRARYSCAN = bool(check_setting_int(CFG, 'General', 'libraryscan', 1))
|
||||
LIBRARYSCAN_INTERVAL = check_setting_int(CFG, 'General', 'libraryscan_interval', 300)
|
||||
DOWNLOAD_SCAN_INTERVAL = check_setting_int(CFG, 'General', 'download_scan_interval', 5)
|
||||
|
||||
|
||||
TORRENTBLACKHOLE_DIR = check_setting_str(CFG, 'General', 'torrentblackhole_dir', '')
|
||||
NUMBEROFSEEDERS = check_setting_str(CFG, 'General', 'numberofseeders', '10')
|
||||
ISOHUNT = bool(check_setting_int(CFG, 'General', 'isohunt', 0))
|
||||
@@ -372,7 +380,7 @@ def initialize():
|
||||
WAFFLES = bool(check_setting_int(CFG, 'Waffles', 'waffles', 0))
|
||||
WAFFLES_UID = check_setting_str(CFG, 'Waffles', 'waffles_uid', '')
|
||||
WAFFLES_PASSKEY = check_setting_str(CFG, 'Waffles', 'waffles_passkey', '')
|
||||
|
||||
|
||||
RUTRACKER = bool(check_setting_int(CFG, 'Rutracker', 'rutracker', 0))
|
||||
RUTRACKER_USER = check_setting_str(CFG, 'Rutracker', 'rutracker_user', '')
|
||||
RUTRACKER_PASSWORD = check_setting_str(CFG, 'Rutracker', 'rutracker_password', '')
|
||||
@@ -386,20 +394,20 @@ def initialize():
|
||||
SAB_PASSWORD = check_setting_str(CFG, 'SABnzbd', 'sab_password', '')
|
||||
SAB_APIKEY = check_setting_str(CFG, 'SABnzbd', 'sab_apikey', '')
|
||||
SAB_CATEGORY = check_setting_str(CFG, 'SABnzbd', 'sab_category', '')
|
||||
|
||||
|
||||
NZBMATRIX = bool(check_setting_int(CFG, 'NZBMatrix', 'nzbmatrix', 0))
|
||||
NZBMATRIX_USERNAME = check_setting_str(CFG, 'NZBMatrix', 'nzbmatrix_username', '')
|
||||
NZBMATRIX_APIKEY = check_setting_str(CFG, 'NZBMatrix', 'nzbmatrix_apikey', '')
|
||||
|
||||
|
||||
NEWZNAB = bool(check_setting_int(CFG, 'Newznab', 'newznab', 0))
|
||||
NEWZNAB_HOST = check_setting_str(CFG, 'Newznab', 'newznab_host', '')
|
||||
NEWZNAB_APIKEY = check_setting_str(CFG, 'Newznab', 'newznab_apikey', '')
|
||||
NEWZNAB_ENABLED = bool(check_setting_int(CFG, 'Newznab', 'newznab_enabled', 1))
|
||||
|
||||
|
||||
# Need to pack the extra newznabs back into a list of tuples
|
||||
flattened_newznabs = check_setting_str(CFG, 'Newznab', 'extra_newznabs', [], log=False)
|
||||
EXTRA_NEWZNABS = list(itertools.izip(*[itertools.islice(flattened_newznabs, i, None, 3) for i in range(3)]))
|
||||
|
||||
|
||||
NZBSORG = bool(check_setting_int(CFG, 'NZBsorg', 'nzbsorg', 0))
|
||||
NZBSORG_UID = check_setting_str(CFG, 'NZBsorg', 'nzbsorg_uid', '')
|
||||
NZBSORG_HASH = check_setting_str(CFG, 'NZBsorg', 'nzbsorg_hash', '')
|
||||
@@ -407,18 +415,20 @@ def initialize():
|
||||
NEWZBIN = bool(check_setting_int(CFG, 'Newzbin', 'newzbin', 0))
|
||||
NEWZBIN_UID = check_setting_str(CFG, 'Newzbin', 'newzbin_uid', '')
|
||||
NEWZBIN_PASSWORD = check_setting_str(CFG, 'Newzbin', 'newzbin_password', '')
|
||||
|
||||
|
||||
NZBSRUS = bool(check_setting_int(CFG, 'NZBsRus', 'nzbsrus', 0))
|
||||
NZBSRUS_UID = check_setting_str(CFG, 'NZBsRus', 'nzbsrus_uid', '')
|
||||
NZBSRUS_APIKEY = check_setting_str(CFG, 'NZBsRus', 'nzbsrus_apikey', '')
|
||||
|
||||
NZBX = bool(check_setting_int(CFG, 'nzbX', 'nzbx', 0))
|
||||
|
||||
LASTFM_USERNAME = check_setting_str(CFG, 'General', 'lastfm_username', '')
|
||||
|
||||
|
||||
INTERFACE = check_setting_str(CFG, 'General', 'interface', 'default')
|
||||
FOLDER_PERMISSIONS = check_setting_str(CFG, 'General', 'folder_permissions', '0755')
|
||||
|
||||
|
||||
ENCODERFOLDER = check_setting_str(CFG, 'General', 'encoderfolder', '')
|
||||
ENCODER_PATH = check_setting_str(CFG, 'General', 'encoder_path', '')
|
||||
ENCODER_PATH = check_setting_str(CFG, 'General', 'encoder_path', '')
|
||||
ENCODER = check_setting_str(CFG, 'General', 'encoder', 'ffmpeg')
|
||||
XLDPROFILE = check_setting_str(CFG, 'General', 'xldprofile', '')
|
||||
BITRATE = check_setting_int(CFG, 'General', 'bitrate', 192)
|
||||
@@ -433,28 +443,28 @@ def initialize():
|
||||
|
||||
PROWL_ENABLED = bool(check_setting_int(CFG, 'Prowl', 'prowl_enabled', 0))
|
||||
PROWL_KEYS = check_setting_str(CFG, 'Prowl', 'prowl_keys', '')
|
||||
PROWL_ONSNATCH = bool(check_setting_int(CFG, 'Prowl', 'prowl_onsnatch', 0))
|
||||
PROWL_ONSNATCH = bool(check_setting_int(CFG, 'Prowl', 'prowl_onsnatch', 0))
|
||||
PROWL_PRIORITY = check_setting_int(CFG, 'Prowl', 'prowl_priority', 0)
|
||||
|
||||
|
||||
XBMC_ENABLED = bool(check_setting_int(CFG, 'XBMC', 'xbmc_enabled', 0))
|
||||
XBMC_HOST = check_setting_str(CFG, 'XBMC', 'xbmc_host', '')
|
||||
XBMC_USERNAME = check_setting_str(CFG, 'XBMC', 'xbmc_username', '')
|
||||
XBMC_PASSWORD = check_setting_str(CFG, 'XBMC', 'xbmc_password', '')
|
||||
XBMC_UPDATE = bool(check_setting_int(CFG, 'XBMC', 'xbmc_update', 0))
|
||||
XBMC_NOTIFY = bool(check_setting_int(CFG, 'XBMC', 'xbmc_notify', 0))
|
||||
|
||||
|
||||
NMA_ENABLED = bool(check_setting_int(CFG, 'NMA', 'nma_enabled', 0))
|
||||
NMA_APIKEY = check_setting_str(CFG, 'NMA', 'nma_apikey', '')
|
||||
NMA_PRIORITY = check_setting_int(CFG, 'NMA', 'nma_priority', 0)
|
||||
NMA_ONSNATCH = bool(check_setting_int(CFG, 'NMA', 'nma_onsnatch', 0))
|
||||
|
||||
NMA_ONSNATCH = bool(check_setting_int(CFG, 'NMA', 'nma_onsnatch', 0))
|
||||
|
||||
SYNOINDEX_ENABLED = bool(check_setting_int(CFG, 'Synoindex', 'synoindex_enabled', 0))
|
||||
|
||||
|
||||
PUSHOVER_ENABLED = bool(check_setting_int(CFG, 'Pushover', 'pushover_enabled', 0))
|
||||
PUSHOVER_KEYS = check_setting_str(CFG, 'Pushover', 'pushover_keys', '')
|
||||
PUSHOVER_ONSNATCH = bool(check_setting_int(CFG, 'Pushover', 'pushover_onsnatch', 0))
|
||||
PUSHOVER_ONSNATCH = bool(check_setting_int(CFG, 'Pushover', 'pushover_onsnatch', 0))
|
||||
PUSHOVER_PRIORITY = check_setting_int(CFG, 'Pushover', 'pushover_priority', 0)
|
||||
|
||||
|
||||
MIRROR = check_setting_str(CFG, 'General', 'mirror', 'musicbrainz.org')
|
||||
CUSTOMHOST = check_setting_str(CFG, 'General', 'customhost', 'localhost')
|
||||
CUSTOMPORT = check_setting_int(CFG, 'General', 'customport', 5000)
|
||||
@@ -463,9 +473,9 @@ def initialize():
|
||||
HPPASS = check_setting_str(CFG, 'General', 'hppass', '')
|
||||
|
||||
CACHE_SIZEMB = check_setting_int(CFG,'Advanced','cache_sizemb',32)
|
||||
|
||||
|
||||
ALBUM_COMPLETION_PCT = check_setting_int(CFG, 'Advanced', 'album_completion_pct', 80)
|
||||
|
||||
|
||||
# update folder formats in the config & bump up config version
|
||||
if CONFIG_VERSION == '0':
|
||||
from headphones.helpers import replace_all
|
||||
@@ -473,9 +483,9 @@ def initialize():
|
||||
folder_values = { 'artist' : 'Artist', 'album':'Album', 'year' : 'Year', 'releasetype' : 'Type', 'first' : 'First', 'lowerfirst' : 'first' }
|
||||
FILE_FORMAT = replace_all(FILE_FORMAT, file_values)
|
||||
FOLDER_FORMAT = replace_all(FOLDER_FORMAT, folder_values)
|
||||
|
||||
|
||||
CONFIG_VERSION = '1'
|
||||
|
||||
|
||||
if CONFIG_VERSION == '1':
|
||||
|
||||
from headphones.helpers import replace_all
|
||||
@@ -501,32 +511,32 @@ def initialize():
|
||||
'year': '$year',
|
||||
'type': '$type',
|
||||
'first': '$first'
|
||||
}
|
||||
}
|
||||
FILE_FORMAT = replace_all(FILE_FORMAT, file_values)
|
||||
FOLDER_FORMAT = replace_all(FOLDER_FORMAT, folder_values)
|
||||
|
||||
|
||||
CONFIG_VERSION = '2'
|
||||
|
||||
|
||||
if CONFIG_VERSION == '2':
|
||||
|
||||
|
||||
# Update the config to use direct path to the encoder rather than the encoder folder
|
||||
if ENCODERFOLDER:
|
||||
ENCODER_PATH = os.path.join(ENCODERFOLDER, ENCODER)
|
||||
CONFIG_VERSION = '3'
|
||||
|
||||
|
||||
if not LOG_DIR:
|
||||
LOG_DIR = os.path.join(DATA_DIR, 'logs')
|
||||
|
||||
|
||||
if not os.path.exists(LOG_DIR):
|
||||
try:
|
||||
os.makedirs(LOG_DIR)
|
||||
except OSError:
|
||||
if VERBOSE:
|
||||
print 'Unable to create the log directory. Logging to screen only.'
|
||||
|
||||
|
||||
# Start the logger, silence console logging if we need to
|
||||
logger.headphones_log.initLogger(verbose=VERBOSE)
|
||||
|
||||
|
||||
if not CACHE_DIR:
|
||||
# Put the cache dir in the data dir for now
|
||||
CACHE_DIR = os.path.join(DATA_DIR, 'cache')
|
||||
@@ -535,23 +545,23 @@ def initialize():
|
||||
os.makedirs(CACHE_DIR)
|
||||
except OSError:
|
||||
logger.error('Could not create cache dir. Check permissions of datadir: ' + DATA_DIR)
|
||||
|
||||
|
||||
# Sanity check for search interval. Set it to at least 6 hours
|
||||
if SEARCH_INTERVAL < 360:
|
||||
logger.info("Search interval too low. Resetting to 6 hour minimum")
|
||||
SEARCH_INTERVAL = 360
|
||||
|
||||
|
||||
# Initialize the database
|
||||
logger.info('Checking to see if the database has all tables....')
|
||||
try:
|
||||
dbcheck()
|
||||
except Exception, e:
|
||||
logger.error("Can't connect to the database: %s" % e)
|
||||
|
||||
|
||||
# Get the currently installed version - returns None, 'win32' or the git hash
|
||||
# Also sets INSTALL_TYPE variable to 'win', 'git' or 'source'
|
||||
CURRENT_VERSION = versioncheck.getVersion()
|
||||
|
||||
|
||||
# Check for new versions
|
||||
if CHECK_GITHUB_ON_STARTUP:
|
||||
try:
|
||||
@@ -563,62 +573,62 @@ def initialize():
|
||||
|
||||
__INITIALIZED__ = True
|
||||
return True
|
||||
|
||||
|
||||
def daemonize():
|
||||
|
||||
if threading.activeCount() != 1:
|
||||
logger.warn('There are %r active threads. Daemonizing may cause \
|
||||
strange behavior.' % threading.enumerate())
|
||||
|
||||
|
||||
sys.stdout.flush()
|
||||
sys.stderr.flush()
|
||||
|
||||
|
||||
# Do first fork
|
||||
try:
|
||||
pid = os.fork()
|
||||
if pid == 0:
|
||||
pass
|
||||
else:
|
||||
# Exit the parent process
|
||||
logger.debug('Forking once...')
|
||||
os._exit(0)
|
||||
pid = os.fork() # @UndefinedVariable - only available in UNIX
|
||||
if pid != 0:
|
||||
sys.exit(0)
|
||||
except OSError, e:
|
||||
sys.exit("1st fork failed: %s [%d]" % (e.strerror, e.errno))
|
||||
|
||||
raise RuntimeError("1st fork failed: %s [%d]" % (e.strerror, e.errno))
|
||||
|
||||
os.setsid()
|
||||
|
||||
# Do second fork
|
||||
try:
|
||||
pid = os.fork()
|
||||
if pid > 0:
|
||||
logger.debug('Forking twice...')
|
||||
os._exit(0) # Exit second parent process
|
||||
except OSError, e:
|
||||
sys.exit("2nd fork failed: %s [%d]" % (e.strerror, e.errno))
|
||||
# Make sure I can read my own files and shut out others
|
||||
prev = os.umask(0) # @UndefinedVariable - only available in UNIX
|
||||
os.umask(prev and int('077', 8))
|
||||
|
||||
# Make the child a session-leader by detaching from the terminal
|
||||
try:
|
||||
pid = os.fork() # @UndefinedVariable - only available in UNIX
|
||||
if pid != 0:
|
||||
sys.exit(0)
|
||||
except OSError, e:
|
||||
raise RuntimeError("2nd fork failed: %s [%d]" % (e.strerror, e.errno))
|
||||
|
||||
dev_null = file('/dev/null', 'r')
|
||||
os.dup2(dev_null.fileno(), sys.stdin.fileno())
|
||||
|
||||
os.chdir("/")
|
||||
os.umask(0)
|
||||
|
||||
si = open('/dev/null', "r")
|
||||
so = open('/dev/null', "a+")
|
||||
se = open('/dev/null', "a+")
|
||||
|
||||
|
||||
os.dup2(si.fileno(), sys.stdin.fileno())
|
||||
os.dup2(so.fileno(), sys.stdout.fileno())
|
||||
os.dup2(se.fileno(), sys.stderr.fileno())
|
||||
|
||||
pid = os.getpid()
|
||||
pid = str(os.getpid())
|
||||
logger.info('Daemonized to PID: %s' % pid)
|
||||
if PIDFILE:
|
||||
logger.info('Writing PID %s to %s' % (pid, PIDFILE))
|
||||
|
||||
if CREATEPID:
|
||||
logger.info("Writing PID " + pid + " to " + str(PIDFILE))
|
||||
file(PIDFILE, 'w').write("%s\n" % pid)
|
||||
|
||||
def launch_browser(host, port, root):
|
||||
|
||||
if host == '0.0.0.0':
|
||||
host = 'localhost'
|
||||
|
||||
try:
|
||||
|
||||
try:
|
||||
webbrowser.open('http://%s:%i%s' % (host, port, root))
|
||||
except Exception, e:
|
||||
logger.error('Could not launch browser: %s' % e)
|
||||
@@ -642,7 +652,9 @@ def config_write():
|
||||
new_config['General']['log_dir'] = LOG_DIR
|
||||
new_config['General']['cache_dir'] = CACHE_DIR
|
||||
new_config['General']['git_path'] = GIT_PATH
|
||||
|
||||
new_config['General']['git_user'] = GIT_USER
|
||||
new_config['General']['git_branch'] = GIT_BRANCH
|
||||
|
||||
new_config['General']['check_github'] = int(CHECK_GITHUB)
|
||||
new_config['General']['check_github_on_startup'] = int(CHECK_GITHUB_ON_STARTUP)
|
||||
new_config['General']['check_github_interval'] = CHECK_GITHUB_INTERVAL
|
||||
@@ -674,7 +686,7 @@ def config_write():
|
||||
new_config['General']['autowant_upcoming'] = int(AUTOWANT_UPCOMING)
|
||||
new_config['General']['autowant_all'] = int(AUTOWANT_ALL)
|
||||
new_config['General']['keep_torrent_files'] = int(KEEP_TORRENT_FILES)
|
||||
|
||||
|
||||
new_config['General']['numberofseeders'] = NUMBEROFSEEDERS
|
||||
new_config['General']['torrentblackhole_dir'] = TORRENTBLACKHOLE_DIR
|
||||
new_config['General']['isohunt'] = int(ISOHUNT)
|
||||
@@ -686,7 +698,7 @@ def config_write():
|
||||
new_config['Waffles']['waffles'] = int(WAFFLES)
|
||||
new_config['Waffles']['waffles_uid'] = WAFFLES_UID
|
||||
new_config['Waffles']['waffles_passkey'] = WAFFLES_PASSKEY
|
||||
|
||||
|
||||
new_config['Rutracker'] = {}
|
||||
new_config['Rutracker']['rutracker'] = int(RUTRACKER)
|
||||
new_config['Rutracker']['rutracker_user'] = RUTRACKER_USER
|
||||
@@ -724,30 +736,33 @@ def config_write():
|
||||
for newznab in EXTRA_NEWZNABS:
|
||||
for item in newznab:
|
||||
flattened_newznabs.append(item)
|
||||
|
||||
|
||||
new_config['Newznab']['extra_newznabs'] = flattened_newznabs
|
||||
|
||||
new_config['NZBsorg'] = {}
|
||||
new_config['NZBsorg']['nzbsorg'] = int(NZBSORG)
|
||||
new_config['NZBsorg']['nzbsorg_uid'] = NZBSORG_UID
|
||||
new_config['NZBsorg']['nzbsorg_hash'] = NZBSORG_HASH
|
||||
|
||||
|
||||
new_config['Newzbin'] = {}
|
||||
new_config['Newzbin']['newzbin'] = int(NEWZBIN)
|
||||
new_config['Newzbin']['newzbin_uid'] = NEWZBIN_UID
|
||||
new_config['Newzbin']['newzbin_password'] = NEWZBIN_PASSWORD
|
||||
|
||||
|
||||
new_config['NZBsRus'] = {}
|
||||
new_config['NZBsRus']['nzbsrus'] = int(NZBSRUS)
|
||||
new_config['NZBsRus']['nzbsrus_uid'] = NZBSRUS_UID
|
||||
new_config['NZBsRus']['nzbsrus_apikey'] = NZBSRUS_APIKEY
|
||||
|
||||
new_config['nzbX'] = {}
|
||||
new_config['nzbX']['nzbx'] = int(NZBX)
|
||||
|
||||
new_config['Prowl'] = {}
|
||||
new_config['Prowl']['prowl_enabled'] = int(PROWL_ENABLED)
|
||||
new_config['Prowl']['prowl_keys'] = PROWL_KEYS
|
||||
new_config['Prowl']['prowl_onsnatch'] = int(PROWL_ONSNATCH)
|
||||
new_config['Prowl']['prowl_priority'] = int(PROWL_PRIORITY)
|
||||
|
||||
|
||||
new_config['XBMC'] = {}
|
||||
new_config['XBMC']['xbmc_enabled'] = int(XBMC_ENABLED)
|
||||
new_config['XBMC']['xbmc_host'] = XBMC_HOST
|
||||
@@ -755,7 +770,7 @@ def config_write():
|
||||
new_config['XBMC']['xbmc_password'] = XBMC_PASSWORD
|
||||
new_config['XBMC']['xbmc_update'] = int(XBMC_UPDATE)
|
||||
new_config['XBMC']['xbmc_notify'] = int(XBMC_NOTIFY)
|
||||
|
||||
|
||||
new_config['NMA'] = {}
|
||||
new_config['NMA']['nma_enabled'] = int(NMA_ENABLED)
|
||||
new_config['NMA']['nma_apikey'] = NMA_APIKEY
|
||||
@@ -767,10 +782,10 @@ def config_write():
|
||||
new_config['Pushover']['pushover_keys'] = PUSHOVER_KEYS
|
||||
new_config['Pushover']['pushover_onsnatch'] = int(PUSHOVER_ONSNATCH)
|
||||
new_config['Pushover']['pushover_priority'] = int(PUSHOVER_PRIORITY)
|
||||
|
||||
|
||||
new_config['Synoindex'] = {}
|
||||
new_config['Synoindex']['synoindex_enabled'] = int(SYNOINDEX_ENABLED)
|
||||
|
||||
|
||||
new_config['General']['lastfm_username'] = LASTFM_USERNAME
|
||||
new_config['General']['interface'] = INTERFACE
|
||||
new_config['General']['folder_permissions'] = FOLDER_PERMISSIONS
|
||||
@@ -787,49 +802,54 @@ def config_write():
|
||||
new_config['General']['encodervbrcbr'] = ENCODERVBRCBR
|
||||
new_config['General']['encoderlossless'] = int(ENCODERLOSSLESS)
|
||||
new_config['General']['delete_lossless_files'] = int(DELETE_LOSSLESS_FILES)
|
||||
|
||||
|
||||
new_config['General']['mirror'] = MIRROR
|
||||
new_config['General']['customhost'] = CUSTOMHOST
|
||||
new_config['General']['customport'] = CUSTOMPORT
|
||||
new_config['General']['customsleep'] = CUSTOMSLEEP
|
||||
new_config['General']['hpuser'] = HPUSER
|
||||
new_config['General']['hppass'] = HPPASS
|
||||
|
||||
|
||||
new_config['Advanced'] = {}
|
||||
new_config['Advanced']['album_completion_pct'] = ALBUM_COMPLETION_PCT
|
||||
new_config['Advanced']['cache_sizemb'] = CACHE_SIZEMB
|
||||
|
||||
|
||||
new_config.write()
|
||||
|
||||
|
||||
|
||||
def start():
|
||||
|
||||
|
||||
global __INITIALIZED__, started
|
||||
|
||||
|
||||
if __INITIALIZED__:
|
||||
|
||||
|
||||
# Start our scheduled background tasks
|
||||
from headphones import updater, searcher, librarysync, postprocessor
|
||||
|
||||
SCHED.add_interval_job(updater.dbUpdate, hours=24)
|
||||
SCHED.add_interval_job(searcher.searchforalbum, minutes=SEARCH_INTERVAL)
|
||||
SCHED.add_interval_job(librarysync.libraryScan, minutes=LIBRARYSCAN_INTERVAL, kwargs={'cron':True})
|
||||
|
||||
|
||||
if CHECK_GITHUB:
|
||||
SCHED.add_interval_job(versioncheck.checkGithub, minutes=CHECK_GITHUB_INTERVAL)
|
||||
|
||||
|
||||
SCHED.add_interval_job(postprocessor.checkFolder, minutes=DOWNLOAD_SCAN_INTERVAL)
|
||||
|
||||
SCHED.start()
|
||||
|
||||
|
||||
started = True
|
||||
|
||||
|
||||
def sig_handler(signum=None, frame=None):
|
||||
if type(signum) != type(None):
|
||||
logger.info("Signal %i caught, saving and exiting..." % int(signum))
|
||||
shutdown()
|
||||
|
||||
def dbcheck():
|
||||
|
||||
conn=sqlite3.connect(DB_FILE)
|
||||
c=conn.cursor()
|
||||
c.execute('CREATE TABLE IF NOT EXISTS artists (ArtistID TEXT UNIQUE, ArtistName TEXT, ArtistSortName TEXT, DateAdded TEXT, Status TEXT, IncludeExtras INTEGER, LatestAlbum TEXT, ReleaseDate TEXT, AlbumID TEXT, HaveTracks INTEGER, TotalTracks INTEGER, LastUpdated TEXT, ArtworkURL TEXT, ThumbURL TEXT, Extras TEXT)')
|
||||
c.execute('CREATE TABLE IF NOT EXISTS albums (ArtistID TEXT, ArtistName TEXT, AlbumTitle TEXT, AlbumASIN TEXT, ReleaseDate TEXT, DateAdded TEXT, AlbumID TEXT UNIQUE, Status TEXT, Type TEXT, ArtworkURL TEXT, ThumbURL TEXT, ReleaseID TEXT, ReleaseCountry TEXT, ReleaseFormat TEXT)') # ReleaseFormat here means CD,Digital,Vinyl, etc. If using the default Headphones hybrid release, ReleaseID will equal AlbumID (AlbumID is releasegroup id)
|
||||
c.execute('CREATE TABLE IF NOT EXISTS albums (ArtistID TEXT, ArtistName TEXT, AlbumTitle TEXT, AlbumASIN TEXT, ReleaseDate TEXT, DateAdded TEXT, AlbumID TEXT UNIQUE, Status TEXT, Type TEXT, ArtworkURL TEXT, ThumbURL TEXT, ReleaseID TEXT, ReleaseCountry TEXT, ReleaseFormat TEXT, SearchTerm TEXT)') # ReleaseFormat here means CD,Digital,Vinyl, etc. If using the default Headphones hybrid release, ReleaseID will equal AlbumID (AlbumID is releasegroup id)
|
||||
c.execute('CREATE TABLE IF NOT EXISTS tracks (ArtistID TEXT, ArtistName TEXT, AlbumTitle TEXT, AlbumASIN TEXT, AlbumID TEXT, TrackTitle TEXT, TrackDuration, TrackID TEXT, TrackNumber INTEGER, Location TEXT, BitRate INTEGER, CleanName TEXT, Format TEXT, ReleaseID TEXT)') # Format here means mp3, flac, etc.
|
||||
c.execute('CREATE TABLE IF NOT EXISTS allalbums (ArtistID TEXT, ArtistName TEXT, AlbumTitle TEXT, AlbumASIN TEXT, ReleaseDate TEXT, AlbumID TEXT, Type TEXT, ReleaseID TEXT, ReleaseCountry TEXT, ReleaseFormat TEXT)')
|
||||
c.execute('CREATE TABLE IF NOT EXISTS alltracks (ArtistID TEXT, ArtistName TEXT, AlbumTitle TEXT, AlbumASIN TEXT, AlbumID TEXT, TrackTitle TEXT, TrackDuration, TrackID TEXT, TrackNumber INTEGER, Location TEXT, BitRate INTEGER, CleanName TEXT, Format TEXT, ReleaseID TEXT)')
|
||||
@@ -842,37 +862,37 @@ def dbcheck():
|
||||
c.execute('CREATE TABLE IF NOT EXISTS releases (ReleaseID TEXT, ReleaseGroupID TEXT, UNIQUE(ReleaseID, ReleaseGroupID))')
|
||||
c.execute('CREATE INDEX IF NOT EXISTS tracks_albumid ON tracks(AlbumID ASC)')
|
||||
c.execute('CREATE INDEX IF NOT EXISTS album_artistid_reldate ON albums(ArtistID ASC, ReleaseDate DESC)')
|
||||
|
||||
|
||||
try:
|
||||
c.execute('SELECT IncludeExtras from artists')
|
||||
except sqlite3.OperationalError:
|
||||
c.execute('ALTER TABLE artists ADD COLUMN IncludeExtras INTEGER DEFAULT 0')
|
||||
|
||||
|
||||
try:
|
||||
c.execute('SELECT LatestAlbum from artists')
|
||||
except sqlite3.OperationalError:
|
||||
c.execute('ALTER TABLE artists ADD COLUMN LatestAlbum TEXT')
|
||||
|
||||
|
||||
try:
|
||||
c.execute('SELECT ReleaseDate from artists')
|
||||
except sqlite3.OperationalError:
|
||||
c.execute('ALTER TABLE artists ADD COLUMN ReleaseDate TEXT')
|
||||
|
||||
|
||||
try:
|
||||
c.execute('SELECT AlbumID from artists')
|
||||
except sqlite3.OperationalError:
|
||||
c.execute('ALTER TABLE artists ADD COLUMN AlbumID TEXT')
|
||||
|
||||
|
||||
try:
|
||||
c.execute('SELECT HaveTracks from artists')
|
||||
except sqlite3.OperationalError:
|
||||
c.execute('ALTER TABLE artists ADD COLUMN HaveTracks INTEGER DEFAULT 0')
|
||||
|
||||
|
||||
try:
|
||||
c.execute('SELECT TotalTracks from artists')
|
||||
except sqlite3.OperationalError:
|
||||
c.execute('ALTER TABLE artists ADD COLUMN TotalTracks INTEGER DEFAULT 0')
|
||||
|
||||
|
||||
try:
|
||||
c.execute('SELECT Type from albums')
|
||||
except sqlite3.OperationalError:
|
||||
@@ -882,108 +902,108 @@ def dbcheck():
|
||||
c.execute('SELECT TrackNumber from tracks')
|
||||
except sqlite3.OperationalError:
|
||||
c.execute('ALTER TABLE tracks ADD COLUMN TrackNumber INTEGER')
|
||||
|
||||
|
||||
try:
|
||||
c.execute('SELECT FolderName from snatched')
|
||||
except sqlite3.OperationalError:
|
||||
c.execute('ALTER TABLE snatched ADD COLUMN FolderName TEXT')
|
||||
|
||||
|
||||
try:
|
||||
c.execute('SELECT Location from tracks')
|
||||
except sqlite3.OperationalError:
|
||||
c.execute('ALTER TABLE tracks ADD COLUMN Location TEXT')
|
||||
|
||||
|
||||
try:
|
||||
c.execute('SELECT Location from have')
|
||||
except sqlite3.OperationalError:
|
||||
c.execute('ALTER TABLE have ADD COLUMN Location TEXT')
|
||||
|
||||
|
||||
try:
|
||||
c.execute('SELECT BitRate from tracks')
|
||||
except sqlite3.OperationalError:
|
||||
c.execute('ALTER TABLE tracks ADD COLUMN BitRate INTEGER')
|
||||
|
||||
c.execute('ALTER TABLE tracks ADD COLUMN BitRate INTEGER')
|
||||
|
||||
try:
|
||||
c.execute('SELECT CleanName from tracks')
|
||||
except sqlite3.OperationalError:
|
||||
c.execute('ALTER TABLE tracks ADD COLUMN CleanName TEXT')
|
||||
|
||||
c.execute('ALTER TABLE tracks ADD COLUMN CleanName TEXT')
|
||||
|
||||
try:
|
||||
c.execute('SELECT CleanName from have')
|
||||
except sqlite3.OperationalError:
|
||||
c.execute('ALTER TABLE have ADD COLUMN CleanName TEXT')
|
||||
|
||||
c.execute('ALTER TABLE have ADD COLUMN CleanName TEXT')
|
||||
|
||||
# Add the Format column
|
||||
try:
|
||||
c.execute('SELECT Format from have')
|
||||
except sqlite3.OperationalError:
|
||||
c.execute('ALTER TABLE have ADD COLUMN Format TEXT DEFAULT NULL')
|
||||
|
||||
c.execute('ALTER TABLE have ADD COLUMN Format TEXT DEFAULT NULL')
|
||||
|
||||
try:
|
||||
c.execute('SELECT Format from tracks')
|
||||
except sqlite3.OperationalError:
|
||||
c.execute('ALTER TABLE tracks ADD COLUMN Format TEXT DEFAULT NULL')
|
||||
|
||||
|
||||
try:
|
||||
c.execute('SELECT LastUpdated from artists')
|
||||
except sqlite3.OperationalError:
|
||||
c.execute('ALTER TABLE artists ADD COLUMN LastUpdated TEXT DEFAULT NULL')
|
||||
|
||||
|
||||
try:
|
||||
c.execute('SELECT ArtworkURL from artists')
|
||||
except sqlite3.OperationalError:
|
||||
c.execute('ALTER TABLE artists ADD COLUMN ArtworkURL TEXT DEFAULT NULL')
|
||||
|
||||
c.execute('ALTER TABLE artists ADD COLUMN ArtworkURL TEXT DEFAULT NULL')
|
||||
|
||||
try:
|
||||
c.execute('SELECT ArtworkURL from albums')
|
||||
except sqlite3.OperationalError:
|
||||
c.execute('ALTER TABLE albums ADD COLUMN ArtworkURL TEXT DEFAULT NULL')
|
||||
|
||||
c.execute('ALTER TABLE albums ADD COLUMN ArtworkURL TEXT DEFAULT NULL')
|
||||
|
||||
try:
|
||||
c.execute('SELECT ThumbURL from artists')
|
||||
except sqlite3.OperationalError:
|
||||
c.execute('ALTER TABLE artists ADD COLUMN ThumbURL TEXT DEFAULT NULL')
|
||||
|
||||
c.execute('ALTER TABLE artists ADD COLUMN ThumbURL TEXT DEFAULT NULL')
|
||||
|
||||
try:
|
||||
c.execute('SELECT ThumbURL from albums')
|
||||
except sqlite3.OperationalError:
|
||||
c.execute('ALTER TABLE albums ADD COLUMN ThumbURL TEXT DEFAULT NULL')
|
||||
|
||||
c.execute('ALTER TABLE albums ADD COLUMN ThumbURL TEXT DEFAULT NULL')
|
||||
|
||||
try:
|
||||
c.execute('SELECT ArtistID from descriptions')
|
||||
except sqlite3.OperationalError:
|
||||
c.execute('ALTER TABLE descriptions ADD COLUMN ArtistID TEXT DEFAULT NULL')
|
||||
|
||||
c.execute('ALTER TABLE descriptions ADD COLUMN ArtistID TEXT DEFAULT NULL')
|
||||
|
||||
try:
|
||||
c.execute('SELECT LastUpdated from descriptions')
|
||||
except sqlite3.OperationalError:
|
||||
c.execute('ALTER TABLE descriptions ADD COLUMN LastUpdated TEXT DEFAULT NULL')
|
||||
|
||||
c.execute('ALTER TABLE descriptions ADD COLUMN LastUpdated TEXT DEFAULT NULL')
|
||||
|
||||
try:
|
||||
c.execute('SELECT ReleaseID from albums')
|
||||
except sqlite3.OperationalError:
|
||||
c.execute('ALTER TABLE albums ADD COLUMN ReleaseID TEXT DEFAULT NULL')
|
||||
|
||||
|
||||
try:
|
||||
c.execute('SELECT ReleaseFormat from albums')
|
||||
except sqlite3.OperationalError:
|
||||
c.execute('ALTER TABLE albums ADD COLUMN ReleaseFormat TEXT DEFAULT NULL')
|
||||
|
||||
|
||||
try:
|
||||
c.execute('SELECT ReleaseCountry from albums')
|
||||
except sqlite3.OperationalError:
|
||||
c.execute('ALTER TABLE albums ADD COLUMN ReleaseCountry TEXT DEFAULT NULL')
|
||||
|
||||
|
||||
try:
|
||||
c.execute('SELECT ReleaseID from tracks')
|
||||
except sqlite3.OperationalError:
|
||||
c.execute('ALTER TABLE tracks ADD COLUMN ReleaseID TEXT DEFAULT NULL')
|
||||
|
||||
|
||||
try:
|
||||
c.execute('SELECT Matched from have')
|
||||
except sqlite3.OperationalError:
|
||||
c.execute('ALTER TABLE have ADD COLUMN Matched TEXT DEFAULT NULL')
|
||||
|
||||
|
||||
try:
|
||||
c.execute('SELECT Extras from artists')
|
||||
except sqlite3.OperationalError:
|
||||
@@ -1001,31 +1021,37 @@ def dbcheck():
|
||||
c.execute('SELECT Kind from snatched')
|
||||
except sqlite3.OperationalError:
|
||||
c.execute('ALTER TABLE snatched ADD COLUMN Kind TEXT DEFAULT NULL')
|
||||
|
||||
try:
|
||||
c.execute('SELECT SearchTerm from albums')
|
||||
except sqlite3.OperationalError:
|
||||
c.execute('ALTER TABLE albums ADD COLUMN SearchTerm TEXT DEFAULT NULL')
|
||||
|
||||
conn.commit()
|
||||
c.close()
|
||||
|
||||
|
||||
|
||||
def shutdown(restart=False, update=False):
|
||||
|
||||
cherrypy.engine.exit()
|
||||
SCHED.shutdown(wait=False)
|
||||
|
||||
|
||||
config_write()
|
||||
|
||||
|
||||
if not restart and not update:
|
||||
logger.info('Headphones is shutting down...')
|
||||
|
||||
if update:
|
||||
logger.info('Headphones is updating...')
|
||||
try:
|
||||
versioncheck.update()
|
||||
except Exception, e:
|
||||
logger.warn('Headphones failed to update: %s. Restarting.' % e)
|
||||
logger.warn('Headphones failed to update: %s. Restarting.' % e)
|
||||
|
||||
if PIDFILE :
|
||||
if CREATEPID :
|
||||
logger.info ('Removing pidfile %s' % PIDFILE)
|
||||
os.remove(PIDFILE)
|
||||
|
||||
|
||||
if restart:
|
||||
logger.info('Headphones is restarting...')
|
||||
popen_list = [sys.executable, FULL_PATH]
|
||||
@@ -1034,5 +1060,5 @@ def shutdown(restart=False, update=False):
|
||||
popen_list += ['--nolaunch']
|
||||
logger.info('Restarting Headphones with ' + str(popen_list))
|
||||
subprocess.Popen(popen_list, cwd=os.getcwd())
|
||||
|
||||
|
||||
os._exit(0)
|
||||
|
||||
@@ -21,6 +21,7 @@ from lib.pygazelle import format as gazelleformat
|
||||
from lib.pygazelle import media as gazellemedia
|
||||
from xml.dom import minidom
|
||||
from xml.parsers.expat import ExpatError
|
||||
import lib.simplejson as json
|
||||
from StringIO import StringIO
|
||||
import gzip
|
||||
|
||||
@@ -135,9 +136,9 @@ def searchNZB(albumid=None, new=False, losslessOnly=False):
|
||||
myDB = db.DBConnection()
|
||||
|
||||
if albumid:
|
||||
results = myDB.select('SELECT ArtistName, AlbumTitle, AlbumID, ReleaseDate, Type from albums WHERE AlbumID=?', [albumid])
|
||||
results = myDB.select('SELECT ArtistName, AlbumTitle, AlbumID, ReleaseDate, Type, SearchTerm from albums WHERE AlbumID=?', [albumid])
|
||||
else:
|
||||
results = myDB.select('SELECT ArtistName, AlbumTitle, AlbumID, ReleaseDate, Type from albums WHERE Status="Wanted" OR Status="Wanted Lossless"')
|
||||
results = myDB.select('SELECT ArtistName, AlbumTitle, AlbumID, ReleaseDate, Type, SearchTerm from albums WHERE Status="Wanted" OR Status="Wanted Lossless"')
|
||||
new = True
|
||||
|
||||
for albums in results:
|
||||
@@ -154,19 +155,25 @@ def searchNZB(albumid=None, new=False, losslessOnly=False):
|
||||
|
||||
cleanalbum = helpers.latinToAscii(helpers.replace_all(albums[1], dic))
|
||||
cleanartist = helpers.latinToAscii(helpers.replace_all(albums[0], dic))
|
||||
|
||||
# FLAC usually doesn't have a year for some reason so I'll leave it out
|
||||
# Various Artist albums might be listed as VA, so I'll leave that out too
|
||||
# Only use the year if the term could return a bunch of different albums, i.e. self-titled albums
|
||||
if albums[0] in albums[1] or len(albums[0]) < 4 or len(albums[1]) < 4:
|
||||
term = cleanartist + ' ' + cleanalbum + ' ' + year
|
||||
elif albums[0] == 'Various Artists':
|
||||
term = cleanalbum + ' ' + year
|
||||
|
||||
# Use the provided search term if available, otherwise build a search term
|
||||
if albums[5]:
|
||||
term = albums[5]
|
||||
|
||||
else:
|
||||
term = cleanartist + ' ' + cleanalbum
|
||||
# FLAC usually doesn't have a year for some reason so I'll leave it out
|
||||
# Various Artist albums might be listed as VA, so I'll leave that out too
|
||||
# Only use the year if the term could return a bunch of different albums, i.e. self-titled albums
|
||||
if albums[0] in albums[1] or len(albums[0]) < 4 or len(albums[1]) < 4:
|
||||
term = cleanartist + ' ' + cleanalbum + ' ' + year
|
||||
elif albums[0] == 'Various Artists':
|
||||
term = cleanalbum + ' ' + year
|
||||
else:
|
||||
term = cleanartist + ' ' + cleanalbum
|
||||
|
||||
# Replace bad characters in the term and unicode it
|
||||
term = re.sub('[\.\-\/]', ' ', term).encode('utf-8')
|
||||
|
||||
artistterm = re.sub('[\.\-\/]', ' ', cleanartist).encode('utf-8')
|
||||
|
||||
logger.info("Searching for %s since it was marked as wanted" % term)
|
||||
@@ -419,6 +426,55 @@ def searchNZB(albumid=None, new=False, losslessOnly=False):
|
||||
|
||||
except Exception, e:
|
||||
logger.error(u"An unknown error occurred trying to parse the feed: %s" % e)
|
||||
|
||||
if headphones.NZBX:
|
||||
provider = "nzbx"
|
||||
if headphones.PREFERRED_QUALITY == 3 or losslessOnly:
|
||||
categories = "3040"
|
||||
elif headphones.PREFERRED_QUALITY:
|
||||
categories = "3040,3010"
|
||||
else:
|
||||
categories = "3010"
|
||||
|
||||
if albums['Type'] == 'Other':
|
||||
categories = "3030"
|
||||
logger.info("Album type is audiobook/spokenword. Using audiobook category")
|
||||
|
||||
params = { "source" : "headphones",
|
||||
"cat": categories,
|
||||
"q": term
|
||||
}
|
||||
|
||||
searchURL = 'https://nzbx.co/api/search?' + urllib.urlencode(params)
|
||||
|
||||
logger.info(u'Parsing results from <a href="%s">nzbx.co</a>' % searchURL)
|
||||
|
||||
try:
|
||||
data = urllib2.urlopen(searchURL, timeout=20).read()
|
||||
except urllib2.URLError, e:
|
||||
logger.warn('Error fetching data from nzbx.co: %s' % str(e))
|
||||
data = False
|
||||
|
||||
if data:
|
||||
|
||||
d = json.loads(data)
|
||||
|
||||
if not len(d):
|
||||
logger.info(u"No results found from nzbx.co for %s" % term)
|
||||
pass
|
||||
|
||||
else:
|
||||
for item in d:
|
||||
try:
|
||||
url = item['nzb']
|
||||
title = item['name']
|
||||
size = item['size']
|
||||
|
||||
resultlist.append((title, size, url, provider))
|
||||
logger.info('Found %s. Size: %s' % (title, helpers.bytes_to_mb(size)))
|
||||
|
||||
except Exception, e:
|
||||
logger.error(u"An unknown error occurred trying to parse the feed: %s" % e)
|
||||
|
||||
# if headphones.NEWZBIN:
|
||||
# provider = "newzbin"
|
||||
@@ -740,9 +796,9 @@ def searchTorrent(albumid=None, new=False, losslessOnly=False):
|
||||
myDB = db.DBConnection()
|
||||
|
||||
if albumid:
|
||||
results = myDB.select('SELECT ArtistName, AlbumTitle, AlbumID, ReleaseDate from albums WHERE AlbumID=?', [albumid])
|
||||
results = myDB.select('SELECT ArtistName, AlbumTitle, AlbumID, ReleaseDate, SearchTerm from albums WHERE AlbumID=?', [albumid])
|
||||
else:
|
||||
results = myDB.select('SELECT ArtistName, AlbumTitle, AlbumID, ReleaseDate from albums WHERE Status="Wanted" OR Status="Wanted Lossless"')
|
||||
results = myDB.select('SELECT ArtistName, AlbumTitle, AlbumID, ReleaseDate, SearchTerm from albums WHERE Status="Wanted" OR Status="Wanted Lossless"')
|
||||
new = True
|
||||
|
||||
# rutracker login
|
||||
@@ -768,16 +824,22 @@ def searchTorrent(albumid=None, new=False, losslessOnly=False):
|
||||
cleanalbum = helpers.latinToAscii(semi_cleanalbum)
|
||||
semi_cleanartist = helpers.replace_all(albums[0], dic)
|
||||
cleanartist = helpers.latinToAscii(semi_cleanartist)
|
||||
|
||||
# FLAC usually doesn't have a year for some reason so I'll leave it out
|
||||
# Various Artist albums might be listed as VA, so I'll leave that out too
|
||||
# Only use the year if the term could return a bunch of different albums, i.e. self-titled albums
|
||||
if albums[0] in albums[1] or len(albums[0]) < 4 or len(albums[1]) < 4:
|
||||
term = cleanartist + ' ' + cleanalbum + ' ' + year
|
||||
elif albums[0] == 'Various Artists':
|
||||
term = cleanalbum + ' ' + year
|
||||
|
||||
# Use provided term if available, otherwise build our own (this code needs to be cleaned up since a lot
|
||||
# of these torrent providers are just using cleanartist/cleanalbum terms
|
||||
if albums[4]:
|
||||
term = albums[4]
|
||||
|
||||
else:
|
||||
term = cleanartist + ' ' + cleanalbum
|
||||
# FLAC usually doesn't have a year for some reason so I'll leave it out
|
||||
# Various Artist albums might be listed as VA, so I'll leave that out too
|
||||
# Only use the year if the term could return a bunch of different albums, i.e. self-titled albums
|
||||
if albums[0] in albums[1] or len(albums[0]) < 4 or len(albums[1]) < 4:
|
||||
term = cleanartist + ' ' + cleanalbum + ' ' + year
|
||||
elif albums[0] == 'Various Artists':
|
||||
term = cleanalbum + ' ' + year
|
||||
else:
|
||||
term = cleanartist + ' ' + cleanalbum
|
||||
|
||||
semi_clean_artist_term = re.sub('[\.\-\/]', ' ', semi_cleanartist).encode('utf-8', 'replace')
|
||||
semi_clean_album_term = re.sub('[\.\-\/]', ' ', semi_cleanalbum).encode('utf-8', 'replace')
|
||||
|
||||
@@ -17,29 +17,27 @@ import platform, subprocess, re, os, urllib2, tarfile
|
||||
|
||||
import headphones
|
||||
from headphones import logger, version
|
||||
from headphones.exceptions import ex
|
||||
|
||||
import lib.simplejson as simplejson
|
||||
|
||||
user = "rembo10"
|
||||
branch = "master"
|
||||
|
||||
def runGit(args):
|
||||
|
||||
if headphones.GIT_PATH:
|
||||
git_locations = ['"'+headphones.GIT_PATH+'"']
|
||||
else:
|
||||
git_locations = ['git']
|
||||
|
||||
|
||||
if platform.system().lower() == 'darwin':
|
||||
git_locations.append('/usr/local/git/bin/git')
|
||||
|
||||
|
||||
|
||||
|
||||
output = err = None
|
||||
|
||||
|
||||
for cur_git in git_locations:
|
||||
|
||||
|
||||
cmd = cur_git+' '+args
|
||||
|
||||
|
||||
try:
|
||||
logger.debug('Trying to execute: "' + cmd + '" with shell in ' + headphones.PROG_DIR)
|
||||
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True, cwd=headphones.PROG_DIR)
|
||||
@@ -48,7 +46,7 @@ def runGit(args):
|
||||
except OSError:
|
||||
logger.debug('Command ' + cmd + ' didn\'t work, couldn\'t find git')
|
||||
continue
|
||||
|
||||
|
||||
if 'not found' in output or "not recognized as an internal or external command" in output:
|
||||
logger.debug('Unable to find git with command ' + cmd)
|
||||
output = None
|
||||
@@ -57,173 +55,173 @@ def runGit(args):
|
||||
output = None
|
||||
elif output:
|
||||
break
|
||||
|
||||
|
||||
return (output, err)
|
||||
|
||||
|
||||
def getVersion():
|
||||
|
||||
if version.HEADPHONES_VERSION.startswith('win32build'):
|
||||
|
||||
|
||||
headphones.INSTALL_TYPE = 'win'
|
||||
|
||||
|
||||
# Don't have a way to update exe yet, but don't want to set VERSION to None
|
||||
return 'Windows Install'
|
||||
|
||||
|
||||
elif os.path.isdir(os.path.join(headphones.PROG_DIR, '.git')):
|
||||
|
||||
|
||||
headphones.INSTALL_TYPE = 'git'
|
||||
output, err = runGit('rev-parse HEAD')
|
||||
|
||||
|
||||
if not output:
|
||||
logger.error('Couldn\'t find latest installed version.')
|
||||
return None
|
||||
|
||||
|
||||
cur_commit_hash = output.strip()
|
||||
|
||||
|
||||
if not re.match('^[a-z0-9]+$', cur_commit_hash):
|
||||
logger.error('Output doesn\'t look like a hash, not using it')
|
||||
return None
|
||||
|
||||
|
||||
return cur_commit_hash
|
||||
|
||||
|
||||
else:
|
||||
|
||||
|
||||
headphones.INSTALL_TYPE = 'source'
|
||||
|
||||
|
||||
version_file = os.path.join(headphones.PROG_DIR, 'version.txt')
|
||||
|
||||
|
||||
if not os.path.isfile(version_file):
|
||||
return None
|
||||
|
||||
|
||||
fp = open(version_file, 'r')
|
||||
current_version = fp.read().strip(' \n\r')
|
||||
fp.close()
|
||||
|
||||
|
||||
if current_version:
|
||||
return current_version
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def checkGithub():
|
||||
|
||||
# Get the latest commit available from github
|
||||
url = 'https://api.github.com/repos/%s/headphones/commits/%s' % (user, branch)
|
||||
logger.info ('Retrieving latest version information from github')
|
||||
url = 'https://api.github.com/repos/%s/headphones/commits/%s' % (headphones.GIT_USER, headphones.GIT_BRANCH)
|
||||
logger.info('Retrieving latest version information from github')
|
||||
try:
|
||||
result = urllib2.urlopen(url).read()
|
||||
result = urllib2.urlopen(url, timeout=20).read()
|
||||
git = simplejson.JSONDecoder().decode(result)
|
||||
headphones.LATEST_VERSION = git['sha']
|
||||
except:
|
||||
logger.warn('Could not get the latest commit from github')
|
||||
headphones.COMMITS_BEHIND = 0
|
||||
return headphones.CURRENT_VERSION
|
||||
|
||||
# See how many commits behind we are
|
||||
|
||||
# See how many commits behind we are
|
||||
if headphones.CURRENT_VERSION:
|
||||
logger.info('Comparing currently installed version with latest github version')
|
||||
url = 'https://api.github.com/repos/%s/headphones/compare/%s...%s' % (user, headphones.CURRENT_VERSION, headphones.LATEST_VERSION)
|
||||
|
||||
url = 'https://api.github.com/repos/%s/headphones/compare/%s...%s' % (headphones.GIT_USER, headphones.CURRENT_VERSION, headphones.LATEST_VERSION)
|
||||
|
||||
try:
|
||||
result = urllib2.urlopen(url).read()
|
||||
result = urllib2.urlopen(url, timeout=20).read()
|
||||
git = simplejson.JSONDecoder().decode(result)
|
||||
headphones.COMMITS_BEHIND = git['total_commits']
|
||||
except:
|
||||
logger.warn('Could not get commits behind from github')
|
||||
headphones.COMMITS_BEHIND = 0
|
||||
return headphones.CURRENT_VERSION
|
||||
|
||||
|
||||
if headphones.COMMITS_BEHIND >= 1:
|
||||
logger.info('New version is available. You are %s commits behind' % headphones.COMMITS_BEHIND)
|
||||
elif headphones.COMMITS_BEHIND == 0:
|
||||
logger.info('Headphones is up to date')
|
||||
elif headphones.COMMITS_BEHIND == -1:
|
||||
logger.info('You are running an unknown version of Headphones. Run the updater to identify your version')
|
||||
|
||||
|
||||
else:
|
||||
logger.info('You are running an unknown version of Headphones. Run the updater to identify your version')
|
||||
|
||||
|
||||
return headphones.LATEST_VERSION
|
||||
|
||||
|
||||
def update():
|
||||
|
||||
|
||||
|
||||
if headphones.INSTALL_TYPE == 'win':
|
||||
|
||||
|
||||
logger.info('Windows .exe updating not supported yet.')
|
||||
pass
|
||||
|
||||
|
||||
|
||||
elif headphones.INSTALL_TYPE == 'git':
|
||||
|
||||
|
||||
output, err = runGit('pull origin ' + version.HEADPHONES_VERSION)
|
||||
|
||||
|
||||
if not output:
|
||||
logger.error('Couldn\'t download latest version')
|
||||
|
||||
|
||||
for line in output.split('\n'):
|
||||
|
||||
|
||||
if 'Already up-to-date.' in line:
|
||||
logger.info('No update available, not updating')
|
||||
logger.info('Output: ' + str(output))
|
||||
elif line.endswith('Aborting.'):
|
||||
logger.error('Unable to update from git: '+line)
|
||||
logger.info('Output: ' + str(output))
|
||||
|
||||
|
||||
else:
|
||||
|
||||
tar_download_url = 'https://github.com/%s/headphones/tarball/%s' % (user, branch)
|
||||
|
||||
tar_download_url = 'https://github.com/%s/headphones/tarball/%s' % (headphones.GIT_USER, headphones.GIT_BRANCH)
|
||||
update_dir = os.path.join(headphones.PROG_DIR, 'update')
|
||||
version_path = os.path.join(headphones.PROG_DIR, 'version.txt')
|
||||
|
||||
|
||||
try:
|
||||
logger.info('Downloading update from: '+tar_download_url)
|
||||
data = urllib2.urlopen(tar_download_url)
|
||||
except (IOError, URLError):
|
||||
logger.error("Unable to retrieve new version from "+tar_download_url+", can't update")
|
||||
return
|
||||
|
||||
|
||||
download_name = data.geturl().split('/')[-1]
|
||||
|
||||
|
||||
tar_download_path = os.path.join(headphones.PROG_DIR, download_name)
|
||||
|
||||
|
||||
# Save tar to disk
|
||||
f = open(tar_download_path, 'wb')
|
||||
f.write(data.read())
|
||||
f.close()
|
||||
|
||||
|
||||
# Extract the tar to update folder
|
||||
logger.info('Extracing file' + tar_download_path)
|
||||
tar = tarfile.open(tar_download_path)
|
||||
tar.extractall(update_dir)
|
||||
tar.close()
|
||||
|
||||
|
||||
# Delete the tar.gz
|
||||
logger.info('Deleting file' + tar_download_path)
|
||||
os.remove(tar_download_path)
|
||||
|
||||
|
||||
# Find update dir name
|
||||
update_dir_contents = [x for x in os.listdir(update_dir) if os.path.isdir(os.path.join(update_dir, x))]
|
||||
if len(update_dir_contents) != 1:
|
||||
logger.error(u"Invalid update data, update failed: "+str(update_dir_contents))
|
||||
logger.error("Invalid update data, update failed: "+str(update_dir_contents))
|
||||
return
|
||||
content_dir = os.path.join(update_dir, update_dir_contents[0])
|
||||
|
||||
|
||||
# walk temp folder and move files to main folder
|
||||
for dirname, dirnames, filenames in os.walk(content_dir):
|
||||
dirname = dirname[len(content_dir)+1:]
|
||||
for curfile in filenames:
|
||||
old_path = os.path.join(content_dir, dirname, curfile)
|
||||
new_path = os.path.join(headphones.PROG_DIR, dirname, curfile)
|
||||
|
||||
|
||||
if os.path.isfile(new_path):
|
||||
os.remove(new_path)
|
||||
os.renames(old_path, new_path)
|
||||
|
||||
|
||||
# Update version.txt
|
||||
try:
|
||||
ver_file = open(version_path, 'w')
|
||||
ver_file.write(headphones.LATEST_VERSION)
|
||||
ver_file.close()
|
||||
except IOError, e:
|
||||
logger.error(u"Unable to write current version to version.txt, update not complete: "+ex(e))
|
||||
logger.error("Unable to write current version to version.txt, update not complete: "+ex(e))
|
||||
return
|
||||
|
||||
@@ -286,6 +286,15 @@ class WebInterface(object):
|
||||
albumswitcher.switch(AlbumID, ReleaseID)
|
||||
raise cherrypy.HTTPRedirect("albumPage?AlbumID=%s" % AlbumID)
|
||||
switchAlbum.exposed = True
|
||||
|
||||
def editSearchTerm(self, AlbumID, SearchTerm):
|
||||
logger.info(u"Updating search term for albumid: " + AlbumID)
|
||||
myDB = db.DBConnection()
|
||||
controlValueDict = {'AlbumID': AlbumID}
|
||||
newValueDict = {'SearchTerm': SearchTerm}
|
||||
myDB.upsert("albums", newValueDict, controlValueDict)
|
||||
raise cherrypy.HTTPRedirect("albumPage?AlbumID=%s" % AlbumID)
|
||||
editSearchTerm.exposed = True
|
||||
|
||||
def upcoming(self):
|
||||
myDB = db.DBConnection()
|
||||
@@ -585,6 +594,7 @@ class WebInterface(object):
|
||||
"use_nzbsrus" : checked(headphones.NZBSRUS),
|
||||
"nzbsrus_uid" : headphones.NZBSRUS_UID,
|
||||
"nzbsrus_apikey" : headphones.NZBSRUS_APIKEY,
|
||||
"use_nzbx" : checked(headphones.NZBX),
|
||||
"torrentblackhole_dir" : headphones.TORRENTBLACKHOLE_DIR,
|
||||
"download_torrent_dir" : headphones.DOWNLOAD_TORRENT_DIR,
|
||||
"numberofseeders" : headphones.NUMBEROFSEEDERS,
|
||||
@@ -688,7 +698,7 @@ class WebInterface(object):
|
||||
def configUpdate(self, http_host='0.0.0.0', http_username=None, http_port=8181, http_password=None, launch_browser=0, api_enabled=0, api_key=None,
|
||||
download_scan_interval=None, nzb_search_interval=None, libraryscan_interval=None, sab_host=None, sab_username=None, sab_apikey=None, sab_password=None,
|
||||
sab_category=None, download_dir=None, blackhole=0, blackhole_dir=None, usenet_retention=None, newznab=0, newznab_host=None, newznab_apikey=None,
|
||||
newznab_enabled=0, nzbsorg=0, nzbsorg_uid=None, nzbsorg_hash=None, nzbsrus=0, nzbsrus_uid=None, nzbsrus_apikey=None, preferred_quality=0, preferred_bitrate=None,
|
||||
newznab_enabled=0, nzbsorg=0, nzbsorg_uid=None, nzbsorg_hash=None, nzbsrus=0, nzbsrus_uid=None, nzbsrus_apikey=None, nzbx=None, preferred_quality=0, preferred_bitrate=None,
|
||||
detect_bitrate=0, move_files=0, torrentblackhole_dir=None, download_torrent_dir=None,
|
||||
numberofseeders=10, use_isohunt=0, use_kat=0, use_mininova=0, waffles=0, waffles_uid=None, waffles_passkey=None, whatcd=0, whatcd_username=None, whatcd_password=None,
|
||||
rutracker=0, rutracker_user=None, rutracker_password=None, rename_files=0, correct_metadata=0, cleanup_files=0, add_album_art=0, embed_album_art=0, embed_lyrics=0,
|
||||
@@ -735,6 +745,7 @@ class WebInterface(object):
|
||||
headphones.NZBSRUS = nzbsrus
|
||||
headphones.NZBSRUS_UID = nzbsrus_uid
|
||||
headphones.NZBSRUS_APIKEY = nzbsrus_apikey
|
||||
headphones.NZBX = nzbx
|
||||
headphones.TORRENTBLACKHOLE_DIR = torrentblackhole_dir
|
||||
headphones.NUMBEROFSEEDERS = numberofseeders
|
||||
headphones.DOWNLOAD_TORRENT_DIR = download_torrent_dir
|
||||
|
||||
209
init.ubuntu
Normal file → Executable file
209
init.ubuntu
Normal file → Executable file
@@ -1,5 +1,44 @@
|
||||
#! /bin/sh
|
||||
|
||||
#!/bin/sh
|
||||
#
|
||||
## Don't edit this file
|
||||
## Edit user configuation in /etc/default/headphones to change
|
||||
##
|
||||
## Make sure init script is executable
|
||||
## sudo chmod +x /opt/headphones/init.ubuntu
|
||||
##
|
||||
## Install the init script
|
||||
## sudo ln -s /opt/headphones/init.ubuntu /etc/init.d/headphones
|
||||
##
|
||||
## Create the headphones daemon user:
|
||||
## sudo adduser --system --no-create-home headphones
|
||||
##
|
||||
## Make sure /opt/headphones is owned by the headphones user
|
||||
## sudo chown headphones:nogroup -R /opt/headphones
|
||||
##
|
||||
## Touch the default file to stop the warning message when starting
|
||||
## sudo touch /etc/default/headphones
|
||||
##
|
||||
## To start Headphones automatically
|
||||
## sudo update-rc.d headphones defaults
|
||||
##
|
||||
## To start/stop/restart Headphones
|
||||
## sudo service headphones start
|
||||
## sudo service headphones stop
|
||||
## sudo service headphones restart
|
||||
##
|
||||
## HP_USER= #$RUN_AS, username to run headphones under, the default is headphones
|
||||
## HP_HOME= #$APP_PATH, the location of Headphones.py, the default is /opt/headphones
|
||||
## HP_DATA= #$DATA_DIR, the location of headphones.db, cache, logs, the default is /opt/headphones
|
||||
## HP_PIDFILE= #$PID_FILE, the location of headphones.pid, the default is /var/run/headphones/headphones.pid
|
||||
## PYTHON_BIN= #$DAEMON, the location of the python binary, the default is /usr/bin/python
|
||||
## HP_OPTS= #$EXTRA_DAEMON_OPTS, extra cli option for headphones, i.e. " --config=/home/headphones/config.ini"
|
||||
## SSD_OPTS= #$EXTRA_SSD_OPTS, extra start-stop-daemon option like " --group=users"
|
||||
## HP_PORT= #$PORT_OPTS, hardcoded port for the webserver, overrides value in config.ini
|
||||
##
|
||||
## EXAMPLE if want to run as different user
|
||||
## add HP_USER=username to /etc/default/headphones
|
||||
## otherwise default headphones is used
|
||||
#
|
||||
### BEGIN INIT INFO
|
||||
# Provides: headphones
|
||||
# Required-Start: $local_fs $network $remote_fs
|
||||
@@ -12,50 +51,152 @@
|
||||
# Description: starts instance of Headphones using start-stop-daemon
|
||||
### END INIT INFO
|
||||
|
||||
############### EDIT ME ##################
|
||||
# path to app
|
||||
APP_PATH=/usr/local/sbin/headphones
|
||||
|
||||
# path to python bin
|
||||
DAEMON=/usr/bin/python
|
||||
|
||||
# startup args
|
||||
DAEMON_OPTS=" Headphones.py -q"
|
||||
|
||||
# script name
|
||||
# Script name
|
||||
NAME=headphones
|
||||
|
||||
# app name
|
||||
DESC=headphones
|
||||
# App name
|
||||
DESC=Headphones
|
||||
|
||||
# user
|
||||
RUN_AS=root
|
||||
SETTINGS_LOADED=FALSE
|
||||
|
||||
PID_FILE=/var/run/headphones.pid
|
||||
. /lib/lsb/init-functions
|
||||
|
||||
############### END EDIT ME ##################
|
||||
# Source Headphones configuration
|
||||
if [ -f /etc/default/headphones ]; then
|
||||
SETTINGS=/etc/default/headphones
|
||||
else
|
||||
log_warning_msg "/etc/default/headphones not found using default settings.";
|
||||
fi
|
||||
|
||||
test -x $DAEMON || exit 0
|
||||
check_retval() {
|
||||
if [ $? -eq 0 ]; then
|
||||
log_end_msg 0
|
||||
return 0
|
||||
else
|
||||
log_end_msg 1
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
set -e
|
||||
load_settings() {
|
||||
if [ $SETTINGS_LOADED != "TRUE" ]; then
|
||||
. $SETTINGS
|
||||
|
||||
## The defaults
|
||||
# Run as username
|
||||
RUN_AS=${HP_USER-headphones}
|
||||
|
||||
# Path to app HP_HOME=path_to_app_Headphones.py
|
||||
APP_PATH=${HP_HOME-/opt/headphones}
|
||||
|
||||
# Data directory where headphones.db, cache and logs are stored
|
||||
DATA_DIR=${HP_DATA-/opt/headphones}
|
||||
|
||||
# Path to store PID file
|
||||
PID_FILE=${HP_PIDFILE-/var/run/headphones/headphones.pid}
|
||||
|
||||
# Path to python bin
|
||||
DAEMON=${PYTHON_BIN-/usr/bin/python}
|
||||
|
||||
# Extra daemon option like: HP_OPTS=" --config=/home/headphones/config.ini"
|
||||
EXTRA_DAEMON_OPTS=${HP_OPTS-}
|
||||
|
||||
# Extra start-stop-daemon option like START_OPTS=" --group=users"
|
||||
EXTRA_SSD_OPTS=${SSD_OPTS-}
|
||||
|
||||
# Hardcoded port to run on, overrides config.ini settings
|
||||
[ -n "$HP_PORT" ] && {
|
||||
PORT_OPTS=" --port=${HP_PORT} "
|
||||
}
|
||||
|
||||
DAEMON_OPTS=" Headphones.py --quiet --daemon --nolaunch --pidfile=${PID_FILE} --datadir=${DATA_DIR} ${PORT_OPTS}${EXTRA_DAEMON_OPTS}"
|
||||
|
||||
SETTINGS_LOADED=TRUE
|
||||
fi
|
||||
|
||||
[ -x $DAEMON ] || {
|
||||
log_warning_msg "$DESC: Can't execute daemon, aborting. See $DAEMON";
|
||||
return 1;}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
load_settings || exit 0
|
||||
|
||||
is_running () {
|
||||
# returns 1 when running, else 0.
|
||||
if [ -e $PID_FILE ]; then
|
||||
PID=`cat $PID_FILE`
|
||||
|
||||
RET=$?
|
||||
[ $RET -gt 1 ] && exit 1 || return $RET
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
handle_pid () {
|
||||
PID_PATH=`dirname $PID_FILE`
|
||||
[ -d $PID_PATH ] || mkdir -p $PID_PATH && chown -R $RUN_AS $PID_PATH > /dev/null || {
|
||||
log_warning_msg "$DESC: Could not create $PID_FILE, See $SETTINGS, aborting.";
|
||||
return 1;}
|
||||
|
||||
if [ -e $PID_FILE ]; then
|
||||
PID=`cat $PID_FILE`
|
||||
if ! kill -0 $PID > /dev/null 2>&1; then
|
||||
log_warning_msg "Removing stale $PID_FILE"
|
||||
rm $PID_FILE
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
handle_datadir () {
|
||||
[ -d $DATA_DIR ] || mkdir -p $DATA_DIR && chown -R $RUN_AS $DATA_DIR > /dev/null || {
|
||||
log_warning_msg "$DESC: Could not create $DATA_DIR, See $SETTINGS, aborting.";
|
||||
return 1;}
|
||||
}
|
||||
|
||||
handle_updates () {
|
||||
chown -R $RUN_AS $APP_PATH > /dev/null || {
|
||||
log_warning_msg "$DESC: $APP_PATH not writable by $RUN_AS for web-updates";
|
||||
return 0; }
|
||||
}
|
||||
|
||||
start_headphones () {
|
||||
handle_pid
|
||||
handle_datadir
|
||||
handle_updates
|
||||
if ! is_running; then
|
||||
log_daemon_msg "Starting $DESC"
|
||||
start-stop-daemon -o -d $APP_PATH -c $RUN_AS --start $EXTRA_SSD_OPTS --pidfile $PID_FILE --exec $DAEMON -- $DAEMON_OPTS
|
||||
check_retval
|
||||
else
|
||||
log_success_msg "$DESC: already running (pid $PID)"
|
||||
fi
|
||||
}
|
||||
|
||||
stop_headphones () {
|
||||
if is_running; then
|
||||
log_daemon_msg "Stopping $DESC"
|
||||
start-stop-daemon -o --stop --pidfile $PID_FILE --retry 15
|
||||
check_retval
|
||||
else
|
||||
log_success_msg "$DESC: not running"
|
||||
fi
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
echo "Starting $DESC"
|
||||
start-stop-daemon -d $APP_PATH -c $RUN_AS --start --background --pidfile $PID_FILE --make-pidfile --exec $DAEMON -- $DAEMON_OPTS
|
||||
start)
|
||||
start_headphones
|
||||
;;
|
||||
stop)
|
||||
echo "Stopping $DESC"
|
||||
start-stop-daemon --stop --pidfile $PID_FILE
|
||||
stop)
|
||||
stop_headphones
|
||||
;;
|
||||
|
||||
restart|force-reload)
|
||||
echo "Restarting $DESC"
|
||||
start-stop-daemon --stop --pidfile $PID_FILE
|
||||
sleep 15
|
||||
start-stop-daemon -d $APP_PATH -c $RUN_AS --start --background --pidfile $PID_FILE --make-pidfile --exec $DAEMON -- $DAEMON_OPTS
|
||||
restart|force-reload)
|
||||
stop_headphones
|
||||
start_headphones
|
||||
;;
|
||||
*)
|
||||
*)
|
||||
N=/etc/init.d/$NAME
|
||||
echo "Usage: $N {start|stop|restart|force-reload}" >&2
|
||||
exit 1
|
||||
|
||||
Reference in New Issue
Block a user