#!/usr/bin/env python # -*- coding: iso-8859-1 -*- # Dosage, the webcomic downloader # Copyright (C) 2004-2005 Tristan Seligmann and Jonathan Jacobs # Copyright (C) 2012 Bastian Kleineidam # # 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 from dosagelib.util import is_tty, get_columns, internal_error from dosagelib.configuration import App, Freeware, Copyright def setupOptions(): """Construct option parser. @return: new option parser @rtype optparse.OptionParser """ 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') parser.add_option('-a', '--all', action='count', dest='all', default=None, help='traverse and retrieve all available comics') 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') parser.add_option('-l', '--list', action='store_const', const=1, dest='list', help='list available comic modules') parser.add_option('--singlelist', action='store_const', const=2, dest='list', help='list available comic modules in a single list') parser.add_option('-V', '--version', action='store_true', dest='version', help='display the version number') parser.add_option('-m', '--modulehelp', action='store_true', dest='modhelp', help='display help for comic modules') 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') parser.add_option('-p', '--progress', action='store_true', dest='progress', default=False, help='display progress bar while downloading comics') return parser def displayVersion(): """Display application name, version, copyright and license.""" print App print Copyright print Freeware return 0 def setOutputInfo(options): """Set global output level and timestamp option.""" out.level = 0 out.level += options.verbose out.timestamps = options.timestamps def saveComicStrip(strip, basepath, progress): """Save a comic strip which can consist of multiple images.""" errors = 0 for image in strip.getImages(): try: 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() if options.all: out.write('Retrieving all strips...') 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) return getComics(options, comics) 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: if ':' in comic: name, index = comic.split(':', 1) indices = index.split(',') else: name = comic indices = None yield scraper.get_scraper(name)(indices=indices) def main(): """Parse options and execute commands.""" try: parser = setupOptions() options, args = parser.parse_args() if not is_tty(sys.stdout) and options.progress: options.progress = False res = run(options, args) except KeyboardInterrupt: print "Aborted." res = 1 except Exception: internal_error() res = 2 return res if __name__ == '__main__': sys.exit(main())