mirror of
https://github.com/evilhero/mylar
synced 2025-02-13 09:44:40 +00:00
torrents will now properly hide torrent information, IMP: Specified daemon port for deluge as an on-screen tip for more detail, IMP: Added 100,200,ALL as viewable watchlist views, FIX: When viewing pullist and annual integration enabled, if annual was present would incorrectly link to invalid annual series instead of the actual series itself, IMP: Added more detail error messages to metatagging errors and better handling of stranded files during cleanup, IMP: Improved some handling for weekly pull-list one-off's and refactored the nzb/oneoff post-processing into a seperate function for future callables, Moved all the main url locations for public torrent sites to the init module so that it can be cascaded down for use in other modules instead as a global, IMP: Added a 'deep_search_32p' variable in the config.ini for specific usage with 32p, where if there is more than one result will dig deeper into each result to try and figure out if there are series matches, as opposed to the default where it will only use ref32p table if available or just the first hit in a multiple series search results and ignore the remainder, FIX:Fixed some unknown characters appearing in the pullist due to unicode-related conversion problems, FIX: fixed some special cases of file parsing errors due to Volume label being named different than expected, FIX: Added a 3s pause between experimental searches to try and not hit their frequency limitation, IMP: Weekly Pullist One-off's will now show status of Snatched/Downloaded as required, FIX: Fixed some deluge parameter problems when using auto-snatch torrent script/option, IMP: Changed the downlocation in the auto-snatch option to an env variable instead of being passed to avoid unicode-related problems, FIX: Fixed some magnet-related issues for torrents when using a watchdir + TPSE, FIX: Added more verbose error message for rtorrent connection issues, FIX: Could not connect to rtorrent client if no username/password were provided, IMP: Set the db updater to run every 5 minutes on the watchlist, automatically refreshing the oldest updated series each time that is more than 5 hours old (force db update from the activity/job schedulers page will run the db updater against the entire watchlist in sequence), IMP: Attempt to handle long paths in windows (ie. > 256c) by prepending the unicode windows api character to the import a directory path (windows only), IMP: When manual metatagging a series, will update the series after all the metatagging has been completed as opposed to after each issue, IMP: Will now display available inkdrops on Config/Search Providers tab when using 32P (future will utilize/indicate inkdrop threshold when downloading)
274 lines
9.4 KiB
Python
274 lines
9.4 KiB
Python
#!/usr/bin/env python
|
|
# This file is part of Mylar.
|
|
#
|
|
# Mylar 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.
|
|
#
|
|
# Mylar 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 Mylar. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
import os, sys, locale
|
|
import errno
|
|
import shutil
|
|
import time
|
|
import threading
|
|
import signal
|
|
|
|
sys.path.insert(1, os.path.join(os.path.dirname(__file__), 'lib'))
|
|
|
|
import mylar
|
|
|
|
from mylar import webstart, logger, filechecker, versioncheck
|
|
|
|
import argparse
|
|
|
|
|
|
if ( sys.platform == 'win32' and sys.executable.split( '\\' )[-1] == 'pythonw.exe'):
|
|
sys.stdout = open(os.devnull, "w")
|
|
sys.stderr = open(os.devnull, "w")
|
|
|
|
def handler_sigterm(signum, frame):
|
|
mylar.SIGNAL = 'shutdown'
|
|
|
|
|
|
def main():
|
|
|
|
# Fixed paths to mylar
|
|
if hasattr(sys, 'frozen'):
|
|
mylar.FULL_PATH = os.path.abspath(sys.executable)
|
|
else:
|
|
mylar.FULL_PATH = os.path.abspath(__file__)
|
|
|
|
mylar.PROG_DIR = os.path.dirname(mylar.FULL_PATH)
|
|
mylar.ARGS = sys.argv[1:]
|
|
|
|
# From sickbeard
|
|
mylar.SYS_ENCODING = None
|
|
|
|
try:
|
|
locale.setlocale(locale.LC_ALL, "")
|
|
mylar.SYS_ENCODING = locale.getpreferredencoding()
|
|
except (locale.Error, IOError):
|
|
pass
|
|
|
|
# for OSes that are poorly configured I'll just force UTF-8
|
|
if not mylar.SYS_ENCODING or mylar.SYS_ENCODING in ('ANSI_X3.4-1968', 'US-ASCII', 'ASCII'):
|
|
mylar.SYS_ENCODING = 'UTF-8'
|
|
|
|
# Set up and gather command line arguments
|
|
parser = argparse.ArgumentParser(description='Automated Comic Book Downloader')
|
|
|
|
parser.add_argument('-v', '--verbose', action='store_true', help='Increase console logging verbosity')
|
|
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 mylar to run on a specified port')
|
|
parser.add_argument('-b', '--backup', action='store_true', help='Will automatically backup & keep the last 2 copies of the .db & ini files prior to startup')
|
|
parser.add_argument('-w', '--noweekly', action='store_true', help='Turn off weekly pull list check on startup (quicker boot sequence)')
|
|
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')
|
|
parser.add_argument('--pidfile', help='Create a pid file (only relevant when running as a daemon)')
|
|
parser.add_argument('--safe', action='store_true', help='redirect the startup page to point to the Manage Comics screen on startup')
|
|
#parser.add_argument('-u', '--update', action='store_true', help='force mylar to perform an update as if in GUI')
|
|
|
|
args = parser.parse_args()
|
|
|
|
if args.verbose:
|
|
mylar.VERBOSE = True
|
|
if args.quiet:
|
|
mylar.QUIET = True
|
|
|
|
# Do an intial setup of the logger.
|
|
logger.initLogger(console=not mylar.QUIET, log_dir=False,
|
|
verbose=mylar.VERBOSE)
|
|
|
|
#if args.update:
|
|
# print('Attempting to update Mylar so things can work again...')
|
|
# try:
|
|
# versioncheck.update()
|
|
# except Exception, e:
|
|
# sys.exit('Mylar failed to update.')
|
|
|
|
if args.daemon:
|
|
if sys.platform == 'win32':
|
|
print "Daemonize not supported under Windows, starting normally"
|
|
else:
|
|
mylar.DAEMON = True
|
|
|
|
if args.pidfile:
|
|
mylar.PIDFILE = str(args.pidfile)
|
|
|
|
# If the pidfile already exists, mylar may still be running, so exit
|
|
if os.path.exists(mylar.PIDFILE):
|
|
sys.exit("PID file '" + mylar.PIDFILE + "' already exists. Exiting.")
|
|
|
|
# The pidfile is only useful in daemon mode, make sure we can write the file properly
|
|
if mylar.DAEMON:
|
|
mylar.CREATEPID = True
|
|
try:
|
|
file(mylar.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:
|
|
mylar.DATA_DIR = args.datadir
|
|
else:
|
|
mylar.DATA_DIR = mylar.PROG_DIR
|
|
|
|
if args.config:
|
|
mylar.CONFIG_FILE = args.config
|
|
else:
|
|
mylar.CONFIG_FILE = os.path.join(mylar.DATA_DIR, 'config.ini')
|
|
|
|
if args.safe:
|
|
mylar.SAFESTART = True
|
|
else:
|
|
mylar.SAFESTART = False
|
|
|
|
if args.noweekly:
|
|
mylar.NOWEEKLY = True
|
|
else:
|
|
mylar.NOWEEKLY = False
|
|
|
|
# Try to create the DATA_DIR if it doesn't exist
|
|
#if not os.path.exists(mylar.DATA_DIR):
|
|
# try:
|
|
# os.makedirs(mylar.DATA_DIR)
|
|
# except OSError:
|
|
# raise SystemExit('Could not create data directory: ' + mylar.DATA_DIR + '. Exiting....')
|
|
|
|
filechecker.validateAndCreateDirectory(mylar.DATA_DIR, True)
|
|
|
|
# Make sure the DATA_DIR is writeable
|
|
if not os.access(mylar.DATA_DIR, os.W_OK):
|
|
raise SystemExit('Cannot write to the data directory: ' + mylar.DATA_DIR + '. Exiting...')
|
|
|
|
# Put the database in the DATA_DIR
|
|
mylar.DB_FILE = os.path.join(mylar.DATA_DIR, 'mylar.db')
|
|
|
|
# backup the db and configs before they load.
|
|
if args.backup:
|
|
print '[AUTO-BACKUP] Backing up .db and config.ini files for safety.'
|
|
backupdir = os.path.join(mylar.DATA_DIR, 'backup')
|
|
|
|
try:
|
|
os.makedirs(backupdir)
|
|
print '[AUTO-BACKUP] Directory does not exist for backup - creating : ' + backupdir
|
|
except OSError as exception:
|
|
if exception.errno != errno.EEXIST:
|
|
print '[AUTO-BACKUP] Directory already exists.'
|
|
raise
|
|
|
|
i = 0
|
|
while (i < 2):
|
|
if i == 0:
|
|
ogfile = mylar.DB_FILE
|
|
back = os.path.join(backupdir, 'mylar.db')
|
|
back_1 = os.path.join(backupdir, 'mylar.db.1')
|
|
else:
|
|
ogfile = mylar.CONFIG_FILE
|
|
back = os.path.join(backupdir, 'config.ini')
|
|
back_1 = os.path.join(backupdir, 'config.ini.1')
|
|
|
|
try:
|
|
print '[AUTO-BACKUP] Now Backing up mylar.db file'
|
|
if os.path.isfile(back_1):
|
|
print '[AUTO-BACKUP] ' + back_1 + ' exists. Deleting and keeping new.'
|
|
os.remove(back_1)
|
|
if os.path.isfile(back):
|
|
print '[AUTO-BACKUP] Now renaming ' + back + ' to ' + back_1
|
|
shutil.move(back, back_1)
|
|
print '[AUTO-BACKUP] Now copying db file to ' + back
|
|
shutil.copy(ogfile, back)
|
|
|
|
except OSError as exception:
|
|
if exception.errno != errno.EXIST:
|
|
raise
|
|
|
|
i += 1
|
|
|
|
from configobj import ConfigObj
|
|
mylar.CFG = ConfigObj(mylar.CONFIG_FILE, encoding='utf-8')
|
|
|
|
# Rename the main thread
|
|
threading.currentThread().name = "MAIN"
|
|
|
|
# Read config & start logging
|
|
mylar.initialize()
|
|
|
|
if mylar.DAEMON:
|
|
mylar.daemonize()
|
|
|
|
# Force the http port if neccessary
|
|
if args.port:
|
|
http_port = args.port
|
|
logger.info('Starting Mylar on foced port: %i' % http_port)
|
|
else:
|
|
http_port = int(mylar.HTTP_PORT)
|
|
|
|
# Check if pyOpenSSL is installed. It is required for certificate generation
|
|
# and for CherryPy.
|
|
if mylar.ENABLE_HTTPS:
|
|
try:
|
|
import OpenSSL
|
|
except ImportError:
|
|
logger.warn("The pyOpenSSL module is missing. Install this " \
|
|
"module to enable HTTPS. HTTPS will be disabled.")
|
|
mylar.ENABLE_HTTPS = False
|
|
|
|
# Try to start the server. Will exit here is address is already in use.
|
|
web_config = {
|
|
'http_port': http_port,
|
|
'http_host': mylar.HTTP_HOST,
|
|
'http_root': mylar.HTTP_ROOT,
|
|
'enable_https': mylar.ENABLE_HTTPS,
|
|
'https_cert': mylar.HTTPS_CERT,
|
|
'https_key': mylar.HTTPS_KEY,
|
|
'https_chain': mylar.HTTPS_CHAIN,
|
|
'http_username': mylar.HTTP_USERNAME,
|
|
'http_password': mylar.HTTP_PASSWORD,
|
|
}
|
|
|
|
# Try to start the server.
|
|
webstart.initialize(web_config)
|
|
|
|
#logger.info('Starting Mylar on port: %i' % http_port)
|
|
|
|
if mylar.LAUNCH_BROWSER and not args.nolaunch:
|
|
mylar.launch_browser(mylar.HTTP_HOST, http_port, mylar.HTTP_ROOT)
|
|
|
|
# Start the background threads
|
|
mylar.start()
|
|
|
|
signal.signal(signal.SIGTERM, handler_sigterm)
|
|
|
|
while True:
|
|
if not mylar.SIGNAL:
|
|
try:
|
|
time.sleep(1)
|
|
except KeyboardInterrupt:
|
|
mylar.SIGNAL = 'shutdown'
|
|
else:
|
|
logger.info('Received signal: ' + mylar.SIGNAL)
|
|
if mylar.SIGNAL == 'shutdown':
|
|
mylar.shutdown()
|
|
elif mylar.SIGNAL == 'restart':
|
|
mylar.shutdown(restart=True)
|
|
else:
|
|
mylar.shutdown(restart=True, update=True)
|
|
|
|
mylar.SIGNAL = None
|
|
|
|
return
|
|
|
|
if __name__ == "__main__":
|
|
main()
|