2012-06-20 19:58:13 +00:00
#!/usr/bin/env python
2012-06-20 20:41:04 +00:00
# -*- coding: iso-8859-1 -*-
2012-06-20 19:58:13 +00:00
# Dosage, the webcomic downloader
# Copyright (C) 2004-2005 Tristan Seligmann and Jonathan Jacobs
2012-06-20 20:41:04 +00:00
# Copyright (C) 2012 Bastian Kleineidam
2012-06-20 19:58:13 +00:00
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of version 2 of the GNU General Public License as
# published by the Free Software Foundation.
#
# This program 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 this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
import sys
import os
import optparse
from dosagelib import events, scraper
from dosagelib.output import out
2012-06-20 20:33:26 +00:00
from dosagelib.util import is_tty, get_columns, internal_error
2012-06-20 19:58:13 +00:00
from dosagelib.configuration import App, Freeware, Copyright
def setupOptions():
2012-09-26 14:47:39 +00:00
"""Construct option parser.
@return: new option parser
@rtype optparse.OptionParser
"""
2012-06-20 19:58:13 +00:00
usage = 'usage: %prog [options] comicModule [comicModule ...]'
parser = optparse.OptionParser(usage=usage)
parser.add_option('-v', '--verbose', action='count', dest='verbose', default=0, help='provides verbose output, use multiple times for more verbosity')
2012-10-11 13:43:16 +00:00
parser.add_option('-a', '--all', action='count', dest='all', default=None, help='traverse and retrieve all available comics')
2012-10-11 10:03:12 +00:00
parser.add_option('-b', '--basepath', action='store', dest='basepath', default='Comics', help='set the path to create invidivual comic directories in, default is Comics', metavar='PATH')
parser.add_option('--baseurl', action='store', dest='baseurl', default=None, help='the base URL of your comics directory (for RSS, HTML, etc.); this should correspond to --base-path', metavar='PATH')
2012-06-20 19:58:13 +00:00
parser.add_option('-l', '--list', action='store_const', const=1, dest='list', help='list available comic modules')
2012-10-11 10:03:12 +00:00
parser.add_option('--singlelist', action='store_const', const=2, dest='list', help='list available comic modules in a single list')
2012-06-20 19:58:13 +00:00
parser.add_option('-V', '--version', action='store_true', dest='version', help='display the version number')
2012-10-11 10:03:12 +00:00
parser.add_option('-m', '--modulehelp', action='store_true', dest='modhelp', help='display help for comic modules')
2012-06-20 19:58:13 +00:00
parser.add_option('-t', '--timestamps', action='store_true', dest='timestamps', default=False, help='print timestamps for all output at any info level')
parser.add_option('-o', '--output', action='store', dest='output', choices=events.getHandlers(), help='output formatting for downloaded comics')
2012-10-11 13:43:16 +00:00
parser.add_option('-p', '--progress', action='store_true', dest='progress', default=False, help='display progress bar while downloading comics')
2012-06-20 19:58:13 +00:00
return parser
2012-09-26 14:47:39 +00:00
def displayVersion():
"""Display application name, version, copyright and license."""
print App
print Copyright
print Freeware
2012-10-11 10:03:12 +00:00
return 0
2012-09-26 14:47:39 +00:00
2012-10-11 10:03:12 +00:00
def setOutputInfo(options):
"""Set global output level and timestamp option."""
out.level = 0
out.level += options.verbose
out.timestamps = options.timestamps
2012-06-20 19:58:13 +00:00
2012-10-11 10:03:12 +00:00
def saveComicStrip(strip, basepath, progress):
"""Save a comic strip which can consist of multiple images."""
errors = 0
for image in strip.getImages():
2012-06-20 19:58:13 +00:00
try:
2012-10-11 10:03:12 +00:00
image.save(basepath, progress)
except IOError, msg:
out.write('Error saving %s: %s' % (image.filename, msg))
errors += 1
return errors
def displayHelp(comics, basepath):
"""Print help for comic strips."""
for scraperobj in getScrapers(comics, basepath):
for line in scraperobj.getHelp().splitlines():
out.write("Help: "+line)
return 0
def getComics(options, comics):
errors = 0
events.installHandler(options.output, options.basepath, options.baseurl)
events.handler.start()
for scraperobj in getScrapers(comics, options.basepath):
out.context = scraperobj.get_name()
2012-10-11 13:43:16 +00:00
if options.all:
out.write('Retrieving all strips...')
2012-10-11 10:03:12 +00:00
strips = scraperobj.getAllStrips()
else:
out.write('Retrieving the current strip...')
strips = [scraperobj.getCurrentStrip()]
for strip in strips:
errors += saveComicStrip(strip, options.basepath, options.progress)
events.handler.end()
return errors
def run(options, comics):
"""Execute comic commands."""
setOutputInfo(options)
if options.version:
return displayVersion()
if options.list:
return doList(options.list == 1)
if len(comics) <= 0:
out.write('Warning: No comics specified, bailing out!')
return 1
if options.modhelp:
return displayHelp(comics, options.basepath)
2012-10-11 12:17:25 +00:00
return getComics(options, comics)
2012-10-11 10:03:12 +00:00
def doList(columnList):
"""List available comics."""
out.write('Available comic scrapers:')
scrapers = getScrapers(['@@'])
if columnList:
doColumnList(scrapers)
else:
doSingleList(scrapers)
out.write('%d supported comics.' % len(scrapers))
return 0
def doSingleList(scrapers):
"""Get list of scraper names, one per line."""
print '\n'.join(scraperobj.get_name() for scraperobj in scrapers)
def doColumnList(scrapers):
"""Get list of scraper names with multiple names per line."""
screenWidth = get_columns()
names = [scraperobj.get_name() for scraperobj in scrapers]
maxlen = max([len(name) for name in names])
namesPerLine = int(screenWidth / (maxlen + 1))
while names:
print ''.join([name.ljust(maxlen) for name in names[:namesPerLine]])
del names[:namesPerLine]
def getScrapers(comics, basepath=None):
"""Get scraper objects for the given comics."""
if '@' in comics:
# only scrapers whose directory already exists
if len(comics) > 1:
out.write("WARN: using '@' as comic name ignores all other specified comics.\n")
for scraperclass in scraper.get_scrapers():
dirname = scraperclass.get_name().replace('/', os.sep)
if os.path.isdir(os.path.join(basepath, dirname)):
yield scraperclass()
elif '@@' in comics:
# all scrapers
for scraperclass in scraper.get_scrapers():
yield scraperclass()
else:
# only selected
for comic in comics:
2012-10-11 12:17:25 +00:00
if ':' in comic:
name, index = comic.split(':', 1)
indices = index.split(',')
2012-06-20 19:58:13 +00:00
else:
2012-10-11 12:17:25 +00:00
name = comic
2012-10-11 10:03:12 +00:00
indices = None
2012-10-11 12:17:25 +00:00
yield scraper.get_scraper(name)(indices=indices)
2012-06-20 19:58:13 +00:00
def main():
2012-09-26 14:47:39 +00:00
"""Parse options and execute commands."""
2012-06-20 19:58:13 +00:00
try:
parser = setupOptions()
options, args = parser.parse_args()
2012-10-11 13:43:16 +00:00
if not is_tty(sys.stdout) and options.progress:
options.progress = False
2012-10-11 10:03:12 +00:00
res = run(options, args)
2012-06-20 19:58:13 +00:00
except KeyboardInterrupt:
print "Aborted."
res = 1
except Exception:
internal_error()
res = 2
return res
if __name__ == '__main__':
sys.exit(main())