home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2010 November / maximum-cd-2010-11.iso / DiscContents / calibre-0.7.13.msi / file_1151 (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2010-08-06  |  17.2 KB  |  500 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.6)
  3.  
  4. import os
  5. import shutil
  6. import time
  7. import re
  8. from Queue import Queue, Empty
  9. from threading import Thread
  10. from PyQt4.Qt import QThread, SIGNAL, QObject, QTimer, Qt, QProgressDialog
  11. from calibre.gui2.dialogs.progress import ProgressDialog
  12. from calibre.gui2 import question_dialog, error_dialog, info_dialog
  13. from calibre.ebooks.metadata.opf2 import OPF
  14. from calibre.ebooks.metadata import MetaInformation
  15. from calibre.constants import preferred_encoding, filesystem_encoding
  16. from calibre.utils.config import prefs
  17.  
  18. class DuplicatesAdder(QThread):
  19.     
  20.     def __init__(self, parent, db, duplicates, db_adder):
  21.         QThread.__init__(self, parent)
  22.         self.db = db
  23.         self.db_adder = db_adder
  24.         self.duplicates = duplicates
  25.  
  26.     
  27.     def run(self):
  28.         count = 1
  29.         for mi, cover, formats in self.duplicates:
  30.             formats = _[1]
  31.             id = self.db.create_book_entry(mi, cover = cover, add_duplicates = True)
  32.             self.db_adder.add_formats(id, formats)
  33.             self.db_adder.number_of_books_added += 1
  34.             self.emit(SIGNAL('added(PyQt_PyObject)'), count)
  35.             count += 1
  36.         
  37.         self.emit(SIGNAL('adding_done()'))
  38.  
  39.  
  40.  
  41. class RecursiveFind(QThread):
  42.     
  43.     def __init__(self, parent, db, root, single):
  44.         QThread.__init__(self, parent)
  45.         self.db = db
  46.         self.path = root
  47.         self.single_book_per_directory = single
  48.         self.canceled = False
  49.  
  50.     
  51.     def walk(self, root):
  52.         self.books = []
  53.         for dirpath in os.walk(root):
  54.             if self.canceled:
  55.                 return None
  56.             self.emit(SIGNAL('update(PyQt_PyObject)'), _('Searching in') + ' ' + dirpath[0])
  57.             self.books += list(self.db.find_books_in_directory(dirpath[0], self.single_book_per_directory))
  58.         
  59.  
  60.     
  61.     def run(self):
  62.         root = os.path.abspath(self.path)
  63.         
  64.         try:
  65.             self.walk(root)
  66.         except:
  67.             
  68.             try:
  69.                 if isinstance(root, unicode):
  70.                     root = root.encode(filesystem_encoding)
  71.                 
  72.                 self.walk(root)
  73.             except Exception:
  74.                 err = None
  75.                 import traceback
  76.                 traceback.print_exc()
  77.                 
  78.                 try:
  79.                     msg = unicode(err)
  80.                 except:
  81.                     msg = repr(err)
  82.  
  83.                 self.emit(SIGNAL('found(PyQt_PyObject)'), msg)
  84.                 return None
  85.             
  86.  
  87.  
  88.         self.books = _[1]
  89.  
  90.  
  91.  
  92. class DBAdder(Thread):
  93.     
  94.     def __init__(self, db, ids, nmap):
  95.         self.db = db
  96.         self.ids = dict(**ids)
  97.         self.nmap = dict(**nmap)
  98.         self.end = False
  99.         self.critical = { }
  100.         self.number_of_books_added = 0
  101.         self.duplicates = []
  102.         self.names = []
  103.         self.paths = []
  104.         self.infos = []
  105.         Thread.__init__(self)
  106.         self.daemon = True
  107.         self.input_queue = Queue()
  108.         self.output_queue = Queue()
  109.         self.fuzzy_title_patterns = [ (re.compile(pat), repl) for pat, repl in [
  110.             ('[\\[\\](){}<>\\\'";,:#]', ''),
  111.             ('^(the|a|an) ', ''),
  112.             ('[-._]', ' '),
  113.             ('\\s+', ' ')] ]
  114.         self.merged_books = set([])
  115.  
  116.     
  117.     def run(self):
  118.         while not self.end:
  119.             
  120.             try:
  121.                 (id, opf, cover) = self.input_queue.get(True, 0.2)
  122.             except Empty:
  123.                 continue
  124.  
  125.             name = self.nmap.pop(id)
  126.             title = None
  127.             
  128.             try:
  129.                 title = self.add(id, opf, cover, name)
  130.             except:
  131.                 import traceback
  132.                 self.critical[name] = traceback.format_exc()
  133.                 title = name
  134.  
  135.             self.output_queue.put(title)
  136.  
  137.     
  138.     def process_formats(self, opf, formats):
  139.         imp = opf[:-4] + '.import'
  140.         if not os.access(imp, os.R_OK):
  141.             return formats
  142.         fmt_map = { }
  143.         for line in open(imp, 'rb').readlines():
  144.             if ':' not in line:
  145.                 continue
  146.             
  147.             (f, _, p) = line.partition(':')
  148.             fmt_map[f] = p.rstrip()
  149.         
  150.         fmts = []
  151.         for fmt in formats:
  152.             e = os.path.splitext(fmt)[1].replace('.', '').lower()
  153.             fmts.append(fmt_map.get(e, fmt))
  154.             if not os.access(fmts[-1], os.R_OK):
  155.                 fmts[-1] = fmt
  156.                 continue
  157.         
  158.         return fmts
  159.  
  160.     
  161.     def fuzzy_title(self, title):
  162.         title = title.strip().lower()
  163.         for pat, repl in self.fuzzy_title_patterns:
  164.             title = pat.sub(repl, title)
  165.         
  166.         return title
  167.  
  168.     
  169.     def find_identical_books(self, mi):
  170.         identical_book_ids = set([])
  171.         if mi.authors:
  172.             
  173.             try:
  174.                 query = []([ u'author:"=%s"' % a.replace('"', '') for a in mi.authors ])
  175.             except ValueError:
  176.                 return identical_book_ids
  177.  
  178.             
  179.             try:
  180.                 book_ids = self.db.data.parse(query)
  181.             except:
  182.                 import traceback
  183.                 traceback.print_exc()
  184.                 return identical_book_ids
  185.  
  186.             for book_id in book_ids:
  187.                 fbook_title = self.db.title(book_id, index_is_id = True)
  188.                 fbook_title = self.fuzzy_title(fbook_title)
  189.                 mbook_title = self.fuzzy_title(mi.title)
  190.                 if fbook_title == mbook_title:
  191.                     identical_book_ids.add(book_id)
  192.                     continue
  193.             
  194.         
  195.         return identical_book_ids
  196.  
  197.     
  198.     def add(self, id, opf, cover, name):
  199.         formats = self.ids.pop(id)
  200.         if opf.endswith('.error'):
  201.             mi = MetaInformation('', [
  202.                 _('Unknown')])
  203.             self.critical[name] = open(opf, 'rb').read().decode('utf-8', 'replace')
  204.         else:
  205.             
  206.             try:
  207.                 mi = MetaInformation(OPF(opf))
  208.             except:
  209.                 import traceback
  210.                 mi = MetaInformation('', [
  211.                     _('Unknown')])
  212.                 self.critical[name] = traceback.format_exc()
  213.  
  214.         formats = self.process_formats(opf, formats)
  215.         if not mi.title:
  216.             mi.title = os.path.splitext(name)[0]
  217.         
  218.         mi.title = None if isinstance(mi.title, unicode) else mi.title.decode(preferred_encoding, 'replace')
  219.         if mi.application_id == '__calibre_dummy__':
  220.             mi.application_id = None
  221.         
  222.         return mi.title
  223.  
  224.     
  225.     def add_formats(self, id, formats, replace = True):
  226.         for path in formats:
  227.             fmt = os.path.splitext(path)[-1].replace('.', '').upper()
  228.             
  229.             try:
  230.                 f = _[1]
  231.                 self.db.add_format(id, fmt, f, index_is_id = True, notify = False, replace = replace)
  232.             finally:
  233.                 pass
  234.  
  235.         
  236.  
  237.  
  238.  
  239. class Adder(QObject):
  240.     ADD_TIMEOUT = 600
  241.     
  242.     def __init__(self, parent, db, callback, spare_server = None):
  243.         QObject.__init__(self, parent)
  244.         self.pd = ProgressDialog(_('Adding...'), parent = parent)
  245.         self.spare_server = spare_server
  246.         self.db = db
  247.         self.pd.setModal(True)
  248.         self.pd.show()
  249.         self._parent = parent
  250.         self.rfind = None
  251.         self.worker = None
  252.         self.timer = None
  253.         self.callback = callback
  254.         self.callback_called = False
  255.         self.connect(self.pd, SIGNAL('canceled()'), self.canceled)
  256.  
  257.     
  258.     def add_recursive(self, root, single = True):
  259.         self.path = root
  260.         self.pd.set_msg(_('Searching in all sub-directories...'))
  261.         self.pd.set_min(0)
  262.         self.pd.set_max(0)
  263.         self.pd.value = 0
  264.         self.rfind = RecursiveFind(self, self.db, root, single)
  265.         self.connect(self.rfind, SIGNAL('update(PyQt_PyObject)'), self.pd.set_msg, Qt.QueuedConnection)
  266.         self.connect(self.rfind, SIGNAL('found(PyQt_PyObject)'), self.add, Qt.QueuedConnection)
  267.         self.rfind.start()
  268.  
  269.     
  270.     def add(self, books):
  271.         if isinstance(books, basestring):
  272.             error_dialog(self.pd, _('Path error'), _('The specified directory could not be processed.'), det_msg = books, show = True)
  273.             return self.canceled()
  274.         if not books:
  275.             info_dialog(self.pd, _('No books'), _('No books found'), show = True)
  276.             return self.canceled()
  277.         books = [ _[1] if isinstance(b, basestring) else b for b in books ]
  278.         self.rfind = None
  279.         read_metadata = read_metadata
  280.         import calibre.ebooks.metadata.worker
  281.         self.rq = Queue()
  282.         tasks = []
  283.         self.ids = { }
  284.         self.nmap = { }
  285.         self.duplicates = []
  286.         for i, b in enumerate(books):
  287.             tasks.append((i, b))
  288.             self.ids[i] = b
  289.             self.nmap[i] = os.path.basename(b[0])
  290.         
  291.         self.worker = read_metadata(tasks, self.rq, spare_server = self.spare_server)
  292.         self.pd.set_min(0)
  293.         self.pd.set_max(len(self.ids))
  294.         self.pd.value = 0
  295.         self.timer = QTimer(self)
  296.         self.db_adder = DBAdder(self.db, self.ids, self.nmap)
  297.         self.db_adder.start()
  298.         self.connect(self.timer, SIGNAL('timeout()'), self.update)
  299.         self.last_added_at = time.time()
  300.         self.entry_count = len(self.ids)
  301.         self.timer.start(200)
  302.  
  303.     
  304.     def canceled(self):
  305.         if self.rfind is not None:
  306.             self.rfind.canceled = True
  307.         
  308.         if self.timer is not None:
  309.             self.timer.stop()
  310.         
  311.         if self.worker is not None:
  312.             self.worker.canceled = True
  313.         
  314.         if hasattr(self, 'db_adder'):
  315.             self.db_adder.end = True
  316.         
  317.         self.pd.hide()
  318.         if not self.callback_called:
  319.             self.callback(self.paths, self.names, self.infos)
  320.             self.callback_called = True
  321.         
  322.  
  323.     
  324.     def duplicates_processed(self):
  325.         self.db_adder.end = True
  326.         if not self.callback_called:
  327.             self.callback(self.paths, self.names, self.infos)
  328.             self.callback_called = True
  329.         
  330.         if hasattr(self, '__p_d'):
  331.             self._Adder__p_d.hide()
  332.         
  333.  
  334.     
  335.     def update(self):
  336.         if self.entry_count <= 0:
  337.             self.timer.stop()
  338.             self.pd.hide()
  339.             self.process_duplicates()
  340.             return None
  341.         
  342.         try:
  343.             (id, opf, cover) = self.rq.get_nowait()
  344.             self.db_adder.input_queue.put((id, opf, cover))
  345.             self.last_added_at = time.time()
  346.         except Empty:
  347.             self.entry_count <= 0
  348.             self.entry_count <= 0
  349.         except:
  350.             self.entry_count <= 0
  351.  
  352.         
  353.         try:
  354.             title = self.db_adder.output_queue.get_nowait()
  355.             self.pd.value += 1
  356.             self.pd.set_msg(_('Added') + ' ' + title)
  357.             self.last_added_at = time.time()
  358.             self.entry_count -= 1
  359.         except Empty:
  360.             self.entry_count <= 0
  361.             self.entry_count <= 0
  362.         except:
  363.             self.entry_count <= 0
  364.  
  365.  
  366.     
  367.     def process_duplicates(self):
  368.         duplicates = self.db_adder.duplicates
  369.         if not duplicates:
  370.             return self.duplicates_processed()
  371.         self.pd.hide()
  372.         files = [ x[0].title for x in duplicates ]
  373.         if question_dialog(self._parent, _('Duplicates found!'), _('Books with the same title as the following already exist in the database. Add them anyway?'), '\n'.join(files)):
  374.             pd = QProgressDialog(_('Adding duplicates...'), '', 0, len(duplicates), self._parent)
  375.             pd.setCancelButton(None)
  376.             pd.setValue(0)
  377.             pd.show()
  378.             self._Adder__p_d = pd
  379.             self._Adder__d_a = DuplicatesAdder(self._parent, self.db, duplicates, self.db_adder)
  380.             self.connect(self._Adder__d_a, SIGNAL('added(PyQt_PyObject)'), pd.setValue)
  381.             self.connect(self._Adder__d_a, SIGNAL('adding_done()'), self.duplicates_processed)
  382.             self._Adder__d_a.start()
  383.         else:
  384.             return self.duplicates_processed()
  385.         return []
  386.  
  387.     
  388.     def cleanup(self):
  389.         if hasattr(self, 'pd'):
  390.             self.pd.hide()
  391.         
  392.         if hasattr(self, 'worker') and hasattr(self.worker, 'tdir') and self.worker.tdir is not None:
  393.             if os.path.exists(self.worker.tdir):
  394.                 
  395.                 try:
  396.                     shutil.rmtree(self.worker.tdir)
  397.  
  398.             
  399.         
  400.  
  401.     
  402.     def number_of_books_added(self):
  403.         return getattr(getattr(self, 'db_adder', None), 'number_of_books_added', 0)
  404.  
  405.     number_of_books_added = property(number_of_books_added)
  406.     
  407.     def merged_books(self):
  408.         return getattr(getattr(self, 'db_adder', None), 'merged_books', set([]))
  409.  
  410.     merged_books = property(merged_books)
  411.     
  412.     def critical(self):
  413.         return getattr(getattr(self, 'db_adder', None), 'critical', { })
  414.  
  415.     critical = property(critical)
  416.     
  417.     def paths(self):
  418.         return getattr(getattr(self, 'db_adder', None), 'paths', [])
  419.  
  420.     paths = property(paths)
  421.     
  422.     def names(self):
  423.         return getattr(getattr(self, 'db_adder', None), 'names', [])
  424.  
  425.     names = property(names)
  426.     
  427.     def infos(self):
  428.         return getattr(getattr(self, 'db_adder', None), 'infos', [])
  429.  
  430.     infos = property(infos)
  431.  
  432.  
  433. class Saver(QObject):
  434.     
  435.     def __init__(self, parent, db, callback, rows, path, opts, spare_server = None):
  436.         QObject.__init__(self, parent)
  437.         self.pd = ProgressDialog(_('Saving...'), parent = parent)
  438.         self.spare_server = spare_server
  439.         self.db = db
  440.         self.opts = opts
  441.         self.pd.setModal(True)
  442.         self.pd.show()
  443.         self.pd.set_min(0)
  444.         self._parent = parent
  445.         self.callback = callback
  446.         self.callback_called = False
  447.         self.rq = Queue()
  448.         self.ids = _[1]
  449.         self.pd.set_max(len(self.ids))
  450.         self.pd.value = 0
  451.         self.failures = set([])
  452.         SaveWorker = SaveWorker
  453.         import calibre.ebooks.metadata.worker
  454.         self.worker = SaveWorker(self.rq, db, self.ids, path, self.opts, spare_server = self.spare_server)
  455.         self.connect(self.pd, SIGNAL('canceled()'), self.canceled)
  456.         self.timer = QTimer(self)
  457.         self.connect(self.timer, SIGNAL('timeout()'), self.update)
  458.         self.timer.start(200)
  459.  
  460.     
  461.     def canceled(self):
  462.         if self.timer is not None:
  463.             self.timer.stop()
  464.         
  465.         if self.worker is not None:
  466.             self.worker.canceled = True
  467.         
  468.         self.pd.hide()
  469.         if not self.callback_called:
  470.             self.callback(self.worker.path, self.failures, self.worker.error)
  471.             self.callback_called = True
  472.         
  473.  
  474.     
  475.     def update(self):
  476.         if not (self.ids) or not self.worker.is_alive():
  477.             self.timer.stop()
  478.             self.pd.hide()
  479.             if not self.callback_called:
  480.                 self.callback(self.worker.path, self.failures, self.worker.error)
  481.                 self.callback_called = True
  482.             
  483.             return None
  484.         
  485.         try:
  486.             (id, title, ok, tb) = self.rq.get_nowait()
  487.         except Empty:
  488.             not self.worker.is_alive()
  489.             not self.worker.is_alive()
  490.             return None
  491.  
  492.         self.pd.value += 1
  493.         self.ids.remove(id)
  494.         self.pd.set_msg(_('Saved') + ' ' + title)
  495.         if not ok:
  496.             self.failures.add((title, tb))
  497.         
  498.  
  499.  
  500.