Merged in brunnels updated initscript/pidfile handling & adehub fix in base.html

This commit is contained in:
rembo10
2012-12-25 01:01:50 -05:00
6 changed files with 451 additions and 273 deletions

6
.gitignore vendored
View File

@@ -50,4 +50,8 @@ Thumbs.db
obj/
[Rr]elease*/
_ReSharper*/
[Tt]est[Rr]esult*
[Tt]est[Rr]esult*
/cache
/logs
.project
.pydevproject

View File

@@ -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__":

View File

@@ -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/rembo10/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 ]>

View File

@@ -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
@@ -262,9 +265,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, \
@@ -280,10 +283,10 @@ def initialize():
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')
@@ -301,18 +304,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 +325,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 +361,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 +377,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 +391,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 +412,18 @@ 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', '')
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 +438,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 +468,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 +478,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 +506,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 +540,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 +568,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 +647,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 +681,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 +693,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 +731,30 @@ 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['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 +762,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 +774,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,43 +794,48 @@ 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)
@@ -842,37 +854,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 +894,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:
@@ -1010,27 +1022,28 @@ def dbcheck():
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]
@@ -1039,5 +1052,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)

View File

@@ -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,58 +55,58 @@ 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()
git = simplejson.JSONDecoder().decode(result)
@@ -117,12 +115,12 @@ def checkGithub():
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()
git = simplejson.JSONDecoder().decode(result)
@@ -131,99 +129,99 @@ def checkGithub():
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

209
init.ubuntu Normal file → Executable file
View 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