import sys import os import optparse import traceback from dosage import events, scraper from dosage.output import out from dosage.util import getWindowSize from dosage.version import VERSION def setupOptions(): 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('-q', '--quiet', action='count', dest='quiet', default=0, help='suppress all output') parser.add_option('-c', '--catch-up', action='count', dest='catchup', default=None, help='traverse and retrieve all available comics up until the strip that already exists locally, use twice to retrieve until all strips exist locally') parser.add_option('-b', '--base-path', action='store', dest='basepath', default='Comics', help='set the path to create invidivual comic directories in, default is Comics', metavar='PATH') parser.add_option('--base-url', 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('--single-list', 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', '--module-help', 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') try: getWindowSize() except NotImplementedError: progress = False else: progress = True if progress: parser.add_option('-p', '--progress', action='store_true', dest='progress', default=False, help='display progress bar while downloading comics') return parser class Dosage(object): def setOutputInfo(self): out.level = 0 out.level += self.settings['verbose'] out.level -= self.settings['quiet'] out.timestamps = self.settings['timestamps'] def saveComic(self, comic): basepath = self.settings['basepath'] progress = self.settings.get('progress', False) fn, saved = comic.save(basepath, progress) return saved def saveComics(self, comics): saved = False for comic in comics: saved = self.saveComic(comic) or saved return saved def safeOp(self, fp, *args, **kwargs): try: fp(*args, **kwargs) except: type, value, tb = sys.exc_info() out.write('Traceback (most recent call last):', 1) out.writelines(traceback.format_stack(), 1) out.writelines(traceback.format_tb(tb)[1:], 1) out.writelines(traceback.format_exception_only(type, value), 0) def getCurrent(self): out.write('Retrieving the current strip...', 0) self.saveComics(self.module.getCurrentComics()) def getIndex(self, index): out.write('Retrieving index "%s"....' % (index,), 0) try: self.module.setStrip(index) self.saveComics(self.module.getNextComics()) except NotImplementedError: out.write('No indexed retrieval support.') def catchup(self): out.write('Catching up...', 0) for comics in self.module: if not self.saveComics(comics) and self.settings['catchup'] < 2: break def catchupIndex(self, index): out.write('Catching up from index "%s"...' % (index,), 0) self.module.setStrip(index) for comics in self.module: if not self.saveComics(comics) and self.settings['catchup'] < 2: break def getScrapers(self): return scraper.items() def getExistingComics(self): for scraper in self.getScrapers(): dirname = scraper.name.replace('/', os.sep) if os.path.isdir(os.path.join(self.settings['basepath'], dirname)): yield scraper def doList(self, columnList): out.write('Available comic scrapers:', 0) scrapers = self.getScrapers() if columnList: self.doColumnList(scrapers) else: self.doSingleList(scrapers) out.write('%d supported comics.' % len(scrapers), 0) def doSingleList(self, scrapers): print '\n'.join(scraper.name for scraper in scrapers) def doColumnList(self, scrapers): try: screenWidth = getWindowSize() except NotImplementedError: screenWidth = 80 if len(scrapers) == 0: return names = [scraper.name for scraper 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 doCatchup(self): for comic in self.useComics(): if self.indices: self.safeOp(self.catchupIndex, self.indices[0]) else: self.safeOp(self.catchup) def doCurrent(self): for comic in self.useComics(): if self.indices: for index in self.indices: self.safeOp(self.getIndex, index) else: self.safeOp(self.getCurrent) def doHelp(self): for scraper in self.useComics(): out.write('Help for %s:' % (scraper.name,), 0, context='') for line in scraper.getHelp().splitlines(): out.write(line, -1) def setupComic(self, scraper): self.module = scraper() out.context = scraper.name return self.module def useComics(self): for comic in self.comics: c = comic.split(':', 2) if len(c) > 1: self.indices = c[1].split(',') else: self.indices = None moduleName = c[0] if moduleName == '@': for s in self.getExistingComics(): yield self.setupComic(s) elif moduleName == '@@': for s in self.getScrapers(): yield self.setupComic(s) else: yield self.setupComic(scraper.get(moduleName)) def displayVersion(self): print '''Dosage v.%s Copyright (C) 2004-2008 Jonathan Jacobs and Tristan Seligmann This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.''' % VERSION def displayShortVersion(self): print VERSION def run(self, settings, comics): self.settings = settings self.setOutputInfo() self.comics = comics om = self.settings['output'] events.installHandler(om, self.settings['basepath'], self.settings['baseurl']) events.handler.start() if self.settings['list']: self.doList(self.settings['list'] == 1) elif self.settings['version']: if out.level < 0: self.displayShortVersion() else: self.displayVersion() elif len(comics) <= 0: out.write('Warning: No comics specified, bailing out!', 0) elif self.settings['modhelp']: self.doHelp() elif self.settings['catchup']: self.doCatchup() else: self.doCurrent() events.handler.end() def main(): parser = setupOptions() options, args = parser.parse_args() d = Dosage() d.run(options.__dict__, args) if __name__ == '__main__': main()