mirror of
https://github.com/rembo10/headphones.git
synced 2026-05-15 16:19:28 +01:00
176
Headphones.py
Executable file → Normal file
176
Headphones.py
Executable file → Normal file
@@ -8,98 +8,98 @@ import headphones
|
||||
from headphones import webstart, logger
|
||||
|
||||
try:
|
||||
import argparse
|
||||
import argparse
|
||||
except ImportError:
|
||||
import lib.argparse as argparse
|
||||
|
||||
import lib.argparse as argparse
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
# Fixed paths to Headphones
|
||||
if hasattr(sys, 'frozen'):
|
||||
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:]
|
||||
|
||||
# Set up and gather command line arguments
|
||||
parser = argparse.ArgumentParser(description='Music add-on for SABnzbd+')
|
||||
# Fixed paths to Headphones
|
||||
if hasattr(sys, 'frozen'):
|
||||
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:]
|
||||
|
||||
# Set up and gather command line arguments
|
||||
parser = argparse.ArgumentParser(description='Music add-on for SABnzbd+')
|
||||
|
||||
parser.add_argument('-q', '--quiet', action='store_true', help='Turn off console logging')
|
||||
parser.add_argument('-d', '--daemon', action='store_true', help='Run as a daemon')
|
||||
parser.add_argument('-p', '--port', type=int, help='Force Headphones to run on a specified port')
|
||||
parser.add_argument('--datadir', help='Specify a directory where to store your data files')
|
||||
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')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.quiet:
|
||||
headphones.QUIET=True
|
||||
|
||||
if args.daemon:
|
||||
headphones.DAEMON=True
|
||||
headphones.QUIET=True
|
||||
|
||||
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)
|
||||
|
||||
# Read config & start logging
|
||||
headphones.initialize()
|
||||
|
||||
if headphones.DAEMON:
|
||||
headphones.daemonize()
|
||||
|
||||
# Force the http port if neccessary
|
||||
if args.port:
|
||||
http_port = args.port
|
||||
logger.info('Starting Headphones on foced port: %i' % http_port)
|
||||
else:
|
||||
http_port = int(headphones.HTTP_PORT)
|
||||
|
||||
# Try to start the server.
|
||||
webstart.initialize({
|
||||
'http_port': http_port,
|
||||
'http_host': headphones.HTTP_HOST,
|
||||
'http_root': headphones.HTTP_ROOT,
|
||||
'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()
|
||||
|
||||
return
|
||||
parser.add_argument('-q', '--quiet', action='store_true', help='Turn off console logging')
|
||||
parser.add_argument('-d', '--daemon', action='store_true', help='Run as a daemon')
|
||||
parser.add_argument('-p', '--port', type=int, help='Force Headphones to run on a specified port')
|
||||
parser.add_argument('--datadir', help='Specify a directory where to store your data files')
|
||||
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')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.quiet:
|
||||
headphones.QUIET=True
|
||||
|
||||
if args.daemon:
|
||||
headphones.DAEMON=True
|
||||
headphones.QUIET=True
|
||||
|
||||
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)
|
||||
|
||||
# Read config & start logging
|
||||
headphones.initialize()
|
||||
|
||||
if headphones.DAEMON:
|
||||
headphones.daemonize()
|
||||
|
||||
# Force the http port if neccessary
|
||||
if args.port:
|
||||
http_port = args.port
|
||||
logger.info('Starting Headphones on foced port: %i' % http_port)
|
||||
else:
|
||||
http_port = int(headphones.HTTP_PORT)
|
||||
|
||||
# Try to start the server.
|
||||
webstart.initialize({
|
||||
'http_port': http_port,
|
||||
'http_host': headphones.HTTP_HOST,
|
||||
'http_root': headphones.HTTP_ROOT,
|
||||
'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()
|
||||
|
||||
return
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
main()
|
||||
|
||||
@@ -11,7 +11,8 @@ from lib.configobj import ConfigObj
|
||||
|
||||
import cherrypy
|
||||
|
||||
from headphones import updater, searcher, importer, versioncheck, logger, postprocessor
|
||||
from headphones import updater, searcher, importer, versioncheck, logger, postprocessor, version, sab
|
||||
from headphones.common import *
|
||||
|
||||
FULL_PATH = None
|
||||
PROG_DIR = None
|
||||
@@ -92,8 +93,13 @@ NZBSORG = False
|
||||
NZBSORG_UID = None
|
||||
NZBSORG_HASH = None
|
||||
|
||||
NEWZBIN = False
|
||||
NEWZBIN_UID = None
|
||||
NEWZBIN_PASSWORD = None
|
||||
|
||||
LASTFM_USERNAME = None
|
||||
|
||||
|
||||
def CheckSection(sec):
|
||||
""" Check if INI section exists, if not create it """
|
||||
try:
|
||||
@@ -138,351 +144,361 @@ def check_setting_str(config, cfg_name, item_name, def_val, log=True):
|
||||
else:
|
||||
logger.debug(item_name + " -> ******")
|
||||
return my_val
|
||||
|
||||
|
||||
|
||||
def initialize():
|
||||
|
||||
with INIT_LOCK:
|
||||
|
||||
global __INITIALIZED__, FULL_PATH, PROG_DIR, QUIET, DAEMON, DATA_DIR, CONFIG_FILE, CFG, LOG_DIR, CACHE_DIR, \
|
||||
HTTP_PORT, HTTP_HOST, HTTP_USERNAME, HTTP_PASSWORD, HTTP_ROOT, LAUNCH_BROWSER, GIT_PATH, \
|
||||
CURRENT_VERSION, LATEST_VERSION, MUSIC_DIR, DESTINATION_DIR, PREFERRED_QUALITY, PREFERRED_BITRATE, DETECT_BITRATE, \
|
||||
CORRECT_METADATA, MOVE_FILES, RENAME_FILES, FOLDER_FORMAT, FILE_FORMAT, CLEANUP_FILES, INCLUDE_EXTRAS, \
|
||||
ADD_ALBUM_ART, EMBED_ALBUM_ART, DOWNLOAD_DIR, BLACKHOLE, BLACKHOLE_DIR, USENET_RETENTION, NZB_SEARCH_INTERVAL, \
|
||||
LIBRARYSCAN_INTERVAL, SAB_HOST, SAB_USERNAME, SAB_PASSWORD, SAB_APIKEY, SAB_CATEGORY, \
|
||||
NZBMATRIX, NZBMATRIX_USERNAME, NZBMATRIX_APIKEY, NEWZNAB, NEWZNAB_HOST, NEWZNAB_APIKEY, \
|
||||
NZBSORG, NZBSORG_UID, NZBSORG_HASH, LASTFM_USERNAME
|
||||
|
||||
if __INITIALIZED__:
|
||||
return False
|
||||
|
||||
# Make sure all the config sections exist
|
||||
CheckSection('General')
|
||||
CheckSection('SABnzbd')
|
||||
CheckSection('NZBMatrix')
|
||||
CheckSection('Newznab')
|
||||
CheckSection('NZBsorg')
|
||||
|
||||
# Set global variables based on config file or use defaults
|
||||
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', '')
|
||||
HTTP_ROOT = check_setting_str(CFG, 'General', 'http_root', '/')
|
||||
LAUNCH_BROWSER = bool(check_setting_int(CFG, 'General', 'launch_browser', 1))
|
||||
GIT_PATH = check_setting_str(CFG, 'General', 'git_path', '')
|
||||
LOG_DIR = check_setting_str(CFG, 'General', 'log_dir', '')
|
||||
|
||||
MUSIC_DIR = check_setting_str(CFG, 'General', 'music_dir', '')
|
||||
DESTINATION_DIR = check_setting_str(CFG, 'General', 'destination_dir', '')
|
||||
PREFERRED_QUALITY = check_setting_int(CFG, 'General', 'preferred_quality', 0)
|
||||
PREFERRED_BITRATE = check_setting_int(CFG, 'General', 'preferred_bitrate', '')
|
||||
DETECT_BITRATE = bool(check_setting_int(CFG, 'General', 'detect_bitrate', 0))
|
||||
CORRECT_METADATA = bool(check_setting_int(CFG, 'General', 'correct_metadata', 0))
|
||||
MOVE_FILES = bool(check_setting_int(CFG, 'General', 'move_files', 0))
|
||||
RENAME_FILES = bool(check_setting_int(CFG, 'General', 'rename_files', 0))
|
||||
FOLDER_FORMAT = check_setting_str(CFG, 'General', 'folder_format', 'artist/album [year]')
|
||||
FILE_FORMAT = check_setting_str(CFG, 'General', 'file_format', 'tracknumber artist - album [year]- title')
|
||||
CLEANUP_FILES = bool(check_setting_int(CFG, 'General', 'cleanup_files', 0))
|
||||
ADD_ALBUM_ART = bool(check_setting_int(CFG, 'General', 'add_album_art', 0))
|
||||
EMBED_ALBUM_ART = bool(check_setting_int(CFG, 'General', 'embed_album_art', 0))
|
||||
DOWNLOAD_DIR = check_setting_str(CFG, 'General', 'download_dir', '')
|
||||
BLACKHOLE = bool(check_setting_int(CFG, 'General', 'blackhole', 0))
|
||||
BLACKHOLE_DIR = check_setting_str(CFG, 'General', 'blackhole_dir', '')
|
||||
USENET_RETENTION = check_setting_int(CFG, 'General', 'usenet_retention', '')
|
||||
INCLUDE_EXTRAS = bool(check_setting_int(CFG, 'General', 'include_extras', 0))
|
||||
|
||||
NZB_SEARCH_INTERVAL = check_setting_int(CFG, 'General', 'nzb_search_interval', 360)
|
||||
LIBRARYSCAN_INTERVAL = check_setting_int(CFG, 'General', 'libraryscan_interval', 180)
|
||||
|
||||
SAB_HOST = check_setting_str(CFG, 'SABnzbd', 'sab_host', '')
|
||||
SAB_USERNAME = check_setting_str(CFG, 'SABnzbd', 'sab_username', '')
|
||||
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', '')
|
||||
|
||||
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', '')
|
||||
|
||||
LASTFM_USERNAME = check_setting_str(CFG, 'General', 'lastfm_username', '')
|
||||
|
||||
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 not QUIET:
|
||||
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(quiet=QUIET)
|
||||
|
||||
# Update some old config code:
|
||||
if FOLDER_FORMAT == '%artist/%album/%track':
|
||||
FOLDER_FORMAT = 'artist/album [year]'
|
||||
if FILE_FORMAT == '%tracknumber %artist - %album - %title':
|
||||
FILE_FORMAT = 'tracknumber artist - album - title'
|
||||
|
||||
# Put the cache dir in the data dir for now
|
||||
CACHE_DIR = os.path.join(DATA_DIR, 'cache')
|
||||
if not os.path.exists(CACHE_DIR):
|
||||
try:
|
||||
os.makedirs(CACHE_DIR)
|
||||
except OSError:
|
||||
logger.error('Could not create cache dir. Check permissions of datadir: ' + DATA_DIR)
|
||||
|
||||
# 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
|
||||
LATEST_VERSION = versioncheck.checkGithub()
|
||||
with INIT_LOCK:
|
||||
|
||||
global __INITIALIZED__, FULL_PATH, PROG_DIR, QUIET, DAEMON, DATA_DIR, CONFIG_FILE, CFG, LOG_DIR, CACHE_DIR, \
|
||||
HTTP_PORT, HTTP_HOST, HTTP_USERNAME, HTTP_PASSWORD, HTTP_ROOT, LAUNCH_BROWSER, GIT_PATH, \
|
||||
CURRENT_VERSION, LATEST_VERSION, MUSIC_DIR, DESTINATION_DIR, PREFERRED_QUALITY, PREFERRED_BITRATE, DETECT_BITRATE, \
|
||||
CORRECT_METADATA, MOVE_FILES, RENAME_FILES, FOLDER_FORMAT, FILE_FORMAT, CLEANUP_FILES, INCLUDE_EXTRAS, \
|
||||
ADD_ALBUM_ART, EMBED_ALBUM_ART, DOWNLOAD_DIR, BLACKHOLE, BLACKHOLE_DIR, USENET_RETENTION, NZB_SEARCH_INTERVAL, \
|
||||
LIBRARYSCAN_INTERVAL, SAB_HOST, SAB_USERNAME, SAB_PASSWORD, SAB_APIKEY, SAB_CATEGORY, \
|
||||
NZBMATRIX, NZBMATRIX_USERNAME, NZBMATRIX_APIKEY, NEWZNAB, NEWZNAB_HOST, NEWZNAB_APIKEY, \
|
||||
NZBSORG, NZBSORG_UID, NZBSORG_HASH, NEWZBIN, NEWZBIN_UID, NEWZBIN_PASSWORD, LASTFM_USERNAME
|
||||
|
||||
if __INITIALIZED__:
|
||||
return False
|
||||
|
||||
# Make sure all the config sections exist
|
||||
CheckSection('General')
|
||||
CheckSection('SABnzbd')
|
||||
CheckSection('NZBMatrix')
|
||||
CheckSection('Newznab')
|
||||
CheckSection('NZBsorg')
|
||||
CheckSection('Newzbin')
|
||||
|
||||
# Set global variables based on config file or use defaults
|
||||
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', '')
|
||||
HTTP_ROOT = check_setting_str(CFG, 'General', 'http_root', '/')
|
||||
LAUNCH_BROWSER = bool(check_setting_int(CFG, 'General', 'launch_browser', 1))
|
||||
GIT_PATH = check_setting_str(CFG, 'General', 'git_path', '')
|
||||
LOG_DIR = check_setting_str(CFG, 'General', 'log_dir', '')
|
||||
|
||||
MUSIC_DIR = check_setting_str(CFG, 'General', 'music_dir', '')
|
||||
DESTINATION_DIR = check_setting_str(CFG, 'General', 'destination_dir', '')
|
||||
PREFERRED_QUALITY = check_setting_int(CFG, 'General', 'preferred_quality', 0)
|
||||
PREFERRED_BITRATE = check_setting_int(CFG, 'General', 'preferred_bitrate', '')
|
||||
DETECT_BITRATE = bool(check_setting_int(CFG, 'General', 'detect_bitrate', 0))
|
||||
CORRECT_METADATA = bool(check_setting_int(CFG, 'General', 'correct_metadata', 0))
|
||||
MOVE_FILES = bool(check_setting_int(CFG, 'General', 'move_files', 0))
|
||||
RENAME_FILES = bool(check_setting_int(CFG, 'General', 'rename_files', 0))
|
||||
FOLDER_FORMAT = check_setting_str(CFG, 'General', 'folder_format', 'artist/album [year]')
|
||||
FILE_FORMAT = check_setting_str(CFG, 'General', 'file_format', 'tracknumber artist - album [year]- title')
|
||||
CLEANUP_FILES = bool(check_setting_int(CFG, 'General', 'cleanup_files', 0))
|
||||
ADD_ALBUM_ART = bool(check_setting_int(CFG, 'General', 'add_album_art', 0))
|
||||
EMBED_ALBUM_ART = bool(check_setting_int(CFG, 'General', 'embed_album_art', 0))
|
||||
DOWNLOAD_DIR = check_setting_str(CFG, 'General', 'download_dir', '')
|
||||
BLACKHOLE = bool(check_setting_int(CFG, 'General', 'blackhole', 0))
|
||||
BLACKHOLE_DIR = check_setting_str(CFG, 'General', 'blackhole_dir', '')
|
||||
USENET_RETENTION = check_setting_int(CFG, 'General', 'usenet_retention', '')
|
||||
INCLUDE_EXTRAS = bool(check_setting_int(CFG, 'General', 'include_extras', 0))
|
||||
|
||||
NZB_SEARCH_INTERVAL = check_setting_int(CFG, 'General', 'nzb_search_interval', 360)
|
||||
LIBRARYSCAN_INTERVAL = check_setting_int(CFG, 'General', 'libraryscan_interval', 180)
|
||||
|
||||
SAB_HOST = check_setting_str(CFG, 'SABnzbd', 'sab_host', '')
|
||||
SAB_USERNAME = check_setting_str(CFG, 'SABnzbd', 'sab_username', '')
|
||||
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', '')
|
||||
|
||||
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', '')
|
||||
|
||||
__INITIALIZED__ = True
|
||||
return True
|
||||
|
||||
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', '')
|
||||
|
||||
LASTFM_USERNAME = check_setting_str(CFG, 'General', 'lastfm_username', '')
|
||||
|
||||
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 not QUIET:
|
||||
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(quiet=QUIET)
|
||||
|
||||
# Update some old config code:
|
||||
if FOLDER_FORMAT == '%artist/%album/%track':
|
||||
FOLDER_FORMAT = 'artist/album [year]'
|
||||
if FILE_FORMAT == '%tracknumber %artist - %album - %title':
|
||||
FILE_FORMAT = 'tracknumber artist - album - title'
|
||||
|
||||
# Put the cache dir in the data dir for now
|
||||
CACHE_DIR = os.path.join(DATA_DIR, 'cache')
|
||||
if not os.path.exists(CACHE_DIR):
|
||||
try:
|
||||
os.makedirs(CACHE_DIR)
|
||||
except OSError:
|
||||
logger.error('Could not create cache dir. Check permissions of datadir: ' + DATA_DIR)
|
||||
|
||||
# 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
|
||||
LATEST_VERSION = versioncheck.checkGithub()
|
||||
|
||||
__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)
|
||||
except OSError, e:
|
||||
sys.exit("1st fork failed: %s [%d]" % (e.strerror, e.errno))
|
||||
|
||||
os.setsid()
|
||||
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)
|
||||
except OSError, e:
|
||||
sys.exit("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))
|
||||
# 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))
|
||||
|
||||
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())
|
||||
|
||||
logger.info('Daemonized to PID: %s' % os.getpid())
|
||||
|
||||
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())
|
||||
|
||||
logger.info('Daemonized to PID: %s' % os.getpid())
|
||||
|
||||
def launch_browser(host, port, root):
|
||||
|
||||
if host == '0.0.0.0':
|
||||
host = 'localhost'
|
||||
|
||||
try:
|
||||
webbrowser.open('http://%s:%i%s' % (host, port, root))
|
||||
except Exception, e:
|
||||
logger.error('Could not launch browser: %s' % e)
|
||||
if host == '0.0.0.0':
|
||||
host = 'localhost'
|
||||
|
||||
try:
|
||||
webbrowser.open('http://%s:%i%s' % (host, port, root))
|
||||
except Exception, e:
|
||||
logger.error('Could not launch browser: %s' % e)
|
||||
|
||||
def config_write():
|
||||
|
||||
new_config = ConfigObj()
|
||||
new_config.filename = CONFIG_FILE
|
||||
new_config = ConfigObj()
|
||||
new_config.filename = CONFIG_FILE
|
||||
|
||||
new_config['General'] = {}
|
||||
new_config['General']['http_port'] = HTTP_PORT
|
||||
new_config['General']['http_host'] = HTTP_HOST
|
||||
new_config['General']['http_username'] = HTTP_USERNAME
|
||||
new_config['General']['http_password'] = HTTP_PASSWORD
|
||||
new_config['General']['http_root'] = HTTP_ROOT
|
||||
new_config['General']['launch_browser'] = int(LAUNCH_BROWSER)
|
||||
new_config['General']['log_dir'] = LOG_DIR
|
||||
new_config['General']['git_path'] = GIT_PATH
|
||||
new_config['General'] = {}
|
||||
new_config['General']['http_port'] = HTTP_PORT
|
||||
new_config['General']['http_host'] = HTTP_HOST
|
||||
new_config['General']['http_username'] = HTTP_USERNAME
|
||||
new_config['General']['http_password'] = HTTP_PASSWORD
|
||||
new_config['General']['http_root'] = HTTP_ROOT
|
||||
new_config['General']['launch_browser'] = int(LAUNCH_BROWSER)
|
||||
new_config['General']['log_dir'] = LOG_DIR
|
||||
new_config['General']['git_path'] = GIT_PATH
|
||||
|
||||
new_config['General']['music_dir'] = MUSIC_DIR
|
||||
new_config['General']['destination_dir'] = DESTINATION_DIR
|
||||
new_config['General']['preferred_quality'] = PREFERRED_QUALITY
|
||||
new_config['General']['preferred_bitrate'] = PREFERRED_BITRATE
|
||||
new_config['General']['detect_bitrate'] = int(DETECT_BITRATE)
|
||||
new_config['General']['correct_metadata'] = int(CORRECT_METADATA)
|
||||
new_config['General']['move_files'] = int(MOVE_FILES)
|
||||
new_config['General']['rename_files'] = int(RENAME_FILES)
|
||||
new_config['General']['folder_format'] = FOLDER_FORMAT
|
||||
new_config['General']['file_format'] = FILE_FORMAT
|
||||
new_config['General']['cleanup_files'] = int(CLEANUP_FILES)
|
||||
new_config['General']['add_album_art'] = int(ADD_ALBUM_ART)
|
||||
new_config['General']['embed_album_art'] = int(EMBED_ALBUM_ART)
|
||||
new_config['General']['download_dir'] = DOWNLOAD_DIR
|
||||
new_config['General']['blackhole'] = int(BLACKHOLE)
|
||||
new_config['General']['blackhole_dir'] = BLACKHOLE_DIR
|
||||
new_config['General']['usenet_retention'] = USENET_RETENTION
|
||||
new_config['General']['include_extras'] = int(INCLUDE_EXTRAS)
|
||||
|
||||
new_config['General']['nzb_search_interval'] = NZB_SEARCH_INTERVAL
|
||||
new_config['General']['libraryscan_interval'] = LIBRARYSCAN_INTERVAL
|
||||
new_config['General']['music_dir'] = MUSIC_DIR
|
||||
new_config['General']['destination_dir'] = DESTINATION_DIR
|
||||
new_config['General']['preferred_quality'] = PREFERRED_QUALITY
|
||||
new_config['General']['preferred_bitrate'] = PREFERRED_BITRATE
|
||||
new_config['General']['detect_bitrate'] = int(DETECT_BITRATE)
|
||||
new_config['General']['correct_metadata'] = int(CORRECT_METADATA)
|
||||
new_config['General']['move_files'] = int(MOVE_FILES)
|
||||
new_config['General']['rename_files'] = int(RENAME_FILES)
|
||||
new_config['General']['folder_format'] = FOLDER_FORMAT
|
||||
new_config['General']['file_format'] = FILE_FORMAT
|
||||
new_config['General']['cleanup_files'] = int(CLEANUP_FILES)
|
||||
new_config['General']['add_album_art'] = int(ADD_ALBUM_ART)
|
||||
new_config['General']['embed_album_art'] = int(EMBED_ALBUM_ART)
|
||||
new_config['General']['download_dir'] = DOWNLOAD_DIR
|
||||
new_config['General']['blackhole'] = int(BLACKHOLE)
|
||||
new_config['General']['blackhole_dir'] = BLACKHOLE_DIR
|
||||
new_config['General']['usenet_retention'] = USENET_RETENTION
|
||||
new_config['General']['include_extras'] = int(INCLUDE_EXTRAS)
|
||||
|
||||
new_config['General']['nzb_search_interval'] = NZB_SEARCH_INTERVAL
|
||||
new_config['General']['libraryscan_interval'] = LIBRARYSCAN_INTERVAL
|
||||
|
||||
new_config['SABnzbd'] = {}
|
||||
new_config['SABnzbd']['sab_host'] = SAB_HOST
|
||||
new_config['SABnzbd']['sab_username'] = SAB_USERNAME
|
||||
new_config['SABnzbd']['sab_password'] = SAB_PASSWORD
|
||||
new_config['SABnzbd']['sab_apikey'] = SAB_APIKEY
|
||||
new_config['SABnzbd']['sab_category'] = SAB_CATEGORY
|
||||
new_config['SABnzbd'] = {}
|
||||
new_config['SABnzbd']['sab_host'] = SAB_HOST
|
||||
new_config['SABnzbd']['sab_username'] = SAB_USERNAME
|
||||
new_config['SABnzbd']['sab_password'] = SAB_PASSWORD
|
||||
new_config['SABnzbd']['sab_apikey'] = SAB_APIKEY
|
||||
new_config['SABnzbd']['sab_category'] = SAB_CATEGORY
|
||||
|
||||
new_config['NZBMatrix'] = {}
|
||||
new_config['NZBMatrix']['nzbmatrix'] = int(NZBMATRIX)
|
||||
new_config['NZBMatrix']['nzbmatrix_username'] = NZBMATRIX_USERNAME
|
||||
new_config['NZBMatrix']['nzbmatrix_apikey'] = NZBMATRIX_APIKEY
|
||||
new_config['NZBMatrix'] = {}
|
||||
new_config['NZBMatrix']['nzbmatrix'] = int(NZBMATRIX)
|
||||
new_config['NZBMatrix']['nzbmatrix_username'] = NZBMATRIX_USERNAME
|
||||
new_config['NZBMatrix']['nzbmatrix_apikey'] = NZBMATRIX_APIKEY
|
||||
|
||||
new_config['Newznab'] = {}
|
||||
new_config['Newznab']['newznab'] = int(NEWZNAB)
|
||||
new_config['Newznab']['newznab_host'] = NEWZNAB_HOST
|
||||
new_config['Newznab']['newznab_apikey'] = NEWZNAB_APIKEY
|
||||
new_config['Newznab'] = {}
|
||||
new_config['Newznab']['newznab'] = int(NEWZNAB)
|
||||
new_config['Newznab']['newznab_host'] = NEWZNAB_HOST
|
||||
new_config['Newznab']['newznab_apikey'] = NEWZNAB_APIKEY
|
||||
|
||||
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['General']['lastfm_username'] = LASTFM_USERNAME
|
||||
|
||||
new_config.write()
|
||||
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['General']['lastfm_username'] = LASTFM_USERNAME
|
||||
|
||||
new_config.write()
|
||||
|
||||
|
||||
|
||||
def start():
|
||||
|
||||
global __INITIALIZED__, started
|
||||
|
||||
if __INITIALIZED__:
|
||||
|
||||
# Start our scheduled background tasks
|
||||
|
||||
global __INITIALIZED__, started
|
||||
|
||||
if __INITIALIZED__:
|
||||
|
||||
# Start our scheduled background tasks
|
||||
|
||||
SCHED.add_cron_job(updater.dbUpdate, hour=4, minute=0, second=0)
|
||||
SCHED.add_interval_job(searcher.searchNZB, minutes=NZB_SEARCH_INTERVAL)
|
||||
SCHED.add_interval_job(importer.scanMusic, minutes=LIBRARYSCAN_INTERVAL)
|
||||
SCHED.add_interval_job(versioncheck.checkGithub, minutes=300)
|
||||
SCHED.add_interval_job(postprocessor.checkFolder, minutes=5)
|
||||
SCHED.add_cron_job(updater.dbUpdate, hour=4, minute=0, second=0)
|
||||
SCHED.add_interval_job(searcher.searchNZB, minutes=NZB_SEARCH_INTERVAL)
|
||||
SCHED.add_interval_job(importer.scanMusic, minutes=LIBRARYSCAN_INTERVAL)
|
||||
SCHED.add_interval_job(versioncheck.checkGithub, minutes=300)
|
||||
SCHED.add_interval_job(postprocessor.checkFolder, minutes=5)
|
||||
|
||||
SCHED.start()
|
||||
|
||||
started = True
|
||||
|
||||
SCHED.start()
|
||||
|
||||
started = True
|
||||
|
||||
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)')
|
||||
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)')
|
||||
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)')
|
||||
c.execute('CREATE TABLE IF NOT EXISTS snatched (AlbumID TEXT, Title TEXT, Size INTEGER, URL TEXT, DateAdded TEXT, Status TEXT, FolderName TEXT)')
|
||||
c.execute('CREATE TABLE IF NOT EXISTS have (ArtistName TEXT, AlbumTitle TEXT, TrackNumber TEXT, TrackTitle TEXT, TrackLength TEXT, BitRate TEXT, Genre TEXT, Date TEXT, TrackID TEXT)')
|
||||
c.execute('CREATE TABLE IF NOT EXISTS lastfmcloud (ArtistName TEXT, ArtistID TEXT, Count INTEGER)')
|
||||
|
||||
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:
|
||||
c.execute('ALTER TABLE albums ADD COLUMN Type TEXT DEFAULT "Album"')
|
||||
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)')
|
||||
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)')
|
||||
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)')
|
||||
c.execute('CREATE TABLE IF NOT EXISTS snatched (AlbumID TEXT, Title TEXT, Size INTEGER, URL TEXT, DateAdded TEXT, Status TEXT, FolderName TEXT)')
|
||||
c.execute('CREATE TABLE IF NOT EXISTS have (ArtistName TEXT, AlbumTitle TEXT, TrackNumber TEXT, TrackTitle TEXT, TrackLength TEXT, BitRate TEXT, Genre TEXT, Date TEXT, TrackID TEXT)')
|
||||
c.execute('CREATE TABLE IF NOT EXISTS lastfmcloud (ArtistName TEXT, ArtistID TEXT, Count INTEGER)')
|
||||
|
||||
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:
|
||||
c.execute('ALTER TABLE albums ADD COLUMN Type TEXT DEFAULT "Album"')
|
||||
|
||||
try:
|
||||
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')
|
||||
|
||||
conn.commit()
|
||||
c.close()
|
||||
try:
|
||||
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')
|
||||
|
||||
conn.commit()
|
||||
c.close()
|
||||
|
||||
|
||||
|
||||
def shutdown(restart=False, update=False):
|
||||
|
||||
cherrypy.engine.exit()
|
||||
SCHED.shutdown(wait=False)
|
||||
|
||||
config_write()
|
||||
|
||||
if update:
|
||||
try:
|
||||
versioncheck.update()
|
||||
except Exception, e:
|
||||
logger.warn('Headphones failed to update: %s. Restarting.' % e)
|
||||
|
||||
if restart:
|
||||
|
||||
popen_list = [sys.executable, FULL_PATH]
|
||||
popen_list += ARGS
|
||||
if '--nolaunch' not in popen_list:
|
||||
popen_list += ['--nolaunch']
|
||||
logger.info('Restarting Headphones with ' + str(popen_list))
|
||||
subprocess.Popen(popen_list, cwd=os.getcwd())
|
||||
|
||||
os._exit(0)
|
||||
|
||||
cherrypy.engine.exit()
|
||||
SCHED.shutdown(wait=False)
|
||||
|
||||
config_write()
|
||||
|
||||
if update:
|
||||
try:
|
||||
versioncheck.update()
|
||||
except Exception, e:
|
||||
logger.warn('Headphones failed to update: %s. Restarting.' % e)
|
||||
|
||||
if restart:
|
||||
|
||||
popen_list = [sys.executable, FULL_PATH]
|
||||
popen_list += ARGS
|
||||
if '--nolaunch' not in popen_list:
|
||||
popen_list += ['--nolaunch']
|
||||
logger.info('Restarting Headphones with ' + str(popen_list))
|
||||
subprocess.Popen(popen_list, cwd=os.getcwd())
|
||||
|
||||
os._exit(0)
|
||||
131
headphones/classes.py
Normal file
131
headphones/classes.py
Normal file
@@ -0,0 +1,131 @@
|
||||
# Author: Nic Wolfe <nic@wolfeden.ca>
|
||||
# URL: http://code.google.com/p/sickbeard/
|
||||
#
|
||||
# This file is part of Sick Beard.
|
||||
#
|
||||
# Sick Beard is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Sick Beard is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Sick Beard. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
|
||||
import headphones
|
||||
|
||||
import urllib
|
||||
import datetime
|
||||
|
||||
from common import USER_AGENT
|
||||
|
||||
class HeadphonesURLopener(urllib.FancyURLopener):
|
||||
version = USER_AGENT
|
||||
|
||||
class AuthURLOpener(HeadphonesURLopener):
|
||||
"""
|
||||
URLOpener class that supports http auth without needing interactive password entry.
|
||||
If the provided username/password don't work it simply fails.
|
||||
|
||||
user: username to use for HTTP auth
|
||||
pw: password to use for HTTP auth
|
||||
"""
|
||||
def __init__(self, user, pw):
|
||||
self.username = user
|
||||
self.password = pw
|
||||
|
||||
# remember if we've tried the username/password before
|
||||
self.numTries = 0
|
||||
|
||||
# call the base class
|
||||
urllib.FancyURLopener.__init__(self)
|
||||
|
||||
def prompt_user_passwd(self, host, realm):
|
||||
"""
|
||||
Override this function and instead of prompting just give the
|
||||
username/password that were provided when the class was instantiated.
|
||||
"""
|
||||
|
||||
# if this is the first try then provide a username/password
|
||||
if self.numTries == 0:
|
||||
self.numTries = 1
|
||||
return (self.username, self.password)
|
||||
|
||||
# if we've tried before then return blank which cancels the request
|
||||
else:
|
||||
return ('', '')
|
||||
|
||||
# this is pretty much just a hack for convenience
|
||||
def openit(self, url):
|
||||
self.numTries = 0
|
||||
return HeadphonesURLopener.open(self, url)
|
||||
|
||||
class SearchResult:
|
||||
"""
|
||||
Represents a search result from an indexer.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.provider = -1
|
||||
|
||||
# URL to the NZB/torrent file
|
||||
self.url = ""
|
||||
|
||||
# used by some providers to store extra info associated with the result
|
||||
self.extraInfo = []
|
||||
|
||||
# quality of the release
|
||||
self.quality = -1
|
||||
|
||||
# release name
|
||||
self.name = ""
|
||||
|
||||
def __str__(self):
|
||||
|
||||
if self.provider == None:
|
||||
return "Invalid provider, unable to print self"
|
||||
|
||||
myString = self.provider.name + " @ " + self.url + "\n"
|
||||
myString += "Extra Info:\n"
|
||||
for extra in self.extraInfo:
|
||||
myString += " " + extra + "\n"
|
||||
return myString
|
||||
|
||||
class NZBSearchResult(SearchResult):
|
||||
"""
|
||||
Regular NZB result with an URL to the NZB
|
||||
"""
|
||||
resultType = "nzb"
|
||||
|
||||
class NZBDataSearchResult(SearchResult):
|
||||
"""
|
||||
NZB result where the actual NZB XML data is stored in the extraInfo
|
||||
"""
|
||||
resultType = "nzbdata"
|
||||
|
||||
class TorrentSearchResult(SearchResult):
|
||||
"""
|
||||
Torrent result with an URL to the torrent
|
||||
"""
|
||||
resultType = "torrent"
|
||||
|
||||
class Proper:
|
||||
def __init__(self, name, url, date):
|
||||
self.name = name
|
||||
self.url = url
|
||||
self.date = date
|
||||
self.provider = None
|
||||
self.quality = -1
|
||||
|
||||
self.tvdbid = -1
|
||||
self.season = -1
|
||||
self.episode = -1
|
||||
|
||||
def __str__(self):
|
||||
return str(self.date)+" "+self.name+" "+str(self.season)+"x"+str(self.episode)+" of "+str(self.tvdbid)
|
||||
162
headphones/common.py
Normal file
162
headphones/common.py
Normal file
@@ -0,0 +1,162 @@
|
||||
'''
|
||||
Created on Aug 1, 2011
|
||||
|
||||
@author: Michael
|
||||
'''
|
||||
import platform, operator, os, re
|
||||
|
||||
from headphones import version
|
||||
|
||||
#Identify Our Application
|
||||
USER_AGENT = 'Headphones/-'+version.HEADPHONES_VERSION+' ('+platform.system()+' '+platform.release()+')'
|
||||
|
||||
### Notification Types
|
||||
NOTIFY_SNATCH = 1
|
||||
NOTIFY_DOWNLOAD = 2
|
||||
|
||||
notifyStrings = {}
|
||||
notifyStrings[NOTIFY_SNATCH] = "Started Download"
|
||||
notifyStrings[NOTIFY_DOWNLOAD] = "Download Finished"
|
||||
|
||||
### Release statuses
|
||||
UNKNOWN = -1 # should never happen
|
||||
UNAIRED = 1 # releases that haven't dropped yet
|
||||
SNATCHED = 2 # qualified with quality
|
||||
WANTED = 3 # releases we don't have but want to get
|
||||
DOWNLOADED = 4 # qualified with quality
|
||||
SKIPPED = 5 # releases we don't want
|
||||
ARCHIVED = 6 # releases that you don't have locally (counts toward download completion stats)
|
||||
IGNORED = 7 # releases that you don't want included in your download stats
|
||||
SNATCHED_PROPER = 9 # qualified with quality
|
||||
|
||||
class Quality:
|
||||
|
||||
NONE = 0
|
||||
B192 = 1<<1 # 2
|
||||
VBR = 1<<2 # 4
|
||||
B256 = 1<<3 # 8
|
||||
B320 = 1<<4 #16
|
||||
FLAC = 1<<5 #32
|
||||
|
||||
# put these bits at the other end of the spectrum, far enough out that they shouldn't interfere
|
||||
UNKNOWN = 1<<15
|
||||
|
||||
qualityStrings = {NONE: "N/A",
|
||||
UNKNOWN: "Unknown",
|
||||
B192: "MP3 192",
|
||||
VBR: "MP3 VBR",
|
||||
B256: "MP3 256",
|
||||
B320: "MP3 320",
|
||||
FLAC: "Flac"}
|
||||
|
||||
statusPrefixes = {DOWNLOADED: "Downloaded",
|
||||
SNATCHED: "Snatched"}
|
||||
|
||||
@staticmethod
|
||||
def _getStatusStrings(status):
|
||||
toReturn = {}
|
||||
for x in Quality.qualityStrings.keys():
|
||||
toReturn[Quality.compositeStatus(status, x)] = Quality.statusPrefixes[status]+" ("+Quality.qualityStrings[x]+")"
|
||||
return toReturn
|
||||
|
||||
@staticmethod
|
||||
def combineQualities(anyQualities, bestQualities):
|
||||
anyQuality = 0
|
||||
bestQuality = 0
|
||||
if anyQualities:
|
||||
anyQuality = reduce(operator.or_, anyQualities)
|
||||
if bestQualities:
|
||||
bestQuality = reduce(operator.or_, bestQualities)
|
||||
return anyQuality | (bestQuality<<16)
|
||||
|
||||
@staticmethod
|
||||
def splitQuality(quality):
|
||||
anyQualities = []
|
||||
bestQualities = []
|
||||
for curQual in Quality.qualityStrings.keys():
|
||||
if curQual & quality:
|
||||
anyQualities.append(curQual)
|
||||
if curQual<<16 & quality:
|
||||
bestQualities.append(curQual)
|
||||
|
||||
return (anyQualities, bestQualities)
|
||||
|
||||
@staticmethod
|
||||
def nameQuality(name):
|
||||
|
||||
name = os.path.basename(name)
|
||||
|
||||
# if we have our exact text then assume we put it there
|
||||
for x in Quality.qualityStrings:
|
||||
if x == Quality.UNKNOWN:
|
||||
continue
|
||||
|
||||
regex = '\W'+Quality.qualityStrings[x].replace(' ','\W')+'\W'
|
||||
regex_match = re.search(regex, name, re.I)
|
||||
if regex_match:
|
||||
return x
|
||||
|
||||
checkName = lambda list, func: func([re.search(x, name, re.I) for x in list])
|
||||
|
||||
#TODO: fix quality checking here
|
||||
if checkName(["mp3", "192"], any) and not checkName(["flac"], all):
|
||||
return Quality.B192
|
||||
elif checkName(["mp3", "256"], any) and not checkName(["flac"], all):
|
||||
return Quality.B256
|
||||
elif checkName(["mp3", "vbr"], any) and not checkName(["flac"], all):
|
||||
return Quality.VBR
|
||||
elif checkName(["mp3", "320"], any) and not checkName(["flac"], all):
|
||||
return Quality.B320
|
||||
else:
|
||||
return Quality.UNKNOWN
|
||||
|
||||
@staticmethod
|
||||
def assumeQuality(name):
|
||||
|
||||
if name.lower().endswith(".mp3"):
|
||||
return Quality.MP3
|
||||
elif name.lower().endswith(".flac"):
|
||||
return Quality.LOSSLESS
|
||||
else:
|
||||
return Quality.UNKNOWN
|
||||
|
||||
@staticmethod
|
||||
def compositeStatus(status, quality):
|
||||
return status + 100 * quality
|
||||
|
||||
@staticmethod
|
||||
def qualityDownloaded(status):
|
||||
return (status - DOWNLOADED) / 100
|
||||
|
||||
@staticmethod
|
||||
def splitCompositeStatus(status):
|
||||
"""Returns a tuple containing (status, quality)"""
|
||||
for x in sorted(Quality.qualityStrings.keys(), reverse=True):
|
||||
if status > x*100:
|
||||
return (status-x*100, x)
|
||||
|
||||
return (Quality.NONE, status)
|
||||
|
||||
@staticmethod
|
||||
def statusFromName(name, assume=True):
|
||||
quality = Quality.nameQuality(name)
|
||||
if assume and quality == Quality.UNKNOWN:
|
||||
quality = Quality.assumeQuality(name)
|
||||
return Quality.compositeStatus(DOWNLOADED, quality)
|
||||
|
||||
DOWNLOADED = None
|
||||
SNATCHED = None
|
||||
SNATCHED_PROPER = None
|
||||
|
||||
Quality.DOWNLOADED = [Quality.compositeStatus(DOWNLOADED, x) for x in Quality.qualityStrings.keys()]
|
||||
Quality.SNATCHED = [Quality.compositeStatus(SNATCHED, x) for x in Quality.qualityStrings.keys()]
|
||||
Quality.SNATCHED_PROPER = [Quality.compositeStatus(SNATCHED_PROPER, x) for x in Quality.qualityStrings.keys()]
|
||||
|
||||
MP3 = Quality.combineQualities([Quality.B192, Quality.B256, Quality.B320, Quality.VBR], [])
|
||||
LOSSLESS = Quality.combineQualities([Quality.FLAC], [])
|
||||
ANY = Quality.combineQualities([Quality.B192, Quality.B256, Quality.B320, Quality.VBR, Quality.FLAC], [])
|
||||
|
||||
qualityPresets = (MP3, LOSSLESS, ANY)
|
||||
qualityPresetStrings = {MP3: "MP3 (All bitrates 192+)",
|
||||
LOSSLESS: "Lossless (flac)",
|
||||
ANY: "Any"}
|
||||
123
headphones/sab.py
Normal file
123
headphones/sab.py
Normal file
@@ -0,0 +1,123 @@
|
||||
# This file is part of Sick Beard.
|
||||
#
|
||||
# Sick Beard is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Sick Beard is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Sick Beard. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
|
||||
import urllib, httplib
|
||||
import datetime
|
||||
|
||||
import headphones
|
||||
|
||||
from lib import MultipartPostHandler
|
||||
import urllib2, cookielib
|
||||
|
||||
from headphones.common import USER_AGENT
|
||||
from headphones import logger
|
||||
|
||||
|
||||
def sendNZB(nzb):
|
||||
|
||||
params = {}
|
||||
|
||||
if headphones.SAB_USERNAME != None:
|
||||
params['ma_username'] = headphones.SAB_USERNAME
|
||||
if headphones.SAB_PASSWORD != None:
|
||||
params['ma_password'] = headphones.SAB_PASSWORD
|
||||
if headphones.SAB_APIKEY != None:
|
||||
params['apikey'] = headphones.SAB_APIKEY
|
||||
if headphones.SAB_CATEGORY != None:
|
||||
params['cat'] = headphones.SAB_CATEGORY
|
||||
|
||||
|
||||
# # if released recently make it high priority
|
||||
# for curEp in nzb.episodes:
|
||||
# if datetime.date.today() - curEp.airdate <= datetime.timedelta(days=7):
|
||||
# params['priority'] = 1
|
||||
|
||||
# if it's a normal result we just pass SAB the URL
|
||||
if nzb.resultType == "nzb":
|
||||
# for newzbin results send the ID to sab specifically
|
||||
if nzb.provider.getID() == 'newzbin':
|
||||
id = nzb.provider.getIDFromURL(nzb.url)
|
||||
if not id:
|
||||
logger.info("Unable to send NZB to sab, can't find ID in URL "+str(nzb.url))
|
||||
return False
|
||||
params['mode'] = 'addid'
|
||||
params['name'] = id
|
||||
else:
|
||||
params['mode'] = 'addurl'
|
||||
params['name'] = nzb.url
|
||||
|
||||
# if we get a raw data result we want to upload it to SAB
|
||||
elif nzb.resultType == "nzbdata":
|
||||
params['mode'] = 'addfile'
|
||||
multiPartParams = {"nzbfile": (nzb.name+".nzb", nzb.extraInfo[0])}
|
||||
|
||||
url = "http://" + headphones.SAB_HOST + "/" + "api?" + urllib.urlencode(params)
|
||||
|
||||
logger.info(u"Sending NZB to SABnzbd")
|
||||
|
||||
logger.info(u"URL: " + url)
|
||||
|
||||
try:
|
||||
|
||||
if nzb.resultType == "nzb":
|
||||
f = urllib.urlopen(url)
|
||||
elif nzb.resultType == "nzbdata":
|
||||
cookies = cookielib.CookieJar()
|
||||
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookies),
|
||||
MultipartPostHandler.MultipartPostHandler)
|
||||
|
||||
req = urllib2.Request(url,
|
||||
multiPartParams,
|
||||
headers={'User-Agent': USER_AGENT})
|
||||
|
||||
f = opener.open(req)
|
||||
|
||||
except (EOFError, IOError), e:
|
||||
logger.info(u"Unable to connect to SAB: ")
|
||||
return False
|
||||
|
||||
except httplib.InvalidURL, e:
|
||||
logger.info(u"Invalid SAB host, check your config: ")
|
||||
return False
|
||||
|
||||
if f == None:
|
||||
logger.info(u"No data returned from SABnzbd, NZB not sent")
|
||||
return False
|
||||
|
||||
try:
|
||||
result = f.readlines()
|
||||
except Exception, e:
|
||||
logger.info(u"Error trying to get result from SAB, NZB not sent: ")
|
||||
return False
|
||||
|
||||
if len(result) == 0:
|
||||
logger.info(u"No data returned from SABnzbd, NZB not sent")
|
||||
return False
|
||||
|
||||
sabText = result[0].strip()
|
||||
|
||||
logger.info(u"Result text from SAB: " + sabText)
|
||||
|
||||
if sabText == "ok":
|
||||
logger.info(u"NZB sent to SAB successfully")
|
||||
return True
|
||||
elif sabText == "Missing authentication":
|
||||
logger.info(u"Incorrect username/password sent to SAB, NZB not sent")
|
||||
return False
|
||||
else:
|
||||
logger.info(u"Unknown failure sending NZB to sab. Return text is: " + sabText)
|
||||
return False
|
||||
@@ -1,299 +1,439 @@
|
||||
import urllib
|
||||
import lib.feedparser as feedparser
|
||||
from xml.dom import minidom
|
||||
import os, re
|
||||
from xml.parsers.expat import ExpatError
|
||||
import os, re, time
|
||||
|
||||
import headphones
|
||||
from headphones import logger, db, helpers
|
||||
from headphones import logger, db, helpers, classes, sab
|
||||
|
||||
class NewzbinDownloader(urllib.FancyURLopener):
|
||||
|
||||
def __init__(self):
|
||||
urllib.FancyURLopener.__init__(self)
|
||||
|
||||
def http_error_default(self, url, fp, errcode, errmsg, headers):
|
||||
|
||||
# if newzbin is throttling us, wait seconds and try again
|
||||
if errcode == 400:
|
||||
|
||||
newzbinErrCode = int(headers.getheader('X-DNZB-RCode'))
|
||||
|
||||
if newzbinErrCode == 450:
|
||||
rtext = str(headers.getheader('X-DNZB-RText'))
|
||||
result = re.search("wait (\d+) seconds", rtext)
|
||||
|
||||
elif newzbinErrCode == 401:
|
||||
logger.info("Newzbin error 401")
|
||||
#raise exceptions.AuthException("Newzbin username or password incorrect")
|
||||
|
||||
elif newzbinErrCode == 402:
|
||||
#raise exceptions.AuthException("Newzbin account not premium status, can't download NZBs")
|
||||
logger.info("Newzbin error 402")
|
||||
|
||||
logger.info("Newzbin throttled our NZB downloading, pausing for " + result.group(1) + "seconds")
|
||||
|
||||
time.sleep(int(result.group(1)))
|
||||
|
||||
#raise exceptions.NewzbinAPIThrottled()
|
||||
|
||||
#this should be in a class somewhere
|
||||
def getNewzbinURL(url):
|
||||
|
||||
myOpener = classes.AuthURLOpener(headphones.NEWZBIN_UID, headphones.NEWZBIN_PASSWORD)
|
||||
try:
|
||||
f = myOpener.openit(url)
|
||||
except (urllib.ContentTooShortError, IOError), e:
|
||||
logger.info("Error loading search results: ContentTooShortError ")
|
||||
return None
|
||||
|
||||
data = f.read()
|
||||
f.close()
|
||||
|
||||
return data
|
||||
|
||||
def searchNZB(albumid=None, new=False):
|
||||
|
||||
myDB = db.DBConnection()
|
||||
|
||||
if albumid:
|
||||
results = myDB.select('SELECT ArtistName, AlbumTitle, AlbumID, ReleaseDate from albums WHERE Status="Wanted" AND AlbumID=?', [albumid])
|
||||
else:
|
||||
results = myDB.select('SELECT ArtistName, AlbumTitle, AlbumID, ReleaseDate from albums WHERE Status="Wanted"')
|
||||
new = True
|
||||
|
||||
for albums in results:
|
||||
|
||||
albumid = albums[2]
|
||||
reldate = albums[3]
|
||||
|
||||
try:
|
||||
year = reldate[:4]
|
||||
except TypeError:
|
||||
year = ''
|
||||
|
||||
dic = {'...':'', ' & ':' ', ' = ': ' ', '?':'', '$':'s', ' + ':' ', '"':'', ',':''}
|
||||
myDB = db.DBConnection()
|
||||
|
||||
if albumid:
|
||||
results = myDB.select('SELECT ArtistName, AlbumTitle, AlbumID, ReleaseDate from albums WHERE Status="Wanted" AND AlbumID=?', [albumid])
|
||||
else:
|
||||
results = myDB.select('SELECT ArtistName, AlbumTitle, AlbumID, ReleaseDate from albums WHERE Status="Wanted"')
|
||||
new = True
|
||||
|
||||
for albums in results:
|
||||
|
||||
albumid = albums[2]
|
||||
reldate = albums[3]
|
||||
|
||||
try:
|
||||
year = reldate[:4]
|
||||
except TypeError:
|
||||
year = ''
|
||||
|
||||
dic = {'...':'', ' & ':' ', ' = ': ' ', '?':'', '$':'s', ' + ':' ', '"':'', ',':''}
|
||||
|
||||
cleanartistalbum = helpers.latinToAscii(helpers.replace_all(albums[0]+' '+albums[1], dic))
|
||||
cleanartistalbum = helpers.latinToAscii(helpers.replace_all(albums[0]+' '+albums[1], dic))
|
||||
|
||||
# FLAC usually doesn't have a year for some reason so I'll leave it out:
|
||||
term = re.sub('[\.\-]', ' ', '%s' % (cleanartistalbum)).encode('utf-8')
|
||||
altterm = re.sub('[\.\-]', ' ', '%s %s' % (cleanartistalbum, year)).encode('utf-8')
|
||||
|
||||
# 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 = altterm
|
||||
|
||||
logger.info("Searching for %s since it was marked as wanted" % term)
|
||||
|
||||
resultlist = []
|
||||
|
||||
if headphones.NZBMATRIX:
|
||||
# FLAC usually doesn't have a year for some reason so I'll leave it out:
|
||||
term = re.sub('[\.\-]', ' ', '%s' % (cleanartistalbum)).encode('utf-8')
|
||||
altterm = re.sub('[\.\-]', ' ', '%s %s' % (cleanartistalbum, year)).encode('utf-8')
|
||||
|
||||
# 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 = altterm
|
||||
|
||||
logger.info("Searching for %s since it was marked as wanted" % term)
|
||||
|
||||
resultlist = []
|
||||
|
||||
if headphones.NZBMATRIX:
|
||||
provider = "nzbmatrix"
|
||||
if headphones.PREFERRED_QUALITY == 3:
|
||||
categories = "23"
|
||||
maxsize = 10000000000
|
||||
elif headphones.PREFERRED_QUALITY:
|
||||
categories = "23,22"
|
||||
maxsize = 2000000000
|
||||
else:
|
||||
categories = "22"
|
||||
maxsize = 300000000
|
||||
|
||||
|
||||
params = { "page": "download",
|
||||
"username": headphones.NZBMATRIX_USERNAME,
|
||||
"apikey": headphones.NZBMATRIX_APIKEY,
|
||||
"subcat": categories,
|
||||
"age": headphones.USENET_RETENTION,
|
||||
"english": 1,
|
||||
"ssl": 1,
|
||||
"scenename": 1,
|
||||
"term": term
|
||||
}
|
||||
|
||||
searchURL = "http://rss.nzbmatrix.com/rss.php?" + urllib.urlencode(params)
|
||||
logger.info(u"Parsing results from "+searchURL)
|
||||
d = feedparser.parse(searchURL)
|
||||
|
||||
for item in d.entries:
|
||||
try:
|
||||
url = item.link
|
||||
title = item.title
|
||||
size = int(item.links[1]['length'])
|
||||
if size < maxsize:
|
||||
resultlist.append((title, size, url, provider))
|
||||
logger.info('Found %s. Size: %s' % (title, helpers.bytes_to_mb(size)))
|
||||
else:
|
||||
logger.info('%s is larger than the maxsize for this category, skipping. (Size: %i bytes)' % (title, size))
|
||||
|
||||
except AttributeError, e:
|
||||
logger.info(u"No results found from NZBMatrix for %s" % term)
|
||||
|
||||
if headphones.NEWZNAB:
|
||||
provider = "newznab"
|
||||
if headphones.PREFERRED_QUALITY == 3:
|
||||
categories = "3040"
|
||||
maxsize = 10000000000
|
||||
elif headphones.PREFERRED_QUALITY:
|
||||
categories = "3040,3010"
|
||||
maxsize = 2000000000
|
||||
else:
|
||||
categories = "3010"
|
||||
maxsize = 300000000
|
||||
|
||||
if headphones.PREFERRED_QUALITY == 3:
|
||||
categories = "23"
|
||||
maxsize = 10000000000
|
||||
elif headphones.PREFERRED_QUALITY:
|
||||
categories = "23,22"
|
||||
maxsize = 2000000000
|
||||
else:
|
||||
categories = "22"
|
||||
maxsize = 300000000
|
||||
|
||||
|
||||
params = { "page": "download",
|
||||
"username": headphones.NZBMATRIX_USERNAME,
|
||||
"apikey": headphones.NZBMATRIX_APIKEY,
|
||||
"subcat": categories,
|
||||
"age": headphones.USENET_RETENTION,
|
||||
"english": 1,
|
||||
"ssl": 1,
|
||||
"scenename": 1,
|
||||
"term": term
|
||||
}
|
||||
|
||||
searchURL = "http://rss.nzbmatrix.com/rss.php?" + urllib.urlencode(params)
|
||||
logger.info(u"Parsing results from "+searchURL)
|
||||
d = feedparser.parse(searchURL)
|
||||
|
||||
for item in d.entries:
|
||||
try:
|
||||
url = item.link
|
||||
title = item.title
|
||||
size = int(item.links[1]['length'])
|
||||
if size < maxsize:
|
||||
resultlist.append((title, size, url))
|
||||
logger.info('Found %s. Size: %s' % (title, helpers.bytes_to_mb(size)))
|
||||
else:
|
||||
logger.info('%s is larger than the maxsize for this category, skipping. (Size: %i bytes)' % (title, size))
|
||||
|
||||
except AttributeError, e:
|
||||
logger.info(u"No results found from NZBMatrix for %s" % term)
|
||||
|
||||
if headphones.NEWZNAB:
|
||||
|
||||
if headphones.PREFERRED_QUALITY == 3:
|
||||
categories = "3040"
|
||||
maxsize = 10000000000
|
||||
elif headphones.PREFERRED_QUALITY:
|
||||
categories = "3040,3010"
|
||||
maxsize = 2000000000
|
||||
else:
|
||||
categories = "3010"
|
||||
maxsize = 300000000
|
||||
params = { "t": "search",
|
||||
"apikey": headphones.NEWZNAB_APIKEY,
|
||||
"cat": categories,
|
||||
"maxage": headphones.USENET_RETENTION,
|
||||
"q": term
|
||||
}
|
||||
|
||||
searchURL = headphones.NEWZNAB_HOST + '/api?' + urllib.urlencode(params)
|
||||
logger.info(u"Parsing results from "+searchURL)
|
||||
|
||||
d = feedparser.parse(searchURL)
|
||||
|
||||
if not len(d.entries):
|
||||
logger.info(u"No results found from %s for %s" % (headphones.NEWZNAB_HOST, term))
|
||||
pass
|
||||
|
||||
else:
|
||||
for item in d.entries:
|
||||
try:
|
||||
url = item.link
|
||||
title = item.title
|
||||
size = int(item.links[1]['length'])
|
||||
if size < maxsize:
|
||||
resultlist.append((title, size, url, provider))
|
||||
logger.info('Found %s. Size: %s' % (title, helpers.bytes_to_mb(size)))
|
||||
else:
|
||||
logger.info('%s is larger than the maxsize for this category, skipping. (Size: %i bytes)' % (title, size))
|
||||
|
||||
except Exception, e:
|
||||
logger.error(u"An unknown error occured trying to parse the feed: %s" % e)
|
||||
|
||||
if headphones.NZBSORG:
|
||||
provider = "nzbsorg"
|
||||
if headphones.PREFERRED_QUALITY == 3:
|
||||
categories = "5"
|
||||
maxsize = 10000000000
|
||||
term = term + ' flac'
|
||||
elif headphones.PREFERRED_QUALITY:
|
||||
categories = "5"
|
||||
maxsize = 2000000000
|
||||
else:
|
||||
categories = "5"
|
||||
maxsize = 300000000
|
||||
|
||||
params = { "t": "search",
|
||||
"apikey": headphones.NEWZNAB_APIKEY,
|
||||
"cat": categories,
|
||||
"maxage": headphones.USENET_RETENTION,
|
||||
"q": term
|
||||
}
|
||||
|
||||
searchURL = headphones.NEWZNAB_HOST + '/api?' + urllib.urlencode(params)
|
||||
logger.info(u"Parsing results from "+searchURL)
|
||||
|
||||
d = feedparser.parse(searchURL)
|
||||
|
||||
if not len(d.entries):
|
||||
logger.info(u"No results found from %s for %s" % (headphones.NEWZNAB_HOST, term))
|
||||
pass
|
||||
|
||||
else:
|
||||
for item in d.entries:
|
||||
try:
|
||||
url = item.link
|
||||
title = item.title
|
||||
size = int(item.links[1]['length'])
|
||||
if size < maxsize:
|
||||
resultlist.append((title, size, url))
|
||||
logger.info('Found %s. Size: %s' % (title, helpers.bytes_to_mb(size)))
|
||||
else:
|
||||
logger.info('%s is larger than the maxsize for this category, skipping. (Size: %i bytes)' % (title, size))
|
||||
|
||||
except Exception, e:
|
||||
logger.error(u"An unknown error occured trying to parse the feed: %s" % e)
|
||||
|
||||
if headphones.NZBSORG:
|
||||
|
||||
if headphones.PREFERRED_QUALITY == 3:
|
||||
categories = "5"
|
||||
maxsize = 10000000000
|
||||
term = term + ' flac'
|
||||
elif headphones.PREFERRED_QUALITY:
|
||||
categories = "5"
|
||||
maxsize = 2000000000
|
||||
else:
|
||||
categories = "5"
|
||||
maxsize = 300000000
|
||||
params = { "action": "search",
|
||||
"dl": 1,
|
||||
"catid": categories,
|
||||
"i": headphones.NZBSORG_UID,
|
||||
"h": headphones.NZBSORG_HASH,
|
||||
"age": headphones.USENET_RETENTION,
|
||||
"q": term
|
||||
}
|
||||
|
||||
searchURL = 'https://secure.nzbs.org/rss.php?' + urllib.urlencode(params)
|
||||
|
||||
#data = urllib.urlopen(searchURL).read()
|
||||
data = urllib.urlopen(searchURL).read()
|
||||
|
||||
logger.info(u"Parsing results from "+searchURL)
|
||||
|
||||
try:
|
||||
d = minidom.parseString(data)
|
||||
node = d.documentElement
|
||||
items = d.getElementsByTagName("item")
|
||||
except ExpatError:
|
||||
logger.error('Unable to get the NZBs.org feed. Check that your settings are correct - post a bug if they are')
|
||||
items = None
|
||||
|
||||
if len(items):
|
||||
|
||||
for item in items:
|
||||
|
||||
sizenode = item.getElementsByTagName("report:size")[0].childNodes
|
||||
titlenode = item.getElementsByTagName("title")[0].childNodes
|
||||
linknode = item.getElementsByTagName("link")[0].childNodes
|
||||
|
||||
for node in sizenode:
|
||||
size = int(node.data)
|
||||
for node in titlenode:
|
||||
title = node.data
|
||||
for node in linknode:
|
||||
url = node.data
|
||||
|
||||
if size < maxsize:
|
||||
resultlist.append((title, size, url, provider))
|
||||
logger.info('Found %s. Size: %s' % (title, helpers.bytes_to_mb(size)))
|
||||
else:
|
||||
logger.info('%s is larger than the maxsize for this category, skipping. (Size: %i bytes)' % (title, size))
|
||||
|
||||
else:
|
||||
|
||||
logger.info('No results found from NZBs.org for %s' % term)
|
||||
|
||||
params = { "action": "search",
|
||||
"dl": 1,
|
||||
"catid": categories,
|
||||
"i": headphones.NZBSORG_UID,
|
||||
"h": headphones.NZBSORG_HASH,
|
||||
"age": headphones.USENET_RETENTION,
|
||||
"q": term
|
||||
}
|
||||
|
||||
searchURL = 'https://secure.nzbs.org/rss.php?' + urllib.urlencode(params)
|
||||
|
||||
data = urllib.urlopen(searchURL).read()
|
||||
|
||||
logger.info(u"Parsing results from "+searchURL)
|
||||
|
||||
try:
|
||||
d = minidom.parseString(data)
|
||||
node = d.documentElement
|
||||
items = d.getElementsByTagName("item")
|
||||
except ExpatError:
|
||||
logger.error('Unable to get the NZBs.org feed. Check that your settings are correct - post a bug if they are')
|
||||
items = None
|
||||
|
||||
if len(items):
|
||||
|
||||
for item in items:
|
||||
|
||||
sizenode = item.getElementsByTagName("report:size")[0].childNodes
|
||||
titlenode = item.getElementsByTagName("title")[0].childNodes
|
||||
linknode = item.getElementsByTagName("link")[0].childNodes
|
||||
|
||||
for node in sizenode:
|
||||
size = int(node.data)
|
||||
for node in titlenode:
|
||||
title = node.data
|
||||
for node in linknode:
|
||||
url = node.data
|
||||
|
||||
if size < maxsize:
|
||||
resultlist.append((title, size, url))
|
||||
logger.info('Found %s. Size: %s' % (title, helpers.bytes_to_mb(size)))
|
||||
else:
|
||||
logger.info('%s is larger than the maxsize for this category, skipping. (Size: %i bytes)' % (title, size))
|
||||
|
||||
else:
|
||||
|
||||
logger.info('No results found from NZBs.org for %s' % term)
|
||||
|
||||
if len(resultlist):
|
||||
|
||||
if headphones.PREFERRED_QUALITY == 2 and headphones.PREFERRED_BITRATE:
|
||||
if headphones.NEWZBIN:
|
||||
provider = "newzbin"
|
||||
providerurl = "https:/www.newzbin.com/"
|
||||
if headphones.PREFERRED_QUALITY == 3:
|
||||
categories = "7" #music
|
||||
format = "2" #flac
|
||||
maxsize = 10000000000
|
||||
elif headphones.PREFERRED_QUALITY:
|
||||
categories = "7" #music
|
||||
format = "10" #mp3+flac
|
||||
maxsize = 2000000000
|
||||
else:
|
||||
categories = "7" #music
|
||||
format = "8" #mp3
|
||||
maxsize = 300000000
|
||||
|
||||
logger.debug('Target bitrate: %s kbps' % headphones.PREFERRED_BITRATE)
|
||||
params = {
|
||||
"fpn": "p",
|
||||
'u_nfo_posts_only': 0,
|
||||
'u_url_posts_only': 0,
|
||||
'u_comment_posts_only': 0,
|
||||
'u_show_passworded': 0,
|
||||
"searchaction": "Search",
|
||||
#"dl": 1,
|
||||
"category": categories,
|
||||
"retention": headphones.USENET_RETENTION,
|
||||
"ps_rb_audio_format": format,
|
||||
"feed": "rss",
|
||||
"u_post_results_amt": 50, #this can default to a high number per user
|
||||
"hauth": 1,
|
||||
"q": term
|
||||
}
|
||||
searchURL = providerurl + "search/?%s" % urllib.urlencode(params)
|
||||
data = getNewzbinURL(searchURL)
|
||||
if data:
|
||||
logger.info(u"Parsing results from "+searchURL)
|
||||
|
||||
try:
|
||||
d = minidom.parseString(data)
|
||||
node = d.documentElement
|
||||
items = d.getElementsByTagName("item")
|
||||
except ExpatError:
|
||||
logger.info('Unable to get the NEWZBIN feed. Check that your settings are correct - post a bug if they are')
|
||||
items = None
|
||||
|
||||
if len(items):
|
||||
|
||||
for item in items:
|
||||
|
||||
sizenode = item.getElementsByTagName("report:size")[0].childNodes
|
||||
titlenode = item.getElementsByTagName("title")[0].childNodes
|
||||
linknode = item.getElementsByTagName("link")[0].childNodes
|
||||
|
||||
for node in sizenode:
|
||||
size = int(node.data)
|
||||
for node in titlenode:
|
||||
title = node.data
|
||||
for node in linknode:
|
||||
url = node.data
|
||||
|
||||
#exract the reportid from the link nodes
|
||||
id_regex = re.escape(providerurl) + 'browse/post/(\d+)/'
|
||||
id_match = re.match(id_regex, url)
|
||||
if not id_match:
|
||||
logger.info("Didn't find a valid Newzbin reportid in linknode")
|
||||
else:
|
||||
url = id_match.group(1) #we have to make a post request later, need the id
|
||||
if size < maxsize and url:
|
||||
resultlist.append((title, size, url, provider))
|
||||
logger.info('Found %s. Size: %s' % (title, helpers.bytes_to_mb(size)))
|
||||
else:
|
||||
logger.info('%s is larger than the maxsize for this category, skipping. (Size: %i bytes)' % (title, size))
|
||||
|
||||
else:
|
||||
logger.info('No results found from NEWZBIN for %s' % term)
|
||||
|
||||
if len(resultlist):
|
||||
|
||||
if headphones.PREFERRED_QUALITY == 2 and headphones.PREFERRED_BITRATE:
|
||||
|
||||
tracks = myDB.select('SELECT TrackDuration from tracks WHERE AlbumID=?', [albumid])
|
||||
logger.debug('Target bitrate: %s kbps' % headphones.PREFERRED_BITRATE)
|
||||
|
||||
try:
|
||||
albumlength = sum([pair[0] for pair in tracks])
|
||||
logger.debug('Album length = %s' % albumlength)
|
||||
targetsize = albumlength/1000 * int(headphones.PREFERRED_BITRATE) * 128
|
||||
logger.info('Target size: %s' % helpers.bytes_to_mb(targetsize))
|
||||
|
||||
newlist = []
|
||||
tracks = myDB.select('SELECT TrackDuration from tracks WHERE AlbumID=?', [albumid])
|
||||
|
||||
for result in resultlist:
|
||||
delta = abs(targetsize - result[1])
|
||||
newlist.append((result[0], result[1], result[2], delta))
|
||||
|
||||
nzblist = sorted(newlist, key=lambda title: title[3])
|
||||
|
||||
except Exception, e:
|
||||
|
||||
logger.debug('Error: %s' % str(e))
|
||||
logger.info('No track information for %s - %s. Defaulting to highest quality' % (albums[0], albums[1]))
|
||||
|
||||
nzblist = sorted(resultlist, key=lambda title: title[1], reverse=True)
|
||||
|
||||
else:
|
||||
|
||||
nzblist = sorted(resultlist, key=lambda title: title[1], reverse=True)
|
||||
|
||||
|
||||
if new:
|
||||
# Checks to see if it's already downloaded
|
||||
i = 0
|
||||
|
||||
while i < len(nzblist):
|
||||
alreadydownloaded = myDB.select('SELECT * from snatched WHERE URL=?', [nzblist[i][2]])
|
||||
|
||||
if len(alreadydownloaded) >= 1:
|
||||
logger.info('%s has already been downloaded. Skipping.' % nzblist[i][0])
|
||||
i += 1
|
||||
|
||||
else:
|
||||
bestqual = nzblist[i]
|
||||
break
|
||||
|
||||
try:
|
||||
x = bestqual[0]
|
||||
except UnboundLocalError:
|
||||
logger.info('No more matches for %s' % term)
|
||||
return
|
||||
|
||||
else:
|
||||
bestqual = nzblist[0]
|
||||
|
||||
|
||||
logger.info(u"Found best result: %s (%s) - %s" % (bestqual[0], bestqual[2], helpers.bytes_to_mb(bestqual[1])))
|
||||
|
||||
downloadurl = bestqual[2]
|
||||
nzb_folder_name = '%s - %s [%s]' % (helpers.latinToAscii(albums[0]).encode('UTF-8'), helpers.latinToAscii(albums[1]).encode('UTF-8'), year)
|
||||
try:
|
||||
albumlength = sum([pair[0] for pair in tracks])
|
||||
logger.debug('Album length = %s' % albumlength)
|
||||
targetsize = albumlength/1000 * int(headphones.PREFERRED_BITRATE) * 128
|
||||
logger.info('Target size: %s' % helpers.bytes_to_mb(targetsize))
|
||||
|
||||
newlist = []
|
||||
|
||||
if headphones.SAB_HOST and not headphones.BLACKHOLE:
|
||||
linkparams = {}
|
||||
|
||||
linkparams["mode"] = "addurl"
|
||||
|
||||
if headphones.SAB_APIKEY:
|
||||
linkparams["apikey"] = headphones.SAB_APIKEY
|
||||
if headphones.SAB_USERNAME:
|
||||
linkparams["ma_username"] = headphones.SAB_USERNAME
|
||||
if headphones.SAB_PASSWORD:
|
||||
linkparams["ma_password"] = headphones.SAB_PASSWORD
|
||||
if headphones.SAB_CATEGORY:
|
||||
linkparams["cat"] = headphones.SAB_CATEGORY
|
||||
|
||||
linkparams["name"] = downloadurl
|
||||
|
||||
linkparams["nzbname"] = nzb_folder_name
|
||||
|
||||
saburl = 'http://' + headphones.SAB_HOST + '/sabnzbd/api?' + urllib.urlencode(linkparams)
|
||||
logger.info(u"Sending link to SABNZBD: " + saburl)
|
||||
|
||||
try:
|
||||
urllib.urlopen(saburl)
|
||||
|
||||
except:
|
||||
logger.error(u"Unable to send link. Are you sure the host address is correct?")
|
||||
break
|
||||
|
||||
myDB.action('UPDATE albums SET status = "Snatched" WHERE AlbumID=?', [albums[2]])
|
||||
myDB.action('INSERT INTO snatched VALUES( ?, ?, ?, ?, DATETIME("NOW", "localtime"), ?, ?)', [albums[2], bestqual[0], bestqual[1], bestqual[2], "Snatched", nzb_folder_name])
|
||||
|
||||
|
||||
elif headphones.BLACKHOLE:
|
||||
|
||||
nzb_name = nzb_folder_name + '.nzb'
|
||||
download_path = os.path.join(headphones.BLACKHOLE_DIR, nzb_name)
|
||||
|
||||
try:
|
||||
urllib.urlretrieve(downloadurl, download_path)
|
||||
except Exception, e:
|
||||
logger.error('Couldn\'t retrieve NZB: %s' % e)
|
||||
break
|
||||
|
||||
myDB.action('UPDATE albums SET status = "Snatched" WHERE AlbumID=?', [albums[2]])
|
||||
myDB.action('INSERT INTO snatched VALUES( ?, ?, ?, ?, DATETIME("NOW", "localtime"), ?, ?)', [albums[2], bestqual[0], bestqual[1], bestqual[2], "Snatched", nzb_folder_name])
|
||||
for result in resultlist:
|
||||
delta = abs(targetsize - result[1])
|
||||
newlist.append((result[0], result[1], result[2], delta))
|
||||
|
||||
nzblist = sorted(newlist, key=lambda title: title[3])
|
||||
|
||||
except Exception, e:
|
||||
|
||||
logger.debug('Error: %s' % str(e))
|
||||
logger.info('No track information for %s - %s. Defaulting to highest quality' % (albums[0], albums[1]))
|
||||
|
||||
nzblist = sorted(resultlist, key=lambda title: title[1], reverse=True)
|
||||
|
||||
else:
|
||||
|
||||
nzblist = sorted(resultlist, key=lambda title: title[1], reverse=True)
|
||||
|
||||
|
||||
if new:
|
||||
# Checks to see if it's already downloaded
|
||||
i = 0
|
||||
|
||||
while i < len(nzblist):
|
||||
alreadydownloaded = myDB.select('SELECT * from snatched WHERE URL=?', [nzblist[i][2]])
|
||||
|
||||
if len(alreadydownloaded) >= 1:
|
||||
logger.info('%s has already been downloaded. Skipping.' % nzblist[i][0])
|
||||
i += 1
|
||||
|
||||
else:
|
||||
bestqual = nzblist[i]
|
||||
break
|
||||
|
||||
try:
|
||||
x = bestqual[0]
|
||||
except UnboundLocalError:
|
||||
logger.info('No more matches for %s' % term)
|
||||
return
|
||||
|
||||
else:
|
||||
bestqual = nzblist[0]
|
||||
|
||||
|
||||
logger.info(u"Found best result: %s (%s) - %s" % (bestqual[0], bestqual[2], helpers.bytes_to_mb(bestqual[1])))
|
||||
|
||||
if bestqual[3] == "newzbin":
|
||||
#logger.info("Found a newzbin result")
|
||||
reportid = bestqual[2]
|
||||
params = urllib.urlencode({"username": headphones.NEWZBIN_UID, "password": headphones.NEWZBIN_PASSWORD, "reportid": reportid})
|
||||
url = providerurl + "/api/dnzb/"
|
||||
urllib._urlopener = NewzbinDownloader()
|
||||
data = urllib.urlopen(url, data=params).read()
|
||||
nzb = classes.NZBDataSearchResult()
|
||||
nzb.extraInfo.append(data)
|
||||
nzb_folder_name = '%s - %s [%s]' % (helpers.latinToAscii(albums[0]).encode('UTF-8'), helpers.latinToAscii(albums[1]).encode('UTF-8'), year)
|
||||
nzb.name = nzb_folder_name
|
||||
logger.info(u"Sending FILE to SABNZBD: " + nzb.name)
|
||||
sab.sendNZB(nzb)
|
||||
|
||||
myDB.action('UPDATE albums SET status = "Snatched" WHERE AlbumID=?', [albums[2]])
|
||||
myDB.action('INSERT INTO snatched VALUES( ?, ?, ?, ?, DATETIME("NOW", "localtime"), ?, ?)', [albums[2], bestqual[0], bestqual[1], bestqual[2], "Snatched", nzb_folder_name])
|
||||
else:
|
||||
downloadurl = bestqual[2]
|
||||
nzb_folder_name = '%s - %s [%s]' % (helpers.latinToAscii(albums[0]).encode('UTF-8'), helpers.latinToAscii(albums[1]).encode('UTF-8'), year)
|
||||
|
||||
if headphones.SAB_HOST and not headphones.BLACKHOLE:
|
||||
linkparams = {}
|
||||
|
||||
linkparams["mode"] = "addurl"
|
||||
|
||||
if headphones.SAB_APIKEY:
|
||||
linkparams["apikey"] = headphones.SAB_APIKEY
|
||||
if headphones.SAB_USERNAME:
|
||||
linkparams["ma_username"] = headphones.SAB_USERNAME
|
||||
if headphones.SAB_PASSWORD:
|
||||
linkparams["ma_password"] = headphones.SAB_PASSWORD
|
||||
if headphones.SAB_CATEGORY:
|
||||
linkparams["cat"] = headphones.SAB_CATEGORY
|
||||
|
||||
linkparams["name"] = downloadurl
|
||||
|
||||
linkparams["nzbname"] = nzb_folder_name
|
||||
|
||||
saburl = 'http://' + headphones.SAB_HOST + '/sabnzbd/api?' + urllib.urlencode(linkparams)
|
||||
logger.info(u"Sending link to SABNZBD: " + saburl)
|
||||
|
||||
try:
|
||||
urllib.urlopen(saburl)
|
||||
|
||||
except:
|
||||
logger.error(u"Unable to send link. Are you sure the host address is correct?")
|
||||
break
|
||||
|
||||
myDB.action('UPDATE albums SET status = "Snatched" WHERE AlbumID=?', [albums[2]])
|
||||
myDB.action('INSERT INTO snatched VALUES( ?, ?, ?, ?, DATETIME("NOW", "localtime"), ?, ?)', [albums[2], bestqual[0], bestqual[1], bestqual[2], "Snatched", nzb_folder_name])
|
||||
|
||||
|
||||
elif headphones.BLACKHOLE:
|
||||
|
||||
nzb_name = nzb_folder_name + '.nzb'
|
||||
download_path = os.path.join(headphones.BLACKHOLE_DIR, nzb_name)
|
||||
|
||||
try:
|
||||
urllib.urlretrieve(downloadurl, download_path)
|
||||
except Exception, e:
|
||||
logger.error('Couldn\'t retrieve NZB: %s' % e)
|
||||
break
|
||||
|
||||
myDB.action('UPDATE albums SET status = "Snatched" WHERE AlbumID=?', [albums[2]])
|
||||
myDB.action('INSERT INTO snatched VALUES( ?, ?, ?, ?, DATETIME("NOW", "localtime"), ?, ?)', [albums[2], bestqual[0], bestqual[1], bestqual[2], "Snatched", nzb_folder_name])
|
||||
@@ -258,6 +258,32 @@ configform = form = '''
|
||||
<input type="text" name="nzbsorg_hash" value="%s" size="46" maxlength="40">
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<br>
|
||||
|
||||
<p>Newzbin:<input type="checkbox" name="newzbin" value="1" %s /></p>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<br>
|
||||
|
||||
<p>
|
||||
Newzbin UID:<br>
|
||||
<input type="text" name="newzbin_uid" value="%s" size="30" maxlength="40">
|
||||
</p>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<br>
|
||||
|
||||
<p>
|
||||
Newzbin Password:<br>
|
||||
<input type="text" name="newzbin_password" value="%s" size="46" maxlength="40">
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
@@ -565,6 +565,9 @@ class WebInterface(object):
|
||||
checked(headphones.NZBSORG),
|
||||
headphones.NZBSORG_UID,
|
||||
headphones.NZBSORG_HASH,
|
||||
checked(headphones.NEWZBIN),
|
||||
headphones.NEWZBIN_UID,
|
||||
headphones.NEWZBIN_PASSWORD,
|
||||
radio(headphones.PREFERRED_QUALITY, 0),
|
||||
radio(headphones.PREFERRED_QUALITY, 1),
|
||||
radio(headphones.PREFERRED_QUALITY, 3),
|
||||
@@ -592,7 +595,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,
|
||||
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, nzbmatrix=0, nzbmatrix_username=None, nzbmatrix_apikey=None, newznab=0, newznab_host=None, newznab_apikey=None,
|
||||
nzbsorg=0, nzbsorg_uid=None, nzbsorg_hash=None, preferred_quality=0, preferred_bitrate=None, detect_bitrate=0, move_files=0,
|
||||
nzbsorg=0, nzbsorg_uid=None, nzbsorg_hash=None, newzbin=0, newzbin_uid=None, newzbin_password=None, preferred_quality=0, preferred_bitrate=None, detect_bitrate=0, move_files=0,
|
||||
rename_files=0, correct_metadata=0, cleanup_files=0, add_album_art=0, embed_album_art=0, destination_dir=None, folder_format=None, file_format=None, include_extras=0, log_dir=None):
|
||||
|
||||
headphones.HTTP_HOST = http_host
|
||||
@@ -618,6 +621,9 @@ class WebInterface(object):
|
||||
headphones.NZBSORG = nzbsorg
|
||||
headphones.NZBSORG_UID = nzbsorg_uid
|
||||
headphones.NZBSORG_HASH = nzbsorg_hash
|
||||
headphones.NEWZBIN = newzbin
|
||||
headphones.NEWZBIN_UID = newzbin_uid
|
||||
headphones.NEWZBIN_PASSWORD = newzbin_password
|
||||
headphones.PREFERRED_QUALITY = int(preferred_quality)
|
||||
headphones.PREFERRED_BITRATE = preferred_bitrate
|
||||
headphones.DETECT_BITRATE = detect_bitrate
|
||||
|
||||
88
lib/MultipartPostHandler.py
Normal file
88
lib/MultipartPostHandler.py
Normal file
@@ -0,0 +1,88 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
####
|
||||
# 06/2010 Nic Wolfe <nic@wolfeden.ca>
|
||||
# 02/2006 Will Holcomb <wholcomb@gmail.com>
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
|
||||
import urllib
|
||||
import urllib2
|
||||
import mimetools, mimetypes
|
||||
import os, sys
|
||||
|
||||
# Controls how sequences are uncoded. If true, elements may be given multiple values by
|
||||
# assigning a sequence.
|
||||
doseq = 1
|
||||
|
||||
class MultipartPostHandler(urllib2.BaseHandler):
|
||||
handler_order = urllib2.HTTPHandler.handler_order - 10 # needs to run first
|
||||
|
||||
def http_request(self, request):
|
||||
data = request.get_data()
|
||||
if data is not None and type(data) != str:
|
||||
v_files = []
|
||||
v_vars = []
|
||||
try:
|
||||
for(key, value) in data.items():
|
||||
if type(value) in (file, list, tuple):
|
||||
v_files.append((key, value))
|
||||
else:
|
||||
v_vars.append((key, value))
|
||||
except TypeError:
|
||||
systype, value, traceback = sys.exc_info()
|
||||
raise TypeError, "not a valid non-string sequence or mapping object", traceback
|
||||
|
||||
if len(v_files) == 0:
|
||||
data = urllib.urlencode(v_vars, doseq)
|
||||
else:
|
||||
boundary, data = MultipartPostHandler.multipart_encode(v_vars, v_files)
|
||||
contenttype = 'multipart/form-data; boundary=%s' % boundary
|
||||
if(request.has_header('Content-Type')
|
||||
and request.get_header('Content-Type').find('multipart/form-data') != 0):
|
||||
print "Replacing %s with %s" % (request.get_header('content-type'), 'multipart/form-data')
|
||||
request.add_unredirected_header('Content-Type', contenttype)
|
||||
|
||||
request.add_data(data)
|
||||
return request
|
||||
|
||||
@staticmethod
|
||||
def multipart_encode(vars, files, boundary = None, buffer = None):
|
||||
if boundary is None:
|
||||
boundary = mimetools.choose_boundary()
|
||||
if buffer is None:
|
||||
buffer = ''
|
||||
for(key, value) in vars:
|
||||
buffer += '--%s\r\n' % boundary
|
||||
buffer += 'Content-Disposition: form-data; name="%s"' % key
|
||||
buffer += '\r\n\r\n' + value + '\r\n'
|
||||
for(key, fd) in files:
|
||||
|
||||
# allow them to pass in a file or a tuple with name & data
|
||||
if type(fd) == file:
|
||||
name_in = fd.name
|
||||
fd.seek(0)
|
||||
data_in = fd.read()
|
||||
elif type(fd) in (tuple, list):
|
||||
name_in, data_in = fd
|
||||
|
||||
filename = os.path.basename(name_in)
|
||||
contenttype = mimetypes.guess_type(filename)[0] or 'application/octet-stream'
|
||||
buffer += '--%s\r\n' % boundary
|
||||
buffer += 'Content-Disposition: form-data; name="%s"; filename="%s"\r\n' % (key, filename)
|
||||
buffer += 'Content-Type: %s\r\n' % contenttype
|
||||
# buffer += 'Content-Length: %s\r\n' % file_size
|
||||
buffer += '\r\n' + data_in + '\r\n'
|
||||
buffer += '--%s--\r\n\r\n' % boundary
|
||||
return boundary, buffer
|
||||
|
||||
https_request = http_request
|
||||
Reference in New Issue
Block a user