home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2011 January / maximum-cd-2011-01.iso / DiscContents / calibre-0.7.26.msi / file_952 (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2010-10-31  |  17.3 KB  |  568 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.6)
  3.  
  4. from __future__ import with_statement
  5. __license__ = 'GPL 3'
  6. __copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
  7. __docformat__ = 'restructuredtext en'
  8. import traceback
  9. import sys
  10. import textwrap
  11. import re
  12. from threading import Thread
  13. from calibre import prints
  14. from calibre.utils.config import OptionParser
  15. from calibre.utils.logging import default_log
  16. from calibre.utils.titlecase import titlecase
  17. from calibre.customize import Plugin
  18. from calibre.ebooks.metadata.covers import check_for_cover
  19. metadata_config = None
  20.  
  21. class MetadataSource(Plugin):
  22.     author = 'Kovid Goyal'
  23.     supported_platforms = [
  24.         'windows',
  25.         'osx',
  26.         'linux']
  27.     metadata_type = 'basic'
  28.     string_customization_help = None
  29.     type = _('Metadata download')
  30.     
  31.     def __call__(self, title, author, publisher, isbn, verbose, log = None, extra = None):
  32.         self.worker = Thread(target = self._fetch)
  33.         self.worker.daemon = True
  34.         self.title = title
  35.         self.verbose = verbose
  36.         self.book_author = author
  37.         self.publisher = publisher
  38.         self.isbn = isbn
  39.         self.log = None if log is not None else default_log
  40.         self.extra = extra
  41.         self.exception = None
  42.         self.tb = None
  43.         self.results = []
  44.         self.worker.start()
  45.  
  46.     
  47.     def _fetch(self):
  48.         
  49.         try:
  50.             self.fetch()
  51.             if self.results:
  52.                 c = self.config_store().get(self.name, { })
  53.                 res = self.results
  54.                 if hasattr(res, 'authors'):
  55.                     res = [
  56.                         res]
  57.                 
  58.                 for mi in res:
  59.                     if not c.get('rating', True):
  60.                         mi.rating = None
  61.                     
  62.                     if not c.get('comments', True):
  63.                         mi.comments = None
  64.                     
  65.                     if not c.get('tags', True):
  66.                         mi.tags = []
  67.                         continue
  68.                 
  69.         except Exception:
  70.             e = None
  71.             self.exception = e
  72.             self.tb = traceback.format_exc()
  73.  
  74.  
  75.     
  76.     def fetch(self):
  77.         raise NotImplementedError
  78.  
  79.     
  80.     def join(self):
  81.         return self.worker.join()
  82.  
  83.     
  84.     def is_customizable(self):
  85.         return True
  86.  
  87.     
  88.     def config_store(self):
  89.         global metadata_config
  90.         if metadata_config is None:
  91.             XMLConfig = XMLConfig
  92.             import calibre.utils.config
  93.             metadata_config = XMLConfig('plugins/metadata_download')
  94.         
  95.         return metadata_config
  96.  
  97.     
  98.     def config_widget(self):
  99.         QWidget = QWidget
  100.         QVBoxLayout = QVBoxLayout
  101.         QLabel = QLabel
  102.         Qt = Qt
  103.         QLineEdit = QLineEdit
  104.         QCheckBox = QCheckBox
  105.         import PyQt4.Qt
  106.         config = config
  107.         import calibre.customize.ui
  108.         w = QWidget()
  109.         w._layout = QVBoxLayout(w)
  110.         w.setLayout(w._layout)
  111.         if self.string_customization_help is not None:
  112.             w._sc_label = QLabel(self.string_customization_help, w)
  113.             w._layout.addWidget(w._sc_label)
  114.             customization = config['plugin_customization']
  115.             def_sc = customization.get(self.name, '')
  116.             if not def_sc:
  117.                 def_sc = ''
  118.             
  119.             w._sc = QLineEdit(def_sc, w)
  120.             w._layout.addWidget(w._sc)
  121.             w._sc_label.setWordWrap(True)
  122.             w._sc_label.setTextInteractionFlags(Qt.LinksAccessibleByMouse | Qt.LinksAccessibleByKeyboard)
  123.             w._sc_label.setOpenExternalLinks(True)
  124.         
  125.         c = self.config_store()
  126.         c = c.get(self.name, { })
  127.         for x, l in {
  128.             'rating': _('ratings'),
  129.             'tags': _('tags'),
  130.             'comments': _('description/reviews') }.items():
  131.             cb = QCheckBox(_('Download %s from %s') % (l, self.name))
  132.             setattr(w, '_' + x, cb)
  133.             cb.setChecked(c.get(x, True))
  134.             w._layout.addWidget(cb)
  135.         
  136.         return w
  137.  
  138.     
  139.     def save_settings(self, w):
  140.         dl_settings = { }
  141.         for x in ('rating', 'tags', 'comments'):
  142.             dl_settings[x] = getattr(w, '_' + x).isChecked()
  143.         
  144.         c = self.config_store()
  145.         c.set(self.name, dl_settings)
  146.         if hasattr(w, '_sc'):
  147.             sc = unicode(w._sc.text()).strip()
  148.             customize_plugin = customize_plugin
  149.             import calibre.customize.ui
  150.             customize_plugin(self, sc)
  151.         
  152.  
  153.     
  154.     def customization_help(self):
  155.         return 'This plugin can only be customized using the GUI'
  156.  
  157.  
  158.  
  159. class GoogleBooks(MetadataSource):
  160.     name = 'Google Books'
  161.     description = _('Downloads metadata from Google Books')
  162.     
  163.     def fetch(self):
  164.         search = search
  165.         import calibre.ebooks.metadata.google_books
  166.         
  167.         try:
  168.             self.results = search(self.title, self.book_author, self.publisher, self.isbn, max_results = 10, verbose = self.verbose)
  169.         except Exception:
  170.             e = None
  171.             self.exception = e
  172.             self.tb = traceback.format_exc()
  173.  
  174.  
  175.  
  176.  
  177. class ISBNDB(MetadataSource):
  178.     name = 'IsbnDB'
  179.     description = _('Downloads metadata from isbndb.com')
  180.     
  181.     def fetch(self):
  182.         if not self.site_customization:
  183.             return None
  184.         option_parser = option_parser
  185.         create_books = create_books
  186.         import calibre.ebooks.metadata.isbndb
  187.         args = [
  188.             'isbndb']
  189.         if self.isbn:
  190.             args.extend([
  191.                 '--isbn',
  192.                 self.isbn])
  193.         elif self.title:
  194.             args.extend([
  195.                 '--title',
  196.                 self.title])
  197.         
  198.         if self.book_author:
  199.             args.extend([
  200.                 '--author',
  201.                 self.book_author])
  202.         
  203.         if self.publisher:
  204.             args.extend([
  205.                 '--publisher',
  206.                 self.publisher])
  207.         
  208.         if self.verbose:
  209.             args.extend([
  210.                 '--verbose'])
  211.         
  212.         args.append(self.site_customization)
  213.         
  214.         try:
  215.             (opts, args) = option_parser().parse_args(args)
  216.             self.results = create_books(opts, args)
  217.         except Exception:
  218.             e = None
  219.             self.exception = e
  220.             self.tb = traceback.format_exc()
  221.  
  222.  
  223.     
  224.     def string_customization_help(self):
  225.         ans = _('To use isbndb.com you must sign up for a %sfree account%s and enter your access key below.')
  226.         return '<p>' + ans % ('<a href="http://www.isbndb.com">', '</a>')
  227.  
  228.     string_customization_help = property(string_customization_help)
  229.  
  230.  
  231. class Amazon(MetadataSource):
  232.     name = 'Amazon'
  233.     metadata_type = 'social'
  234.     description = _('Downloads social metadata from amazon.com')
  235.     
  236.     def fetch(self):
  237.         if not self.isbn:
  238.             return None
  239.         get_social_metadata = get_social_metadata
  240.         import calibre.ebooks.metadata.amazon
  241.         
  242.         try:
  243.             self.results = get_social_metadata(self.title, self.book_author, self.publisher, self.isbn)
  244.         except Exception:
  245.             self.isbn
  246.             e = self.isbn
  247.             self.exception = e
  248.             self.tb = traceback.format_exc()
  249.         except:
  250.             self.isbn
  251.  
  252.  
  253.  
  254.  
  255. class LibraryThing(MetadataSource):
  256.     name = 'LibraryThing'
  257.     metadata_type = 'social'
  258.     description = _('Downloads series/tags/rating information from librarything.com')
  259.     
  260.     def fetch(self):
  261.         if not self.isbn:
  262.             return None
  263.         get_social_metadata = get_social_metadata
  264.         import calibre.ebooks.metadata.library_thing
  265.         
  266.         try:
  267.             self.results = get_social_metadata(self.title, self.book_author, self.publisher, self.isbn)
  268.         except Exception:
  269.             self.isbn
  270.             e = self.isbn
  271.             self.exception = e
  272.             self.tb = traceback.format_exc()
  273.         except:
  274.             self.isbn
  275.  
  276.  
  277.  
  278.  
  279. def result_index(source, result):
  280.     if not result.isbn:
  281.         return -1
  282.     for i, x in enumerate(source):
  283.         if x.isbn == result.isbn:
  284.             return i
  285.     
  286.     return -1
  287.  
  288.  
  289. def merge_results(one, two):
  290.     for x in two:
  291.         idx = result_index(one, x)
  292.         if idx < 0:
  293.             one.append(x)
  294.             continue
  295.         one[idx].smart_update(x)
  296.     
  297.  
  298.  
  299. class MetadataSources(object):
  300.     
  301.     def __init__(self, sources):
  302.         self.sources = sources
  303.  
  304.     
  305.     def __enter__(self):
  306.         for s in self.sources:
  307.             s.__enter__()
  308.         
  309.         return self
  310.  
  311.     
  312.     def __exit__(self, *args):
  313.         for s in self.sources:
  314.             s.__exit__()
  315.         
  316.  
  317.     
  318.     def __call__(self, *args, **kwargs):
  319.         for s in self.sources:
  320.             s(*args, **kwargs)
  321.         
  322.  
  323.     
  324.     def join(self):
  325.         for s in self.sources:
  326.             s.join()
  327.         
  328.  
  329.  
  330.  
  331. def filter_metadata_results(item):
  332.     keywords = [
  333.         'audio',
  334.         'tape',
  335.         'cassette',
  336.         'abridged',
  337.         'playaway']
  338.     for keyword in keywords:
  339.         if item.publisher and keyword in item.publisher.lower():
  340.             return False
  341.     
  342.     return True
  343.  
  344.  
  345. def do_cover_check(item):
  346.     item.has_cover = False
  347.     
  348.     try:
  349.         item.has_cover = check_for_cover(item)
  350.     except:
  351.         pass
  352.  
  353.  
  354.  
  355. def check_for_covers(items):
  356.     threads = [ Thread(target = do_cover_check, args = (item,)) for item in items ]
  357.     for t in threads:
  358.         t.start()
  359.     
  360.     for t in threads:
  361.         t.join()
  362.     
  363.  
  364.  
  365. def search(title = None, author = None, publisher = None, isbn = None, isbndb_key = None, verbose = 0):
  366.     metadata_sources = metadata_sources
  367.     migrate_isbndb_key = migrate_isbndb_key
  368.     import calibre.customize.ui
  369.     migrate_isbndb_key()
  370.     if isbn is not None:
  371.         isbn = re.sub('[^a-zA-Z0-9]', '', isbn).upper()
  372.     
  373.     fetchers = list(metadata_sources(isbndb_key = isbndb_key))
  374.     
  375.     try:
  376.         manager = _[1]
  377.         manager(title, author, publisher, isbn, verbose)
  378.         manager.join()
  379.     finally:
  380.         pass
  381.  
  382.     results = list(fetchers[0].results)
  383.     for fetcher in fetchers[1:]:
  384.         merge_results(results, fetcher.results)
  385.     
  386.     results = list(filter(filter_metadata_results, results))
  387.     check_for_covers(results)
  388.     words = ('the', 'a', 'an', 'of', 'and')
  389.     prefix_pat = re.compile('^(%s)\\s+' % '|'.join(words))
  390.     trailing_paren_pat = re.compile('\\(.*\\)$')
  391.     whitespace_pat = re.compile('\\s+')
  392.     
  393.     def sort_func(x, y):
  394.         
  395.         def cleanup_title(s):
  396.             if s is None:
  397.                 s = _('Unknown')
  398.             
  399.             s = s.strip().lower()
  400.             s = prefix_pat.sub(' ', s)
  401.             s = trailing_paren_pat.sub('', s)
  402.             s = whitespace_pat.sub(' ', s)
  403.             return s.strip()
  404.  
  405.         t = cleanup_title(title)
  406.         x_title = cleanup_title(x.title)
  407.         y_title = cleanup_title(y.title)
  408.         tx = cmp(t, x_title)
  409.         ty = cmp(t, y_title)
  410.         result = (None, None, None) if abs(tx) == abs(ty) else abs(tx) - abs(ty)
  411.         if result == 0:
  412.             result = -cmp(x.has_cover, y.has_cover)
  413.         
  414.         if result == 0:
  415.             cx = None(len if x.comments else '')
  416.             cy = None(len if y.comments else '')
  417.             t = (cx + cy) / 20
  418.             result = cy - cx
  419.             if abs(result) < t:
  420.                 result = 0
  421.             
  422.         
  423.         return result
  424.  
  425.     results = sorted(results, cmp = sort_func)
  426.     if len(results) > 1:
  427.         if not (results[0].comments) or len(results[0].comments) == 0:
  428.             for r in results[1:]:
  429.                 
  430.                 try:
  431.                     if title and title.lower() == r.title[:len(title)].lower() and r.comments and len(r.comments):
  432.                         results[0].comments = r.comments
  433.                         break
  434.                 continue
  435.                 continue
  436.  
  437.             
  438.         
  439.         pubdate = None
  440.         for r in results:
  441.             if r.pubdate is not None:
  442.                 pubdate = r.pubdate
  443.                 break
  444.                 continue
  445.         
  446.         if pubdate is not None:
  447.             for r in results:
  448.                 if r.pubdate is None:
  449.                     r.pubdate = pubdate
  450.                     continue
  451.             
  452.         
  453.     
  454.     
  455.     def fix_case(x):
  456.         if x and x.isupper():
  457.             x = titlecase(x)
  458.         
  459.         return x
  460.  
  461.     for r in results:
  462.         r.title = fix_case(r.title)
  463.         if r.authors:
  464.             r.authors = list(map(fix_case, r.authors))
  465.             continue
  466.     
  467.     return ([], [ (x.name, x.exception, x.tb) for x in fetchers ])
  468.  
  469.  
  470. def get_social_metadata(mi, verbose = 0):
  471.     metadata_sources = metadata_sources
  472.     import calibre.customize.ui
  473.     fetchers = list(metadata_sources(metadata_type = 'social'))
  474.     
  475.     try:
  476.         manager = _[1]
  477.         manager(mi.title, mi.authors, mi.publisher, mi.isbn, verbose)
  478.         manager.join()
  479.     finally:
  480.         pass
  481.  
  482.     (ratings, tags, comments, series, series_index) = ([], set([]), set([]), None, None)
  483.     for fetcher in fetchers:
  484.         if fetcher.results:
  485.             dmi = fetcher.results
  486.             if dmi.tags:
  487.                 for t in dmi.tags:
  488.                     tags.add(t)
  489.                 
  490.             
  491.             if mi.pubdate is None and dmi.pubdate is not None:
  492.                 mi.pubdate = dmi.pubdate
  493.             
  494.             if dmi.comments:
  495.                 comments.add(dmi.comments)
  496.             
  497.             if dmi.series is not None:
  498.                 series = dmi.series
  499.                 if dmi.series_index is not None:
  500.                     series_index = dmi.series_index
  501.                 
  502.             
  503.         dmi.series is not None
  504.     
  505.     if ratings:
  506.         rating = sum(ratings) / float(len(ratings))
  507.         if mi.rating is None or mi.rating < 0.1:
  508.             mi.rating = rating
  509.         else:
  510.             mi.rating = (mi.rating + rating) / 2
  511.     
  512.     if tags:
  513.         if not mi.tags:
  514.             mi.tags = []
  515.         
  516.         mi.tags += list(tags)
  517.         mi.tags = list(sorted(list(set(mi.tags))))
  518.     
  519.     if comments:
  520.         if not (mi.comments) or len(mi.comments) + 20 < len(' '.join(comments)):
  521.             mi.comments = ''
  522.             for x in comments:
  523.                 mi.comments += x + '\n\n'
  524.             
  525.         
  526.     
  527.     if series and series_index is not None:
  528.         mi.series = series
  529.         mi.series_index = series_index
  530.     
  531.     return _[2]
  532.  
  533.  
  534. def option_parser():
  535.     parser = OptionParser(textwrap.dedent('        %prog [options]\n\n        Fetch book metadata from online sources. You must specify at least one\n        of title, author, publisher or ISBN. If you specify ISBN, the others\n        are ignored.\n        '))
  536.     parser.add_option('-t', '--title', help = 'Book title')
  537.     parser.add_option('-a', '--author', help = 'Book author(s)')
  538.     parser.add_option('-p', '--publisher', help = 'Book publisher')
  539.     parser.add_option('-i', '--isbn', help = 'Book ISBN')
  540.     parser.add_option('-m', '--max-results', default = 10, help = 'Maximum number of results to fetch')
  541.     parser.add_option('-k', '--isbndb-key', help = "The access key for your ISBNDB.com account. Only needed if you want to search isbndb.com and you haven't customized the IsbnDB plugin.")
  542.     parser.add_option('-v', '--verbose', default = 0, action = 'count', help = 'Be more verbose about errors')
  543.     return parser
  544.  
  545.  
  546. def main(args = sys.argv):
  547.     parser = option_parser()
  548.     (opts, args) = parser.parse_args(args)
  549.     (results, exceptions) = search(opts.title, opts.author, opts.publisher, opts.isbn, opts.isbndb_key, opts.verbose)
  550.     social_exceptions = []
  551.     for result in results:
  552.         social_exceptions.extend(get_social_metadata(result, opts.verbose))
  553.         prints(unicode(result))
  554.         print 
  555.     
  556.     for name, exception, tb in exceptions + social_exceptions:
  557.         if exception is not None:
  558.             print 'WARNING: Fetching from', name, 'failed with error:'
  559.             print exception
  560.             print tb
  561.             continue
  562.     
  563.     return 0
  564.  
  565. if __name__ == '__main__':
  566.     sys.exit(main())
  567.  
  568.