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

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.6)
  3.  
  4. from __future__ import with_statement
  5. __license__ = 'GPL v3'
  6. __copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
  7. __docformat__ = 'restructuredtext en'
  8. import os
  9. import sys
  10. import shutil
  11. import cStringIO
  12. import glob
  13. import time
  14. import functools
  15. import traceback
  16. from itertools import repeat
  17. from math import floor
  18. from PyQt4.QtGui import QImage
  19. from calibre.ebooks.metadata import title_sort, author_to_author_sort
  20. from calibre.library.database import LibraryDatabase
  21. from calibre.library.field_metadata import FieldMetadata, TagsIcons
  22. from calibre.library.schema_upgrades import SchemaUpgrade
  23. from calibre.library.caches import ResultCache
  24. from calibre.library.custom_columns import CustomColumns
  25. from calibre.library.sqlite import connect, IntegrityError, DBThread
  26. from calibre.library.prefs import DBPrefs
  27. from calibre.ebooks.metadata import string_to_authors, authors_to_string, MetaInformation
  28. from calibre.ebooks.metadata.meta import get_metadata, metadata_from_formats
  29. from calibre.constants import preferred_encoding, iswindows, isosx, filesystem_encoding
  30. from calibre.ptempfile import PersistentTemporaryFile
  31. from calibre.customize.ui import run_plugins_on_import
  32. from calibre.utils.filenames import ascii_filename
  33. from calibre.utils.date import utcnow, now as nowf, utcfromtimestamp
  34. from calibre.utils.config import prefs, tweaks
  35. from calibre.utils.search_query_parser import saved_searches, set_saved_searches
  36. from calibre.ebooks import BOOK_EXTENSIONS, check_ebook_format
  37. from calibre.utils.magick.draw import save_cover_data_to
  38. if iswindows:
  39.     import calibre.utils.winshell as winshell
  40.  
  41.  
  42. def delete_file(path):
  43.     
  44.     try:
  45.         winshell.delete_file(path, silent = True, no_confirm = True)
  46.     except:
  47.         os.remove(path)
  48.  
  49.  
  50.  
  51. def delete_tree(path, permanent = False):
  52.     if permanent:
  53.         shutil.rmtree(path)
  54.     else:
  55.         
  56.         try:
  57.             if not permanent:
  58.                 winshell.delete_file(path, silent = True, no_confirm = True)
  59.         except:
  60.             shutil.rmtree(path)
  61.  
  62.  
  63. copyfile = None if hasattr(os, 'link') else shutil.copyfile
  64.  
  65. class Tag(object):
  66.     
  67.     def __init__(self, name, id = None, count = 0, state = 0, avg = 0, sort = None, tooltip = None, icon = None):
  68.         self.name = name
  69.         self.id = id
  70.         self.count = count
  71.         self.state = state
  72.         self.avg_rating = None if avg is not None else 0
  73.         self.sort = sort
  74.         if self.avg_rating > 0:
  75.             if tooltip:
  76.                 tooltip = tooltip + ': '
  77.             
  78.             tooltip = _('%sAverage rating is %3.1f') % (tooltip, self.avg_rating)
  79.         
  80.         self.tooltip = tooltip
  81.         self.icon = icon
  82.  
  83.     
  84.     def __unicode__(self):
  85.         return u'%s:%s:%s:%s:%s' % (self.name, self.count, self.id, self.state, self.tooltip)
  86.  
  87.     
  88.     def __str__(self):
  89.         return unicode(self).encode('utf-8')
  90.  
  91.     
  92.     def __repr__(self):
  93.         return str(self)
  94.  
  95.  
  96.  
  97. class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
  98.     PATH_LIMIT = None if 'win32' in sys.platform else 100
  99.     
  100.     def user_version(self):
  101.         doc = 'The user version of this database'
  102.         
  103.         def fget(self):
  104.             return self.conn.get('pragma user_version;', all = False)
  105.  
  106.         
  107.         def fset(self, val):
  108.             self.conn.execute('pragma user_version=%d' % int(val))
  109.             self.conn.commit()
  110.  
  111.         return property(doc = doc, fget = fget, fset = fset)
  112.  
  113.     user_version = dynamic_property(user_version)
  114.     
  115.     def connect(self):
  116.         if 'win32' in sys.platform and len(self.library_path) + 4 * self.PATH_LIMIT + 10 > 259:
  117.             raise ValueError('Path to library too long. Must be less than %d characters.' % (259 - 4 * self.PATH_LIMIT - 10))
  118.         len(self.library_path) + 4 * self.PATH_LIMIT + 10 > 259
  119.         exists = os.path.exists(self.dbpath)
  120.         self.conn = connect(self.dbpath, self.row_factory)
  121.         if exists and self.user_version == 0:
  122.             self.conn.close()
  123.             os.remove(self.dbpath)
  124.             self.conn = connect(self.dbpath, self.row_factory)
  125.         
  126.         if self.user_version == 0:
  127.             self.initialize_database()
  128.         
  129.         self.books_list_filter = self.conn.create_dynamic_filter('books_list_filter')
  130.  
  131.     
  132.     def exists_at(cls, path):
  133.         if path:
  134.             pass
  135.         return os.path.exists(os.path.join(path, 'metadata.db'))
  136.  
  137.     exists_at = classmethod(exists_at)
  138.     
  139.     def __init__(self, library_path, row_factory = False):
  140.         self.field_metadata = FieldMetadata()
  141.         if not os.path.exists(library_path):
  142.             os.makedirs(library_path)
  143.         
  144.         self.listeners = set([])
  145.         self.library_path = os.path.abspath(library_path)
  146.         self.row_factory = row_factory
  147.         self.dbpath = os.path.join(library_path, 'metadata.db')
  148.         self.dbpath = os.environ.get('CALIBRE_OVERRIDE_DATABASE_PATH', self.dbpath)
  149.         if isinstance(self.dbpath, unicode) and not iswindows:
  150.             self.dbpath = self.dbpath.encode(filesystem_encoding)
  151.         
  152.         self.connect()
  153.         if not iswindows and not isosx:
  154.             pass
  155.         self.is_case_sensitive = not os.path.exists(self.dbpath.replace('metadata.db', 'MeTAdAtA.dB'))
  156.         SchemaUpgrade.__init__(self)
  157.         self.initialize_dynamic()
  158.  
  159.     
  160.     def initialize_dynamic(self):
  161.         self.prefs = DBPrefs(self)
  162.         
  163.         def migrate_preference(key, default):
  164.             oldval = prefs[key]
  165.             if oldval != default:
  166.                 self.prefs[key] = oldval
  167.                 prefs[key] = default
  168.             
  169.             if key not in self.prefs:
  170.                 self.prefs[key] = default
  171.             
  172.  
  173.         migrate_preference('user_categories', { })
  174.         migrate_preference('saved_searches', { })
  175.         set_saved_searches(self, 'saved_searches')
  176.         self.conn.executescript('\n        DROP TRIGGER IF EXISTS author_insert_trg;\n        CREATE TEMP TRIGGER author_insert_trg\n            AFTER INSERT ON authors\n            BEGIN\n            UPDATE authors SET sort=author_to_author_sort(NEW.name) WHERE id=NEW.id;\n        END;\n        DROP TRIGGER IF EXISTS author_update_trg;\n        CREATE TEMP TRIGGER author_update_trg\n            BEFORE UPDATE ON authors\n            BEGIN\n            UPDATE authors SET sort=author_to_author_sort(NEW.name)\n            WHERE id=NEW.id AND name <> NEW.name;\n        END;\n        ')
  177.         self.conn.execute('UPDATE authors SET sort=author_to_author_sort(name) WHERE sort IS NULL')
  178.         self.conn.executescript(u'\n            CREATE TEMP VIEW IF NOT EXISTS tag_browser_news AS SELECT DISTINCT\n                id,\n                name,\n                (SELECT COUNT(books_tags_link.id) FROM books_tags_link WHERE tag=x.id) count,\n                (0) as avg_rating,\n                name as sort\n            FROM tags as x WHERE name!="{0}" AND id IN\n                (SELECT DISTINCT tag FROM books_tags_link WHERE book IN\n                    (SELECT DISTINCT book FROM books_tags_link WHERE tag IN\n                        (SELECT id FROM tags WHERE name="{0}")));\n            '.format(_('News')))
  179.         self.conn.executescript(u'\n            CREATE TEMP VIEW IF NOT EXISTS tag_browser_filtered_news AS SELECT DISTINCT\n                id,\n                name,\n                (SELECT COUNT(books_tags_link.id) FROM books_tags_link WHERE tag=x.id and books_list_filter(book)) count,\n                (0) as avg_rating,\n                name as sort\n            FROM tags as x WHERE name!="{0}" AND id IN\n                (SELECT DISTINCT tag FROM books_tags_link WHERE book IN\n                    (SELECT DISTINCT book FROM books_tags_link WHERE tag IN\n                        (SELECT id FROM tags WHERE name="{0}")));\n            '.format(_('News')))
  180.         self.conn.commit()
  181.         CustomColumns.__init__(self)
  182.         template = '                (SELECT {query} FROM books_{table}_link AS link INNER JOIN\n                    {table} ON(link.{link_col}={table}.id) WHERE link.book=books.id)\n                    {col}\n                '
  183.         columns = [
  184.             'id',
  185.             'title',
  186.             ('authors', 'authors', 'author', 'sortconcat(link.id, name)'),
  187.             'timestamp',
  188.             '(SELECT MAX(uncompressed_size) FROM data WHERE book=books.id) size',
  189.             ('rating', 'ratings', 'rating', 'ratings.rating'),
  190.             ('tags', 'tags', 'tag', 'group_concat(name)'),
  191.             '(SELECT text FROM comments WHERE book=books.id) comments',
  192.             ('series', 'series', 'series', 'name'),
  193.             ('publisher', 'publishers', 'publisher', 'name'),
  194.             'series_index',
  195.             'sort',
  196.             'author_sort',
  197.             '(SELECT group_concat(format) FROM data WHERE data.book=books.id) formats',
  198.             'isbn',
  199.             'path',
  200.             'lccn',
  201.             'pubdate',
  202.             'flags',
  203.             'uuid']
  204.         lines = []
  205.         for col in columns:
  206.             line = col
  207.             if isinstance(col, tuple):
  208.                 line = template.format(col = col[0], table = col[1], link_col = col[2], query = col[3])
  209.             
  210.             lines.append(line)
  211.         
  212.         custom_map = self.custom_columns_in_meta()
  213.         custom_cols = list(sorted(custom_map.keys()))
  214.         []([ custom_map[x] for x in custom_cols ])
  215.         self.FIELD_MAP = {
  216.             'id': 0,
  217.             'title': 1,
  218.             'authors': 2,
  219.             'timestamp': 3,
  220.             'size': 4,
  221.             'rating': 5,
  222.             'tags': 6,
  223.             'comments': 7,
  224.             'series': 8,
  225.             'publisher': 9,
  226.             'series_index': 10,
  227.             'sort': 11,
  228.             'author_sort': 12,
  229.             'formats': 13,
  230.             'isbn': 14,
  231.             'path': 15,
  232.             'lccn': 16,
  233.             'pubdate': 17,
  234.             'flags': 18,
  235.             'uuid': 19 }
  236.         for k, v in self.FIELD_MAP.iteritems():
  237.             self.field_metadata.set_field_record_index(k, v, prefer_custom = False)
  238.         
  239.         base = max(self.FIELD_MAP.values())
  240.         for col in custom_cols:
  241.             self.field_metadata.set_field_record_index(self.custom_column_num_map[col]['label'], base, prefer_custom = True)
  242.             if self.custom_column_num_map[col]['datatype'] == 'series':
  243.                 continue
  244.             self.FIELD_MAP[str(col) + '_s_index'] = base = base + 1
  245.         
  246.         self.FIELD_MAP['cover'] = base + 1
  247.         self.field_metadata.set_field_record_index('cover', base + 1, prefer_custom = False)
  248.         self.FIELD_MAP['ondevice'] = base + 2
  249.         self.field_metadata.set_field_record_index('ondevice', base + 2, prefer_custom = False)
  250.         script = '\n        DROP VIEW IF EXISTS meta2;\n        CREATE TEMP VIEW meta2 AS\n        SELECT\n        {0}\n        FROM books;\n        '.format(', \n'.join(lines))
  251.         self.conn.executescript(script)
  252.         self.conn.commit()
  253.         tb_cats = self.field_metadata
  254.         for k in tb_cats.keys():
  255.             if tb_cats[k]['kind'] in ('user', 'search'):
  256.                 del tb_cats[k]
  257.                 continue
  258.             self.FIELD_MAP[col] = base = base + 1
  259.         
  260.         for user_cat in sorted(self.prefs.get('user_categories', { }).keys()):
  261.             cat_name = user_cat + ':'
  262.             tb_cats.add_user_category(label = cat_name, name = user_cat)
  263.         
  264.         self.book_on_device_func = None
  265.         self.data = ResultCache(self.FIELD_MAP, self.field_metadata)
  266.         self.search = self.data.search
  267.         self.refresh = functools.partial(self.data.refresh, self)
  268.         self.sort = self.data.sort
  269.         self.index = self.data.index
  270.         self.refresh_ids = functools.partial(self.data.refresh_ids, self)
  271.         self.row = self.data.row
  272.         self.has_id = self.data.has_id
  273.         self.count = self.data.count
  274.         self.refresh_ondevice = functools.partial(self.data.refresh_ondevice, self)
  275.         self.refresh()
  276.         self.last_update_check = self.last_modified()
  277.         
  278.         def get_property(idx, index_is_id = False, loc = (-1,)):
  279.             row = None if index_is_id else self.data[idx]
  280.             if row is not None:
  281.                 return row[loc]
  282.  
  283.         for prop in ('author_sort', 'authors', 'comment', 'comments', 'isbn', 'publisher', 'rating', 'series', 'series_index', 'tags', 'title', 'timestamp', 'uuid', 'pubdate'):
  284.             None if len(saved_searches().names()) else lines.extend(setattr, self, prop(functools.partial, get_property = 'loc'[self.FIELD_MAP if prop == 'comment' else prop]))
  285.         
  286.  
  287.     
  288.     def initialize_database(self):
  289.         metadata_sqlite = open(P('metadata_sqlite.sql'), 'rb').read()
  290.         self.conn.executescript(metadata_sqlite)
  291.         self.user_version = 1
  292.  
  293.     
  294.     def last_modified(self):
  295.         return utcfromtimestamp(os.stat(self.dbpath).st_mtime)
  296.  
  297.     
  298.     def check_if_modified(self):
  299.         if self.last_modified() > self.last_update_check:
  300.             self.refresh()
  301.         
  302.         self.last_update_check = utcnow()
  303.  
  304.     
  305.     def path(self, index, index_is_id = False):
  306.         row = None if index_is_id else self.data[index]
  307.         return row[self.FIELD_MAP['path']].replace('/', os.sep)
  308.  
  309.     
  310.     def abspath(self, index, index_is_id = False):
  311.         path = os.path.join(self.library_path, self.path(index, index_is_id = index_is_id))
  312.         if not os.path.exists(path):
  313.             os.makedirs(path)
  314.         
  315.         return path
  316.  
  317.     
  318.     def construct_path_name(self, id):
  319.         authors = self.authors(id, index_is_id = True)
  320.         if not authors:
  321.             authors = _('Unknown')
  322.         
  323.         author = ascii_filename(authors.split(',')[0][:self.PATH_LIMIT]).decode(filesystem_encoding, 'ignore')
  324.         title = ascii_filename(self.title(id, index_is_id = True)[:self.PATH_LIMIT]).decode(filesystem_encoding, 'ignore')
  325.         path = author + '/' + title + ' (%d)' % id
  326.         return path
  327.  
  328.     
  329.     def construct_file_name(self, id):
  330.         authors = self.authors(id, index_is_id = True)
  331.         if not authors:
  332.             authors = _('Unknown')
  333.         
  334.         author = ascii_filename(authors.split(',')[0][:self.PATH_LIMIT]).decode(filesystem_encoding, 'replace')
  335.         title = ascii_filename(self.title(id, index_is_id = True)[:self.PATH_LIMIT]).decode(filesystem_encoding, 'replace')
  336.         name = title + ' - ' + author
  337.         while name.endswith('.'):
  338.             name = name[:-1]
  339.         return name
  340.  
  341.     
  342.     def rmtree(self, path, permanent = False):
  343.         if not self.normpath(self.library_path).startswith(self.normpath(path)):
  344.             delete_tree(path, permanent = permanent)
  345.         
  346.  
  347.     
  348.     def normpath(self, path):
  349.         path = os.path.abspath(os.path.realpath(path))
  350.         if not self.is_case_sensitive:
  351.             path = path.lower()
  352.         
  353.         return path
  354.  
  355.     
  356.     def set_path(self, index, index_is_id = False):
  357.         id = None if index_is_id else self.id(index)
  358.         path = self.construct_path_name(id)
  359.         current_path = self.path(id, index_is_id = True).replace(os.sep, '/')
  360.         formats = self.formats(id, index_is_id = True)
  361.         formats = None if formats else []
  362.         fname = self.construct_file_name(id)
  363.         changed = False
  364.         for format in formats:
  365.             name = self.conn.get('SELECT name FROM data WHERE book=? AND format=?', (id, format), all = False)
  366.             if name and name != fname:
  367.                 changed = True
  368.                 break
  369.                 continue
  370.         
  371.         if path == current_path and not changed:
  372.             return None
  373.         tpath = os.path.join(self.library_path, *path.split('/'))
  374.         if not os.path.exists(tpath):
  375.             os.makedirs(tpath)
  376.         
  377.         spath = os.path.join(self.library_path, *current_path.split('/'))
  378.         if current_path and os.path.exists(spath):
  379.             cdata = self.cover(id, index_is_id = True)
  380.             if cdata is not None:
  381.                 open(os.path.join(tpath, 'cover.jpg'), 'wb').write(cdata)
  382.             
  383.             for format in formats:
  384.                 f = self.format(id, format, index_is_id = True, as_file = False)
  385.                 if not f:
  386.                     continue
  387.                 
  388.                 stream = cStringIO.StringIO(f)
  389.                 self.add_format(id, format, stream, index_is_id = True, path = tpath)
  390.             
  391.         
  392.         self.conn.execute('UPDATE books SET path=? WHERE id=?', (path, id))
  393.         self.conn.commit()
  394.         self.data.set(id, self.FIELD_MAP['path'], path, row_is_id = True)
  395.         if current_path and os.path.exists(spath):
  396.             if self.normpath(spath) != self.normpath(tpath):
  397.                 self.rmtree(spath, permanent = True)
  398.                 parent = os.path.dirname(spath)
  399.                 if len(os.listdir(parent)) == 0:
  400.                     self.rmtree(parent, permanent = True)
  401.                 
  402.             
  403.         
  404.  
  405.     
  406.     def add_listener(self, listener):
  407.         self.listeners.add(listener)
  408.  
  409.     
  410.     def notify(self, event, ids = []):
  411.         for listener in self.listeners:
  412.             
  413.             try:
  414.                 listener(event, ids)
  415.             continue
  416.             traceback.print_exc()
  417.             continue
  418.             continue
  419.  
  420.         
  421.  
  422.     
  423.     def cover(self, index, index_is_id = False, as_file = False, as_image = False, as_path = False):
  424.         id = None if index_is_id else self.id(index)
  425.         path = os.path.join(self.library_path, self.path(id, index_is_id = True), 'cover.jpg')
  426.         if os.access(path, os.R_OK):
  427.             if as_path:
  428.                 return path
  429.             
  430.             try:
  431.                 f = open(path, 'rb')
  432.             except (IOError, OSError):
  433.                 as_path
  434.                 as_path
  435.                 time.sleep(0.2)
  436.                 f = open(path, 'rb')
  437.             except:
  438.                 as_path
  439.  
  440.             if as_image:
  441.                 img = QImage()
  442.                 img.loadFromData(f.read())
  443.                 f.close()
  444.                 return img
  445.             ans = as_image if as_file else f.read()
  446.             if ans is not f:
  447.                 f.close()
  448.             
  449.             return ans
  450.  
  451.     
  452.     def get_metadata(self, idx, index_is_id = False, get_cover = False):
  453.         aum = self.authors(idx, index_is_id = index_is_id)
  454.         mi = MetaInformation(self.title(idx, index_is_id = index_is_id), aum)
  455.         mi.author_sort = self.author_sort(idx, index_is_id = index_is_id)
  456.         if mi.authors:
  457.             mi.author_sort_map = { }
  458.             for name, sort in zip(mi.authors, self.authors_sort_strings(idx, index_is_id)):
  459.                 mi.author_sort_map[name] = sort
  460.             
  461.         
  462.         mi.comments = self.comments(idx, index_is_id = index_is_id)
  463.         mi.publisher = self.publisher(idx, index_is_id = index_is_id)
  464.         mi.timestamp = self.timestamp(idx, index_is_id = index_is_id)
  465.         mi.pubdate = self.pubdate(idx, index_is_id = index_is_id)
  466.         mi.uuid = self.uuid(idx, index_is_id = index_is_id)
  467.         tags = self.tags(idx, index_is_id = index_is_id)
  468.         mi.series = self.series(idx, index_is_id = index_is_id)
  469.         if mi.series:
  470.             mi.series_index = self.series_index(idx, index_is_id = index_is_id)
  471.         
  472.         mi.rating = self.rating(idx, index_is_id = index_is_id)
  473.         mi.isbn = self.isbn(idx, index_is_id = index_is_id)
  474.         id = None if index_is_id else self.id(idx)
  475.         mi.application_id = id
  476.         if get_cover:
  477.             mi.cover = self.cover(id, index_is_id = True, as_path = True)
  478.         
  479.         return mi
  480.  
  481.     
  482.     def has_book(self, mi):
  483.         title = mi.title
  484.         if title:
  485.             if not isinstance(title, unicode):
  486.                 title = title.decode(preferred_encoding, 'replace')
  487.             
  488.             return bool(self.conn.get('SELECT id FROM books where title=?', (title,), all = False))
  489.         return False
  490.  
  491.     
  492.     def has_cover(self, index, index_is_id = False):
  493.         id = None if index_is_id else self.id(index)
  494.         path = os.path.join(self.library_path, self.path(id, index_is_id = True), 'cover.jpg')
  495.         return os.access(path, os.R_OK)
  496.  
  497.     
  498.     def remove_cover(self, id, notify = True):
  499.         path = os.path.join(self.library_path, self.path(id, index_is_id = True), 'cover.jpg')
  500.         if os.path.exists(path):
  501.             
  502.             try:
  503.                 os.remove(path)
  504.             except (IOError, OSError):
  505.                 time.sleep(0.2)
  506.                 os.remove(path)
  507.             except:
  508.                 None<EXCEPTION MATCH>(IOError, OSError)
  509.             
  510.  
  511.         None<EXCEPTION MATCH>(IOError, OSError)
  512.         if notify:
  513.             self.notify('cover', [
  514.                 id])
  515.         
  516.  
  517.     
  518.     def set_cover(self, id, data, notify = True):
  519.         path = os.path.join(self.library_path, self.path(id, index_is_id = True), 'cover.jpg')
  520.         if callable(getattr(data, 'save', None)):
  521.             data.save(path)
  522.         elif callable(getattr(data, 'read', None)):
  523.             data = data.read()
  524.         
  525.         
  526.         try:
  527.             save_cover_data_to(data, path)
  528.         except (IOError, OSError):
  529.             time.sleep(0.2)
  530.             save_cover_data_to(data, path)
  531.  
  532.         if notify:
  533.             self.notify('cover', [
  534.                 id])
  535.         
  536.  
  537.     
  538.     def book_on_device(self, id):
  539.         if callable(self.book_on_device_func):
  540.             return self.book_on_device_func(id)
  541.  
  542.     
  543.     def book_on_device_string(self, id):
  544.         loc = []
  545.         on = self.book_on_device(id)
  546.         if on is not None:
  547.             (m, a, b) = on
  548.             if m is not None:
  549.                 loc.append(_('Main'))
  550.             
  551.             if a is not None:
  552.                 loc.append(_('Card A'))
  553.             
  554.             if b is not None:
  555.                 loc.append(_('Card B'))
  556.             
  557.         
  558.         return ', '.join(loc)
  559.  
  560.     
  561.     def set_book_on_device_func(self, func):
  562.         self.book_on_device_func = func
  563.  
  564.     
  565.     def all_formats(self):
  566.         formats = self.conn.get('SELECT DISTINCT format from data')
  567.         if not formats:
  568.             return set([])
  569.         return []([ f[0] for f in formats ])
  570.  
  571.     
  572.     def formats(self, index, index_is_id = False):
  573.         id = None if index_is_id else self.id(index)
  574.         
  575.         try:
  576.             formats = self.conn.get('SELECT format FROM data WHERE book=?', (id,))
  577.             formats = map((lambda x: x[0]), formats)
  578.         except:
  579.             return None
  580.  
  581.         ans = []
  582.         for format in formats:
  583.             if self.format_abspath(id, format, index_is_id = True) is not None:
  584.                 ans.append(format)
  585.                 continue
  586.         
  587.         if not ans:
  588.             return None
  589.         return ','.join(ans)
  590.  
  591.     
  592.     def has_format(self, index, format, index_is_id = False):
  593.         return self.format_abspath(index, format, index_is_id) is not None
  594.  
  595.     
  596.     def format_last_modified(self, id_, fmt):
  597.         path = self.format_abspath(id_, fmt, index_is_id = True)
  598.         if path is not None:
  599.             return utcfromtimestamp(os.stat(path).st_mtime)
  600.  
  601.     
  602.     def format_abspath(self, index, format, index_is_id = False):
  603.         id = None if index_is_id else self.id(index)
  604.         
  605.         try:
  606.             name = self.conn.get('SELECT name FROM data WHERE book=? AND format=?', (id, format), all = False)
  607.         except:
  608.             return None
  609.  
  610.  
  611.     
  612.     def format(self, index, format, index_is_id = False, as_file = False, mode = 'r+b'):
  613.         path = self.format_abspath(index, format, index_is_id = index_is_id)
  614.         if path is not None:
  615.             f = open(path, mode)
  616.             ret = None if as_file else f.read()
  617.             if not as_file:
  618.                 f.close()
  619.             
  620.             return ret
  621.  
  622.     
  623.     def add_format_with_hooks(self, index, format, fpath, index_is_id = False, path = None, notify = True):
  624.         npath = self.run_import_plugins(fpath, format)
  625.         format = os.path.splitext(npath)[-1].lower().replace('.', '').upper()
  626.         stream = open(npath, 'rb')
  627.         format = check_ebook_format(stream, format)
  628.         return self.add_format(index, format, stream, index_is_id = index_is_id, path = path, notify = notify)
  629.  
  630.     
  631.     def add_format(self, index, format, stream, index_is_id = False, path = None, notify = True, replace = True):
  632.         id = None if index_is_id else self.id(index)
  633.         if path is None:
  634.             path = os.path.join(self.library_path, self.path(id, index_is_id = True))
  635.         
  636.         name = self.conn.get('SELECT name FROM data WHERE book=? AND format=?', (id, format), all = False)
  637.         if name:
  638.             if not replace:
  639.                 return False
  640.             self.conn.execute('DELETE FROM data WHERE book=? AND format=?', (id, format))
  641.         
  642.         name = self.construct_file_name(id)
  643.         ext = None if format else ''
  644.         dest = os.path.join(path, name + ext)
  645.         pdir = os.path.dirname(dest)
  646.         if not os.path.exists(pdir):
  647.             os.makedirs(pdir)
  648.         
  649.         
  650.         try:
  651.             f = _[1]
  652.             shutil.copyfileobj(stream, f)
  653.         finally:
  654.             pass
  655.  
  656.         stream.seek(0, 2)
  657.         size = stream.tell()
  658.         self.conn.execute('INSERT INTO data (book,format,uncompressed_size,name) VALUES (?,?,?,?)', (id, format.upper(), size, name))
  659.         self.conn.commit()
  660.         self.refresh_ids([
  661.             id])
  662.         return True
  663.  
  664.     
  665.     def delete_book(self, id, notify = True):
  666.         
  667.         try:
  668.             path = os.path.join(self.library_path, self.path(id, index_is_id = True))
  669.         except:
  670.             path = None
  671.  
  672.         self.data.remove(id)
  673.         if path and os.path.exists(path):
  674.             
  675.             try:
  676.                 winshell.delete_file(path, no_confirm = True, silent = True)
  677.             except:
  678.                 self.rmtree(path)
  679.  
  680.             parent = os.path.dirname(path)
  681.             if len(os.listdir(parent)) == 0:
  682.                 self.rmtree(parent)
  683.             
  684.         
  685.         self.conn.execute('DELETE FROM books WHERE id=?', (id,))
  686.         self.conn.commit()
  687.         self.clean()
  688.         self.data.books_deleted([
  689.             id])
  690.         if notify:
  691.             self.notify('delete', [
  692.                 id])
  693.         
  694.  
  695.     
  696.     def remove_format(self, index, format, index_is_id = False, notify = True):
  697.         id = None if index_is_id else self.id(index)
  698.         name = self.conn.get('SELECT name FROM data WHERE book=? AND format=?', (id, format), all = False)
  699.         if name:
  700.             path = self.format_abspath(id, format, index_is_id = True)
  701.             
  702.             try:
  703.                 delete_file(path)
  704.             except:
  705.                 traceback.print_exc()
  706.  
  707.             self.conn.execute('DELETE FROM data WHERE book=? AND format=?', (id, format.upper()))
  708.             self.conn.commit()
  709.             self.refresh_ids([
  710.                 id])
  711.             if notify:
  712.                 self.notify('metadata', [
  713.                     id])
  714.             
  715.         
  716.  
  717.     
  718.     def clean(self):
  719.         
  720.         def doit(ltable, table, ltable_col):
  721.             st = 'DELETE FROM books_%s_link WHERE (SELECT COUNT(id) FROM books WHERE id=book) < 1;' % ltable
  722.             self.conn.execute(st)
  723.             st = 'DELETE FROM %(table)s WHERE (SELECT COUNT(id) FROM books_%(ltable)s_link WHERE %(ltable_col)s=%(table)s.id) < 1;' % dict(ltable = ltable, table = table, ltable_col = ltable_col)
  724.             self.conn.execute(st)
  725.  
  726.         for ltable, table, ltable_col in [
  727.             ('authors', 'authors', 'author'),
  728.             ('publishers', 'publishers', 'publisher'),
  729.             ('tags', 'tags', 'tag'),
  730.             ('series', 'series', 'series')]:
  731.             doit(ltable, table, ltable_col)
  732.         
  733.         for id_, tag in self.conn.get('SELECT id, name FROM tags', all = True):
  734.             if not tag.strip():
  735.                 self.conn.execute('DELETE FROM books_tags_link WHERE tag=?', (id_,))
  736.                 self.conn.execute('DELETE FROM tags WHERE id=?', (id_,))
  737.                 continue
  738.             (None,)
  739.         
  740.         self.clean_custom()
  741.         self.conn.commit()
  742.  
  743.     
  744.     def get_recipes(self):
  745.         return self.conn.get('SELECT id, script FROM feeds')
  746.  
  747.     
  748.     def get_recipe(self, id):
  749.         return self.conn.get('SELECT script FROM feeds WHERE id=?', (id,), all = False)
  750.  
  751.     
  752.     def get_books_for_category(self, category, id_):
  753.         ans = set([])
  754.         if category not in self.field_metadata:
  755.             return ans
  756.         field = self.field_metadata[category]
  757.         ans = self.conn.get('SELECT book FROM books_{tn}_link WHERE {col}=?'.format(tn = field['table'], col = field['link_column']), (id_,))
  758.         return set((lambda .0: for x in .0:
  759. x[0])(ans))
  760.  
  761.     CATEGORY_SORTS = ('name', 'popularity', 'rating')
  762.     
  763.     def get_categories(self, sort = 'name', ids = None, icon_map = None):
  764.         None(self.books_list_filter.change if not ids else ids)
  765.         categories = { }
  766.         if icon_map is not None and type(icon_map) != TagsIcons:
  767.             raise TypeError('icon_map passed to get_categories must be of type TagIcons')
  768.         type(icon_map) != TagsIcons
  769.         tb_cats = self.field_metadata
  770.         for category in tb_cats.keys():
  771.             cat = tb_cats[category]
  772.             if not cat['is_category'] or cat['kind'] in ('user', 'search'):
  773.                 continue
  774.             
  775.             tn = cat['table']
  776.             categories[category] = []
  777.             if tn is None:
  778.                 continue
  779.             
  780.             cn = cat['column']
  781.             if ids is None:
  782.                 query = 'SELECT id, {0}, count, avg_rating, sort\n                           FROM tag_browser_{1}'.format(cn, tn)
  783.             else:
  784.                 query = 'SELECT id, {0}, count, avg_rating, sort\n                           FROM tag_browser_filtered_{1}'.format(cn, tn)
  785.             if sort == 'popularity':
  786.                 query += ' ORDER BY count DESC, sort ASC'
  787.             elif sort == 'name':
  788.                 query += ' ORDER BY sort ASC'
  789.             else:
  790.                 query += ' ORDER BY avg_rating DESC, sort ASC'
  791.             data = self.conn.get(query)
  792.             (icon, tooltip) = (None, '')
  793.             label = tb_cats.key_to_label(category)
  794.             if icon_map:
  795.                 if not tb_cats.is_custom_field(category):
  796.                     if category in icon_map:
  797.                         icon = icon_map[label]
  798.                     
  799.                 else:
  800.                     icon = icon_map[':custom']
  801.                     icon_map[category] = icon
  802.                     tooltip = self.custom_column_label_map[label]['name']
  803.             
  804.             datatype = cat['datatype']
  805.             if datatype == 'rating':
  806.                 
  807.                 item_not_zero_func = lambda x: if x[1] > 0:
  808. passx[2] > 0
  809.                 
  810.                 formatter = lambda x: u'Γÿà' * int(x / 2)
  811.             elif category == 'authors':
  812.                 
  813.                 item_not_zero_func = lambda x: x[2] > 0
  814.                 
  815.                 formatter = lambda x: x.replace('|', ',')
  816.             else:
  817.                 
  818.                 item_not_zero_func = lambda x: x[2] > 0
  819.                 
  820.                 formatter = lambda x: unicode(x)
  821.             categories[category] = _[1]
  822.         
  823.         for r in categories['rating']:
  824.             for x in categories['rating']:
  825.                 if r.name == x.name and r.id != x.id:
  826.                     r.count = r.count + x.count
  827.                     categories['rating'].remove(x)
  828.                     break
  829.                     continue
  830.                 []
  831.             
  832.         
  833.         categories['formats'] = []
  834.         icon = None
  835.         if icon_map and 'formats' in icon_map:
  836.             icon = icon_map['formats']
  837.         
  838.         for fmt in self.conn.get('SELECT DISTINCT format FROM data'):
  839.             fmt = fmt[0]
  840.             if ids is not None:
  841.                 count = self.conn.get('SELECT COUNT(id)\n                                       FROM data\n                                       WHERE format="%s" AND\n                                       books_list_filter(book)' % fmt, all = False)
  842.             else:
  843.                 count = self.conn.get('SELECT COUNT(id)\n                                       FROM data\n                                       WHERE format="%s"' % fmt, all = False)
  844.             if count > 0:
  845.                 categories['formats'].append(Tag(fmt, count = count, icon = icon))
  846.                 continue
  847.         
  848.         if sort == 'popularity':
  849.             categories['formats'].sort(key = (lambda x: x.count), reverse = True)
  850.         else:
  851.             categories['formats'].sort(key = (lambda x: x.name))
  852.         user_categories = self.prefs['user_categories']
  853.         taglist = { }
  854.         for c in categories.keys():
  855.             taglist[c] = dict(map((lambda t: (t.name, t)), categories[c]))
  856.         
  857.         for user_cat in sorted(user_categories.keys()):
  858.             items = []
  859.             for name, label, ign in user_categories[user_cat]:
  860.                 if label in taglist and name in taglist[label]:
  861.                     items.append(taglist[label][name])
  862.                     continue
  863.             
  864.             if len(items):
  865.                 cat_name = user_cat + ':'
  866.                 if icon_map is not None:
  867.                     icon_map[cat_name] = icon_map[':user']
  868.                 
  869.                 if sort == 'popularity':
  870.                     categories[cat_name] = sorted(items, key = (lambda x: x.count), reverse = True)
  871.                 elif sort == 'name':
  872.                     categories[cat_name] = sorted(items, key = (lambda x: x.sort.lower()))
  873.                 else:
  874.                     categories[cat_name] = sorted(items, key = (lambda x: x.avg_rating), reverse = True)
  875.             sort == 'popularity'
  876.         
  877.         items = []
  878.         icon = None
  879.         if icon_map and 'search' in icon_map:
  880.             icon = icon_map['search']
  881.         
  882.         for srch in saved_searches().names():
  883.             items.append(Tag(srch, tooltip = saved_searches().lookup(srch), icon = icon))
  884.         
  885.         if len(items):
  886.             if icon_map is not None:
  887.                 icon_map['search'] = icon_map['search']
  888.             
  889.             categories['search'] = items
  890.         
  891.         return categories
  892.  
  893.     
  894.     def tags_older_than(self, tag, delta):
  895.         tag = tag.lower().strip()
  896.         now = nowf()
  897.         for r in self.data._data:
  898.             if r is not None:
  899.                 if now - r[self.FIELD_MAP['timestamp']] > delta:
  900.                     tags = r[self.FIELD_MAP['tags']]
  901.                     if tags and tag in tags.lower():
  902.                         yield r[self.FIELD_MAP['id']]
  903.                     
  904.                 
  905.             now - r[self.FIELD_MAP['timestamp']] > delta
  906.         
  907.  
  908.     
  909.     def get_next_series_num_for(self, series):
  910.         series_id = self.conn.get('SELECT id from series WHERE name=?', (series,), all = False)
  911.         if series_id is None:
  912.             return 1
  913.         series_num = self.conn.get('SELECT MAX(series_index) FROM books WHERE id IN (SELECT book FROM books_series_link where series=?)', (series_id,), all = False)
  914.         if series_num is None:
  915.             return 1
  916.         return floor(series_num + 1)
  917.  
  918.     
  919.     def set(self, row, column, val):
  920.         id = self.data[row][0]
  921.         col = {
  922.             'title': 1,
  923.             'authors': 2,
  924.             'publisher': 3,
  925.             'rating': 4,
  926.             'tags': 7 }[column]
  927.         self.data.set(row, col, val)
  928.         if column == 'authors':
  929.             val = string_to_authors(val)
  930.             self.set_authors(id, val, notify = False)
  931.         elif column == 'title':
  932.             self.set_title(id, val, notify = False)
  933.         elif column == 'publisher':
  934.             self.set_publisher(id, val, notify = False)
  935.         elif column == 'rating':
  936.             self.set_rating(id, val, notify = False)
  937.         elif column == 'tags':
  938.             []([], _[1], append = False, notify = False)
  939.         
  940.         self.data.refresh_ids(self, [
  941.             id])
  942.         self.set_path(id, True)
  943.         self.notify('metadata', [
  944.             id])
  945.  
  946.     
  947.     def set_metadata(self, id, mi, ignore_errors = False):
  948.         
  949.         def doit(func, *args, **kwargs):
  950.             
  951.             try:
  952.                 func(*args, **kwargs)
  953.             except:
  954.                 if ignore_errors:
  955.                     traceback.print_exc()
  956.                 else:
  957.                     raise 
  958.  
  959.  
  960.         if mi.title:
  961.             self.set_title(id, mi.title)
  962.         
  963.         if not mi.authors:
  964.             mi.authors = [
  965.                 _('Unknown')]
  966.         
  967.         authors = []
  968.         for a in mi.authors:
  969.             authors += string_to_authors(a)
  970.         
  971.         self.set_authors(id, authors, notify = False)
  972.         if mi.author_sort:
  973.             doit(self.set_author_sort, id, mi.author_sort, notify = False)
  974.         
  975.         if mi.publisher:
  976.             doit(self.set_publisher, id, mi.publisher, notify = False)
  977.         
  978.         if mi.rating:
  979.             doit(self.set_rating, id, mi.rating, notify = False)
  980.         
  981.         if mi.series:
  982.             doit(self.set_series, id, mi.series, notify = False)
  983.         
  984.         if mi.cover_data[1] is not None:
  985.             doit(self.set_cover, id, mi.cover_data[1])
  986.         elif mi.cover is not None and os.access(mi.cover, os.R_OK):
  987.             doit(self.set_cover, id, open(mi.cover, 'rb'))
  988.         
  989.         if mi.tags:
  990.             doit(self.set_tags, id, mi.tags, notify = False)
  991.         
  992.         if mi.comments:
  993.             doit(self.set_comment, id, mi.comments, notify = False)
  994.         
  995.         if mi.isbn and mi.isbn.strip():
  996.             doit(self.set_isbn, id, mi.isbn, notify = False)
  997.         
  998.         if mi.series_index:
  999.             doit(self.set_series_index, id, mi.series_index, notify = False)
  1000.         
  1001.         if mi.pubdate:
  1002.             doit(self.set_pubdate, id, mi.pubdate, notify = False)
  1003.         
  1004.         if getattr(mi, 'timestamp', None) is not None:
  1005.             doit(self.set_timestamp, id, mi.timestamp, notify = False)
  1006.         
  1007.         self.set_path(id, True)
  1008.         self.notify('metadata', [
  1009.             id])
  1010.  
  1011.     
  1012.     def authors_sort_strings(self, id, index_is_id = False):
  1013.         id = None if index_is_id else self.id(id)
  1014.         aut_strings = self.conn.get('\n                        SELECT sort\n                        FROM authors, books_authors_link as bl\n                        WHERE bl.book=? and authors.id=bl.author\n                        ORDER BY bl.id', (id,))
  1015.         result = []
  1016.         for sort, in aut_strings:
  1017.             result.append(sort)
  1018.         
  1019.         return result
  1020.  
  1021.     
  1022.     def author_sort_from_book(self, id, index_is_id = False):
  1023.         auts = self.authors_sort_strings(id, index_is_id)
  1024.         return ' & '.join(auts).replace('|', ',')
  1025.  
  1026.     
  1027.     def author_sort_from_authors(self, authors):
  1028.         result = []
  1029.         for aut in authors:
  1030.             r = self.conn.get('SELECT sort FROM authors WHERE name=?', (aut.replace(',', '|'),), all = False)
  1031.             if r is None:
  1032.                 result.append(author_to_author_sort(aut))
  1033.                 continue
  1034.             result.append(r)
  1035.         
  1036.         return ' & '.join(result).replace('|', ',')
  1037.  
  1038.     
  1039.     def set_authors(self, id, authors, notify = True):
  1040.         if not authors:
  1041.             authors = [
  1042.                 _('Unknown')]
  1043.         
  1044.         self.conn.execute('DELETE FROM books_authors_link WHERE book=?', (id,))
  1045.         self.conn.execute('DELETE FROM authors WHERE (SELECT COUNT(id) FROM books_authors_link WHERE author=authors.id) < 1')
  1046.         for a in authors:
  1047.             if not a:
  1048.                 continue
  1049.             
  1050.             a = a.strip().replace(',', '|')
  1051.             if not isinstance(a, unicode):
  1052.                 a = a.decode(preferred_encoding, 'replace')
  1053.             
  1054.             author = self.conn.get('SELECT id from authors WHERE name=?', (a,), all = False)
  1055.             if author:
  1056.                 aid = author
  1057.                 self.conn.execute('UPDATE authors SET name=? WHERE id=?', (a, aid))
  1058.             else:
  1059.                 aid = self.conn.execute('INSERT INTO authors(name) VALUES (?)', (a,)).lastrowid
  1060.             
  1061.             try:
  1062.                 self.conn.execute('INSERT INTO books_authors_link(book, author) VALUES (?,?)', (id, aid))
  1063.             continue
  1064.             except IntegrityError:
  1065.                 continue
  1066.             
  1067.  
  1068.         
  1069.         self.conn.commit()
  1070.         ss = self.author_sort_from_book(id, index_is_id = True)
  1071.         self.conn.execute('UPDATE books SET author_sort=? WHERE id=?', (ss, id))
  1072.         self.conn.commit()
  1073.         self.FIELD_MAP['authors'](','.join, [], []([ a.replace(',', '|') for a in authors ]), row_is_id = True)
  1074.         self.data.set(id, self.FIELD_MAP['author_sort'], ss, row_is_id = True)
  1075.         self.set_path(id, True)
  1076.  
  1077.     
  1078.     def set_title(self, id, title, notify = True):
  1079.         if not title:
  1080.             return None
  1081.         if not isinstance(title, unicode):
  1082.             title = title.decode(preferred_encoding, 'replace')
  1083.         
  1084.         self.conn.execute('UPDATE books SET title=? WHERE id=?', (title, id))
  1085.         self.data.set(id, self.FIELD_MAP['title'], title, row_is_id = True)
  1086.         if tweaks['title_series_sorting'] == 'library_order':
  1087.             self.data.set(id, self.FIELD_MAP['sort'], title_sort(title), row_is_id = True)
  1088.         else:
  1089.             self.data.set(id, self.FIELD_MAP['sort'], title, row_is_id = True)
  1090.         self.set_path(id, True)
  1091.         self.conn.commit()
  1092.         if notify:
  1093.             self.notify('metadata', [
  1094.                 id])
  1095.         
  1096.  
  1097.     
  1098.     def set_timestamp(self, id, dt, notify = True):
  1099.         if dt:
  1100.             self.conn.execute('UPDATE books SET timestamp=? WHERE id=?', (dt, id))
  1101.             self.data.set(id, self.FIELD_MAP['timestamp'], dt, row_is_id = True)
  1102.             self.conn.commit()
  1103.             if notify:
  1104.                 self.notify('metadata', [
  1105.                     id])
  1106.             
  1107.         
  1108.  
  1109.     
  1110.     def set_pubdate(self, id, dt, notify = True):
  1111.         if dt:
  1112.             self.conn.execute('UPDATE books SET pubdate=? WHERE id=?', (dt, id))
  1113.             self.data.set(id, self.FIELD_MAP['pubdate'], dt, row_is_id = True)
  1114.             self.conn.commit()
  1115.             if notify:
  1116.                 self.notify('metadata', [
  1117.                     id])
  1118.             
  1119.         
  1120.  
  1121.     
  1122.     def set_publisher(self, id, publisher, notify = True):
  1123.         self.conn.execute('DELETE FROM books_publishers_link WHERE book=?', (id,))
  1124.         self.conn.execute('DELETE FROM publishers WHERE (SELECT COUNT(id) FROM books_publishers_link WHERE publisher=publishers.id) < 1')
  1125.         if publisher:
  1126.             if not isinstance(publisher, unicode):
  1127.                 publisher = publisher.decode(preferred_encoding, 'replace')
  1128.             
  1129.             pub = self.conn.get('SELECT id from publishers WHERE name=?', (publisher,), all = False)
  1130.             if pub:
  1131.                 aid = pub
  1132.             else:
  1133.                 aid = self.conn.execute('INSERT INTO publishers(name) VALUES (?)', (publisher,)).lastrowid
  1134.             self.conn.execute('INSERT INTO books_publishers_link(book, publisher) VALUES (?,?)', (id, aid))
  1135.             self.conn.commit()
  1136.             self.data.set(id, self.FIELD_MAP['publisher'], publisher, row_is_id = True)
  1137.             if notify:
  1138.                 self.notify('metadata', [
  1139.                     id])
  1140.             
  1141.         
  1142.  
  1143.     
  1144.     def get_tags_with_ids(self):
  1145.         result = self.conn.get('SELECT id,name FROM tags')
  1146.         if not result:
  1147.             return []
  1148.         return result
  1149.  
  1150.     
  1151.     def rename_tag(self, old_id, new_name):
  1152.         new_name = new_name.strip()
  1153.         new_id = self.conn.get('SELECT id from tags\n                       WHERE name=?', (new_name,), all = False)
  1154.         if new_id is None or old_id == new_id:
  1155.             self.conn.execute('UPDATE tags SET name=?\n                                 WHERE id=?', (new_name, old_id))
  1156.         else:
  1157.             books = self.conn.get('SELECT book from books_tags_link\n                                     WHERE tag=?', (old_id,))
  1158.             for book_id, in books:
  1159.                 self.conn.execute('DELETE FROM books_tags_link\n                                     WHERE book=? and tag=?', (book_id, new_id))
  1160.             
  1161.             self.conn.execute('UPDATE books_tags_link SET tag=?\n                                 WHERE tag=?', (new_id, old_id))
  1162.             self.conn.execute('DELETE FROM tags WHERE id=?', (old_id,))
  1163.         self.conn.commit()
  1164.  
  1165.     
  1166.     def delete_tag_using_id(self, id):
  1167.         self.conn.execute('DELETE FROM books_tags_link WHERE tag=?', (id,))
  1168.         self.conn.execute('DELETE FROM tags WHERE id=?', (id,))
  1169.         self.conn.commit()
  1170.  
  1171.     
  1172.     def get_series_with_ids(self):
  1173.         result = self.conn.get('SELECT id,name FROM series')
  1174.         if not result:
  1175.             return []
  1176.         return result
  1177.  
  1178.     
  1179.     def rename_series(self, old_id, new_name):
  1180.         new_name = new_name.strip()
  1181.         new_id = self.conn.get('SELECT id from series\n                       WHERE name=?', (new_name,), all = False)
  1182.         if new_id is None or old_id == new_id:
  1183.             self.conn.execute('UPDATE series SET name=? WHERE id=?', (new_name, old_id))
  1184.         else:
  1185.             books = self.conn.get('SELECT books.id\n                                     FROM books, books_series_link as lt\n                                     WHERE books.id = lt.book AND lt.series=?\n                                     ORDER BY books.series_index', (old_id,))
  1186.             index = self.get_next_series_num_for(new_name)
  1187.             self.conn.execute('UPDATE books_series_link\n                                 SET series=?\n                                 WHERE series=?', (new_id, old_id))
  1188.             for book_id, in books:
  1189.                 self.conn.execute('UPDATE books\n                                     SET series_index=?\n                                     WHERE id=?', (index, book_id))
  1190.                 index = index + 1
  1191.             
  1192.         self.conn.commit()
  1193.  
  1194.     
  1195.     def delete_series_using_id(self, id):
  1196.         books = self.conn.get('SELECT book from books_series_link WHERE series=?', (id,))
  1197.         self.conn.execute('DELETE FROM books_series_link WHERE series=?', (id,))
  1198.         self.conn.execute('DELETE FROM series WHERE id=?', (id,))
  1199.         self.conn.commit()
  1200.         for book_id, in books:
  1201.             self.conn.execute('UPDATE books SET series_index=1.0 WHERE id=?', (book_id,))
  1202.         
  1203.  
  1204.     
  1205.     def get_publishers_with_ids(self):
  1206.         result = self.conn.get('SELECT id,name FROM publishers')
  1207.         if not result:
  1208.             return []
  1209.         return result
  1210.  
  1211.     
  1212.     def rename_publisher(self, old_id, new_name):
  1213.         new_name = new_name.strip()
  1214.         new_id = self.conn.get('SELECT id from publishers\n                       WHERE name=?', (new_name,), all = False)
  1215.         if new_id is None or old_id == new_id:
  1216.             self.conn.execute('UPDATE publishers SET name=? WHERE id=?', (new_name, old_id))
  1217.         else:
  1218.             self.conn.execute('UPDATE books_publishers_link\n                                 SET publisher=?\n                                 WHERE publisher=?', (new_id, old_id))
  1219.             self.conn.execute('DELETE FROM publishers WHERE id=?', (old_id,))
  1220.         self.conn.commit()
  1221.  
  1222.     
  1223.     def delete_publisher_using_id(self, old_id):
  1224.         self.conn.execute('DELETE FROM books_publishers_link\n                             WHERE publisher=?', (old_id,))
  1225.         self.conn.execute('DELETE FROM publishers WHERE id=?', (old_id,))
  1226.         self.conn.commit()
  1227.  
  1228.     
  1229.     def get_authors_with_ids(self):
  1230.         result = self.conn.get('SELECT id,name,sort FROM authors')
  1231.         if not result:
  1232.             return []
  1233.         return result
  1234.  
  1235.     
  1236.     def set_sort_field_for_author(self, old_id, new_sort):
  1237.         self.conn.execute('UPDATE authors SET sort=? WHERE id=?', (new_sort.strip(), old_id))
  1238.         self.conn.commit()
  1239.         bks = self.conn.get('SELECT book from books_authors_link WHERE author=?', (old_id,))
  1240.         for book_id, in bks:
  1241.             ss = self.author_sort_from_book(book_id, index_is_id = True)
  1242.             self.set_author_sort(book_id, ss)
  1243.         
  1244.  
  1245.     
  1246.     def rename_author(self, old_id, new_name):
  1247.         new_name = new_name.replace(',', '|').strip()
  1248.         bks = self.conn.get('SELECT book from books_authors_link WHERE author=?', (old_id,))
  1249.         books = []
  1250.         for book_id, in bks:
  1251.             books.append(book_id)
  1252.         
  1253.         new_id = self.conn.get('SELECT id from authors WHERE name=?', (new_name,), all = False)
  1254.         if new_id is None or old_id == new_id:
  1255.             self.conn.execute('UPDATE authors SET name=? WHERE id=?', (new_name, old_id))
  1256.         elif old_id == new_id:
  1257.             self.conn.execute('UPDATE authors SET name=? WHERE id=?', (new_name, old_id))
  1258.             self.conn.commit()
  1259.             return new_id
  1260.         for book_id in books:
  1261.             authors = self.conn.get('\n                    SELECT author from books_authors_link\n                    WHERE book=?\n                    ORDER BY id', (book_id,))
  1262.             for i, aut in enumerate(authors):
  1263.                 authors[i] = None if aut[0] != old_id else new_id
  1264.             
  1265.             self.conn.execute('DELETE FROM books_authors_link\n                                     WHERE book=?', (book_id,))
  1266.             for aid in authors:
  1267.                 
  1268.                 try:
  1269.                     self.conn.execute('\n                            INSERT INTO books_authors_link(book, author)\n                            VALUES (?,?)', (book_id, aid))
  1270.                 continue
  1271.                 except IntegrityError:
  1272.                     continue
  1273.                 
  1274.  
  1275.             
  1276.         
  1277.         bks = self.conn.get('SELECT book FROM books_authors_link WHERE author=?', (old_id,))
  1278.         self.conn.execute('DELETE FROM authors WHERE id=?', (old_id,))
  1279.         self.conn.commit()
  1280.         for book_id in books:
  1281.             self.data.refresh_ids(self, [
  1282.                 book_id])
  1283.             self.set_path(book_id, index_is_id = True)
  1284.             ss = self.author_sort_from_book(book_id, index_is_id = True)
  1285.             self.set_author_sort(book_id, ss)
  1286.         
  1287.         return new_id
  1288.  
  1289.     
  1290.     def get_tags(self, id):
  1291.         result = self.conn.get('SELECT name FROM tags WHERE id IN (SELECT tag FROM books_tags_link WHERE book=?)', (id,), all = True)
  1292.         if not result:
  1293.             return set([])
  1294.         return []([ r[0] for r in result ])
  1295.  
  1296.     
  1297.     def set_tags(self, id, tags, append = False, notify = True):
  1298.         if not append:
  1299.             self.conn.execute('DELETE FROM books_tags_link WHERE book=?', (id,))
  1300.             self.conn.execute('DELETE FROM tags WHERE (SELECT COUNT(id) FROM books_tags_link WHERE tag=tags.id) < 1')
  1301.         
  1302.         otags = self.get_tags(id)
  1303.         tags = _[1]
  1304.         tags = [ _[2] if not isinstance(x, unicode) else x for x in tags ]
  1305.         tags = [ u' '.join(x.split()) for x in tags ]
  1306.         for tag in set(tags) - otags:
  1307.             tag = tag.strip()
  1308.             existing_tags = self.all_tags()
  1309.             lt = [ t.lower() for t in existing_tags ]
  1310.             
  1311.             try:
  1312.                 idx = lt.index(tag.lower())
  1313.             except ValueError:
  1314.                 []
  1315.                 []
  1316.                 []
  1317.                 idx = -1
  1318.             except:
  1319.                 [] if not isinstance(tag, unicode) else []
  1320.  
  1321.             if idx > -1:
  1322.                 etag = existing_tags[idx]
  1323.                 tid = self.conn.get('SELECT id FROM tags WHERE name=?', (etag,), all = False)
  1324.                 if etag != tag:
  1325.                     self.conn.execute('UPDATE tags SET name=? WHERE id=?', (tag, tid))
  1326.                 
  1327.             else:
  1328.                 tid = self.conn.execute('INSERT INTO tags(name) VALUES(?)', (tag,)).lastrowid
  1329.             if not self.conn.get('SELECT book FROM books_tags_link WHERE book=? AND tag=?', (id, tid), all = False):
  1330.                 self.conn.execute('INSERT INTO books_tags_link(book, tag) VALUES (?,?)', (id, tid))
  1331.                 continue
  1332.         
  1333.         self.conn.commit()
  1334.         tags = ','.join(self.get_tags(id))
  1335.         self.data.set(id, self.FIELD_MAP['tags'], tags, row_is_id = True)
  1336.         if notify:
  1337.             self.notify('metadata', [
  1338.                 id])
  1339.         
  1340.  
  1341.     
  1342.     def unapply_tags(self, book_id, tags, notify = True):
  1343.         for tag in tags:
  1344.             id = self.conn.get('SELECT id FROM tags WHERE name=?', (tag,), all = False)
  1345.             if id:
  1346.                 self.conn.execute('DELETE FROM books_tags_link WHERE tag=? AND book=?', (id, book_id))
  1347.                 continue
  1348.         
  1349.         self.conn.commit()
  1350.         self.data.refresh_ids(self, [
  1351.             book_id])
  1352.         if notify:
  1353.             self.notify('metadata', [
  1354.                 id])
  1355.         
  1356.  
  1357.     
  1358.     def is_tag_used(self, tag):
  1359.         existing_tags = self.all_tags()
  1360.         lt = [ t.lower() for t in existing_tags ]
  1361.         
  1362.         try:
  1363.             lt.index(tag.lower())
  1364.             return True
  1365.         except ValueError:
  1366.             []
  1367.             []
  1368.             []
  1369.             return False
  1370.  
  1371.  
  1372.     
  1373.     def delete_tag(self, tag):
  1374.         existing_tags = self.all_tags()
  1375.         lt = [ t.lower() for t in existing_tags ]
  1376.         
  1377.         try:
  1378.             idx = lt.index(tag.lower())
  1379.         except ValueError:
  1380.             []
  1381.             []
  1382.             []
  1383.             idx = -1
  1384.         except:
  1385.             []
  1386.  
  1387.  
  1388.     
  1389.     def set_series(self, id, series, notify = True):
  1390.         self.conn.execute('DELETE FROM books_series_link WHERE book=?', (id,))
  1391.         self.conn.execute('DELETE FROM series WHERE (SELECT COUNT(id) FROM books_series_link WHERE series=series.id) < 1')
  1392.         if series:
  1393.             if not isinstance(series, unicode):
  1394.                 series = series.decode(preferred_encoding, 'replace')
  1395.             
  1396.             series = series.strip()
  1397.             series = u' '.join(series.split())
  1398.             s = self.conn.get('SELECT id from series WHERE name=?', (series,), all = False)
  1399.             if s:
  1400.                 aid = s
  1401.             else:
  1402.                 aid = self.conn.execute('INSERT INTO series(name) VALUES (?)', (series,)).lastrowid
  1403.             self.conn.execute('INSERT INTO books_series_link(book, series) VALUES (?,?)', (id, aid))
  1404.         
  1405.         self.conn.commit()
  1406.         self.data.set(id, self.FIELD_MAP['series'], series, row_is_id = True)
  1407.         if notify:
  1408.             self.notify('metadata', [
  1409.                 id])
  1410.         
  1411.  
  1412.     
  1413.     def set_series_index(self, id, idx, notify = True):
  1414.         if idx is None:
  1415.             idx = 1
  1416.         
  1417.         
  1418.         try:
  1419.             idx = float(idx)
  1420.         except:
  1421.             idx = 1
  1422.  
  1423.         self.conn.execute('UPDATE books SET series_index=? WHERE id=?', (idx, id))
  1424.         self.conn.commit()
  1425.         self.data.set(id, self.FIELD_MAP['series_index'], idx, row_is_id = True)
  1426.         if notify:
  1427.             self.notify('metadata', [
  1428.                 id])
  1429.         
  1430.  
  1431.     
  1432.     def set_rating(self, id, rating, notify = True):
  1433.         rating = int(rating)
  1434.         self.conn.execute('DELETE FROM books_ratings_link WHERE book=?', (id,))
  1435.         rat = self.conn.get('SELECT id FROM ratings WHERE rating=?', (rating,), all = False)
  1436.         rat = None if rat else self.conn.execute('INSERT INTO ratings(rating) VALUES (?)', (rating,)).lastrowid
  1437.         self.conn.execute('INSERT INTO books_ratings_link(book, rating) VALUES (?,?)', (id, rat))
  1438.         self.conn.commit()
  1439.         self.data.set(id, self.FIELD_MAP['rating'], rating, row_is_id = True)
  1440.         if notify:
  1441.             self.notify('metadata', [
  1442.                 id])
  1443.         
  1444.  
  1445.     
  1446.     def set_comment(self, id, text, notify = True):
  1447.         self.conn.execute('DELETE FROM comments WHERE book=?', (id,))
  1448.         self.conn.execute('INSERT INTO comments(book,text) VALUES (?,?)', (id, text))
  1449.         self.conn.commit()
  1450.         self.data.set(id, self.FIELD_MAP['comments'], text, row_is_id = True)
  1451.         if notify:
  1452.             self.notify('metadata', [
  1453.                 id])
  1454.         
  1455.  
  1456.     
  1457.     def set_author_sort(self, id, sort, notify = True):
  1458.         self.conn.execute('UPDATE books SET author_sort=? WHERE id=?', (sort, id))
  1459.         self.conn.commit()
  1460.         self.data.set(id, self.FIELD_MAP['author_sort'], sort, row_is_id = True)
  1461.         if notify:
  1462.             self.notify('metadata', [
  1463.                 id])
  1464.         
  1465.  
  1466.     
  1467.     def set_isbn(self, id, isbn, notify = True):
  1468.         self.conn.execute('UPDATE books SET isbn=? WHERE id=?', (isbn, id))
  1469.         self.conn.commit()
  1470.         self.data.set(id, self.FIELD_MAP['isbn'], isbn, row_is_id = True)
  1471.         if notify:
  1472.             self.notify('metadata', [
  1473.                 id])
  1474.         
  1475.  
  1476.     
  1477.     def add_catalog(self, path, title):
  1478.         format = os.path.splitext(path)[1][1:].lower()
  1479.         
  1480.         try:
  1481.             stream = _[1]
  1482.             matches = self.data.get_matches('title', '=' + title)
  1483.             db_id = None
  1484.             if matches:
  1485.                 db_id = list(matches)[0]
  1486.             
  1487.             if db_id is None:
  1488.                 obj = self.conn.execute('INSERT INTO books(title, author_sort) VALUES (?, ?)', (title, 'calibre'))
  1489.                 db_id = obj.lastrowid
  1490.                 self.data.books_added([
  1491.                     db_id], self)
  1492.                 self.set_path(db_id, index_is_id = True)
  1493.                 self.conn.commit()
  1494.             
  1495.             
  1496.             try:
  1497.                 mi = get_metadata(stream, format)
  1498.             except:
  1499.                 mi = MetaInformation(title, [
  1500.                     'calibre'])
  1501.  
  1502.             stream.seek(0)
  1503.             mi.title = title
  1504.             mi.authors = [
  1505.                 'calibre']
  1506.             mi.tags = [
  1507.                 _('Catalog')]
  1508.             mi.pubdate = mi.timestamp = utcnow()
  1509.             if format == 'mobi':
  1510.                 mi.cover = None
  1511.                 mi.cover_data = (None, None)
  1512.             
  1513.             self.set_metadata(db_id, mi)
  1514.             self.add_format(db_id, format, stream, index_is_id = True)
  1515.         finally:
  1516.             pass
  1517.  
  1518.         self.conn.commit()
  1519.         self.data.refresh_ids(self, [
  1520.             db_id])
  1521.         return db_id
  1522.  
  1523.     
  1524.     def add_news(self, path, arg):
  1525.         format = os.path.splitext(path)[1][1:].lower()
  1526.         stream = None if hasattr(path, 'read') else open(path, 'rb')
  1527.         stream.seek(0)
  1528.         mi = get_metadata(stream, format, use_libprs_metadata = False)
  1529.         stream.seek(0)
  1530.         mi.series_index = 1
  1531.         mi.tags = [
  1532.             _('News')]
  1533.         if arg['add_title_tag']:
  1534.             mi.tags += [
  1535.                 arg['title']]
  1536.         
  1537.         if arg['custom_tags']:
  1538.             mi.tags += arg['custom_tags']
  1539.         
  1540.         obj = self.conn.execute('INSERT INTO books(title, author_sort) VALUES (?, ?)', (mi.title, mi.authors[0]))
  1541.         id = obj.lastrowid
  1542.         self.data.books_added([
  1543.             id], self)
  1544.         self.set_path(id, index_is_id = True)
  1545.         self.conn.commit()
  1546.         if mi.pubdate is None:
  1547.             mi.pubdate = utcnow()
  1548.         
  1549.         if mi.timestamp is None:
  1550.             mi.timestamp = utcnow()
  1551.         
  1552.         self.set_metadata(id, mi)
  1553.         self.add_format(id, format, stream, index_is_id = True)
  1554.         if not hasattr(path, 'read'):
  1555.             stream.close()
  1556.         
  1557.         self.conn.commit()
  1558.         self.data.refresh_ids(self, [
  1559.             id])
  1560.         return id
  1561.  
  1562.     
  1563.     def run_import_plugins(self, path_or_stream, format):
  1564.         format = format.lower()
  1565.         if hasattr(path_or_stream, 'seek'):
  1566.             path_or_stream.seek(0)
  1567.             pt = PersistentTemporaryFile('_import_plugin.' + format)
  1568.             shutil.copyfileobj(path_or_stream, pt, 1048576)
  1569.             pt.close()
  1570.             path = pt.name
  1571.         else:
  1572.             path = path_or_stream
  1573.         return run_plugins_on_import(path, format)
  1574.  
  1575.     
  1576.     def create_book_entry(self, mi, cover = None, add_duplicates = True):
  1577.         if not add_duplicates and self.has_book(mi):
  1578.             return None
  1579.         series_index = self.has_book(mi) if mi.series_index is None else mi.series_index
  1580.         aus = None if mi.author_sort else self.author_sort_from_authors(mi.authors)
  1581.         title = mi.title
  1582.         if isinstance(aus, str):
  1583.             aus = aus.decode(preferred_encoding, 'replace')
  1584.         
  1585.         if isinstance(title, str):
  1586.             title = title.decode(preferred_encoding)
  1587.         
  1588.         obj = self.conn.execute('INSERT INTO books(title, series_index, author_sort) VALUES (?, ?, ?)', (title, series_index, aus))
  1589.         id = obj.lastrowid
  1590.         self.data.books_added([
  1591.             id], self)
  1592.         self.set_path(id, True)
  1593.         self.conn.commit()
  1594.         if mi.timestamp is None:
  1595.             mi.timestamp = utcnow()
  1596.         
  1597.         if mi.pubdate is None:
  1598.             mi.pubdate = utcnow()
  1599.         
  1600.         self.set_metadata(id, mi)
  1601.         if cover is not None:
  1602.             
  1603.             try:
  1604.                 self.set_cover(id, cover)
  1605.             traceback.print_exc()
  1606.  
  1607.         
  1608.         return id
  1609.  
  1610.     
  1611.     def add_books(self, paths, formats, metadata, add_duplicates = True):
  1612.         formats = iter(formats)
  1613.         metadata = iter(metadata)
  1614.         duplicates = []
  1615.         ids = []
  1616.         for path in paths:
  1617.             mi = metadata.next()
  1618.             format = formats.next()
  1619.             if not add_duplicates and self.has_book(mi):
  1620.                 duplicates.append((path, format, mi))
  1621.                 continue
  1622.             
  1623.             series_index = None if mi.series_index is None else mi.series_index
  1624.             aus = None if mi.author_sort else self.author_sort_from_authors(mi.authors)
  1625.             title = mi.title
  1626.             if isinstance(aus, str):
  1627.                 aus = aus.decode(preferred_encoding, 'replace')
  1628.             
  1629.             if isinstance(title, str):
  1630.                 title = title.decode(preferred_encoding)
  1631.             
  1632.             obj = self.conn.execute('INSERT INTO books(title, series_index, author_sort) VALUES (?, ?, ?)', (title, series_index, aus))
  1633.             id = obj.lastrowid
  1634.             self.data.books_added([
  1635.                 id], self)
  1636.             ids.append(id)
  1637.             self.set_path(id, True)
  1638.             self.conn.commit()
  1639.             if mi.timestamp is None:
  1640.                 mi.timestamp = utcnow()
  1641.             
  1642.             if mi.pubdate is None:
  1643.                 mi.pubdate = utcnow()
  1644.             
  1645.             self.set_metadata(id, mi)
  1646.             npath = self.run_import_plugins(path, format)
  1647.             format = os.path.splitext(npath)[-1].lower().replace('.', '').upper()
  1648.             stream = open(npath, 'rb')
  1649.             format = check_ebook_format(stream, format)
  1650.             self.add_format(id, format, stream, index_is_id = True)
  1651.             stream.close()
  1652.         
  1653.         self.conn.commit()
  1654.         self.data.refresh_ids(self, ids)
  1655.         if duplicates:
  1656.             paths = list((lambda .0: for duplicate in .0:
  1657. duplicate[0])(duplicates))
  1658.             formats = list((lambda .0: for duplicate in .0:
  1659. duplicate[1])(duplicates))
  1660.             metadata = list((lambda .0: for duplicate in .0:
  1661. duplicate[2])(duplicates))
  1662.             return ((paths, formats, metadata), len(ids))
  1663.         return (None, len(ids))
  1664.  
  1665.     
  1666.     def import_book(self, mi, formats, notify = True):
  1667.         series_index = None if mi.series_index is None else mi.series_index
  1668.         if not mi.title:
  1669.             mi.title = _('Unknown')
  1670.         
  1671.         if not mi.authors:
  1672.             mi.authors = [
  1673.                 _('Unknown')]
  1674.         
  1675.         aus = None if mi.author_sort else self.author_sort_from_authors(mi.authors)
  1676.         if isinstance(aus, str):
  1677.             aus = aus.decode(preferred_encoding, 'replace')
  1678.         
  1679.         title = None if isinstance(mi.title, unicode) else mi.title.decode(preferred_encoding, 'replace')
  1680.         obj = self.conn.execute('INSERT INTO books(title, series_index, author_sort) VALUES (?, ?, ?)', (title, series_index, aus))
  1681.         id = obj.lastrowid
  1682.         self.data.books_added([
  1683.             id], self)
  1684.         self.set_path(id, True)
  1685.         if mi.timestamp is None:
  1686.             mi.timestamp = utcnow()
  1687.         
  1688.         if mi.pubdate is None:
  1689.             mi.pubdate = utcnow()
  1690.         
  1691.         self.set_metadata(id, mi, ignore_errors = True)
  1692.         for path in formats:
  1693.             ext = os.path.splitext(path)[1][1:].lower()
  1694.             if ext == 'opf':
  1695.                 continue
  1696.             
  1697.             self.add_format_with_hooks(id, ext, path, index_is_id = True)
  1698.         
  1699.         self.conn.commit()
  1700.         self.data.refresh_ids(self, [
  1701.             id])
  1702.         if notify:
  1703.             self.notify('add', [
  1704.                 id])
  1705.         
  1706.  
  1707.     
  1708.     def get_top_level_move_items(self):
  1709.         items = set(os.listdir(self.library_path))
  1710.         paths = set([])
  1711.         for x in self.data.universal_set():
  1712.             path = self.path(x, index_is_id = True)
  1713.             path = path.split(os.sep)[0]
  1714.             paths.add(path)
  1715.         
  1716.         paths.add('metadata.db')
  1717.         path_map = { }
  1718.         for x in paths:
  1719.             path_map[x] = x
  1720.         
  1721.         items = items.intersection(paths)
  1722.         return (items, path_map)
  1723.  
  1724.     
  1725.     def move_library_to(self, newloc, progress = (lambda x: x)):
  1726.         if not os.path.exists(newloc):
  1727.             os.makedirs(newloc)
  1728.         
  1729.         old_dirs = set([])
  1730.         (items, path_map) = self.get_top_level_move_items()
  1731.         for x in items:
  1732.             src = os.path.join(self.library_path, x)
  1733.             dest = os.path.join(newloc, path_map[x])
  1734.             if os.path.isdir(src):
  1735.                 if os.path.exists(dest):
  1736.                     shutil.rmtree(dest)
  1737.                 
  1738.                 shutil.copytree(src, dest)
  1739.                 old_dirs.add(src)
  1740.             elif os.path.exists(dest):
  1741.                 os.remove(dest)
  1742.             
  1743.             shutil.copyfile(src, dest)
  1744.             x = path_map[x]
  1745.             if not isinstance(x, unicode):
  1746.                 x = x.decode(filesystem_encoding, 'replace')
  1747.             
  1748.             progress(x)
  1749.         
  1750.         dbpath = os.path.join(newloc, os.path.basename(self.dbpath))
  1751.         opath = self.dbpath
  1752.         self.conn.close()
  1753.         self.library_path = newloc
  1754.         self.dbpath = dbpath
  1755.         self.connect()
  1756.         
  1757.         try:
  1758.             os.unlink(opath)
  1759.         except:
  1760.             pass
  1761.  
  1762.         for dir in old_dirs:
  1763.             
  1764.             try:
  1765.                 shutil.rmtree(dir)
  1766.             continue
  1767.             continue
  1768.  
  1769.         
  1770.  
  1771.     
  1772.     def __iter__(self):
  1773.         for record in self.data._data:
  1774.             if record is not None:
  1775.                 yield record
  1776.                 continue
  1777.         
  1778.  
  1779.     
  1780.     def all_ids(self):
  1781.         x = self.FIELD_MAP['id']
  1782.         for i in iter(self):
  1783.             yield i[x]
  1784.         
  1785.  
  1786.     
  1787.     def get_data_as_dict(self, prefix = None, authors_as_string = False, ids = None):
  1788.         if prefix is None:
  1789.             prefix = self.library_path
  1790.         
  1791.         FIELDS = set([
  1792.             'title',
  1793.             'authors',
  1794.             'author_sort',
  1795.             'publisher',
  1796.             'rating',
  1797.             'timestamp',
  1798.             'size',
  1799.             'tags',
  1800.             'comments',
  1801.             'series',
  1802.             'series_index',
  1803.             'isbn',
  1804.             'uuid',
  1805.             'pubdate'])
  1806.         for x in self.custom_column_num_map:
  1807.             FIELDS.add(x)
  1808.         
  1809.         data = []
  1810.         for record in self.data:
  1811.             if record is None:
  1812.                 continue
  1813.             
  1814.             db_id = record[self.FIELD_MAP['id']]
  1815.             if ids is not None and db_id not in ids:
  1816.                 continue
  1817.             
  1818.             x = { }
  1819.             for field in FIELDS:
  1820.                 x[field] = record[self.FIELD_MAP[field]]
  1821.             
  1822.             data.append(x)
  1823.             x['id'] = db_id
  1824.             x['formats'] = []
  1825.             if not x['authors']:
  1826.                 x['authors'] = _('Unknown')
  1827.             
  1828.             x['authors'] = [ i.replace('|', ',') for i in x['authors'].split(',') ]
  1829.             x['tags'] = [] if x['tags'] else []
  1830.             path = os.path.join(prefix, self.path(record[self.FIELD_MAP['id']], index_is_id = True))
  1831.             x['cover'] = os.path.join(path, 'cover.jpg')
  1832.             formats = self.formats(record[self.FIELD_MAP['id']], index_is_id = True)
  1833.             if formats:
  1834.                 for fmt in formats.split(','):
  1835.                     path = self.format_abspath(x['id'], fmt, index_is_id = True)
  1836.                     if path is None:
  1837.                         continue
  1838.                     
  1839.                     if prefix != self.library_path:
  1840.                         path = os.path.relpath(path, self.library_path)
  1841.                         path = os.path.join(prefix, path)
  1842.                     
  1843.                     x['formats'].append(path)
  1844.                     x['fmt_' + fmt.lower()] = path
  1845.                 
  1846.                 x['available_formats'] = [ i.upper() for i in formats.split(',') ]
  1847.                 continue
  1848.             []
  1849.         
  1850.         return data
  1851.  
  1852.     
  1853.     def migrate_old(self, db, progress):
  1854.         QCoreApplication = QCoreApplication
  1855.         import PyQt4.QtCore
  1856.         header = _(u'<p>Migrating old database to ebook library in %s<br><center>') % self.library_path
  1857.         progress.setValue(0)
  1858.         progress.setLabelText(header)
  1859.         QCoreApplication.processEvents()
  1860.         
  1861.         db.conn.row_factory = lambda cursor, row: tuple(row)
  1862.         
  1863.         db.conn.text_factory = lambda x: unicode(x, 'utf-8', 'replace')
  1864.         books = db.conn.get('SELECT id, title, sort, timestamp, series_index, author_sort, isbn FROM books ORDER BY id ASC')
  1865.         progress.setAutoReset(False)
  1866.         progress.setRange(0, len(books))
  1867.         for book in books:
  1868.             self.conn.execute('INSERT INTO books(id, title, sort, timestamp, series_index, author_sort, isbn) VALUES(?, ?, ?, ?, ?, ?, ?, ?);', book)
  1869.         
  1870.         tables = '\nauthors  ratings      tags    series    books_tags_link\ncomments               publishers\nbooks_authors_link     conversion_options\nbooks_publishers_link\nbooks_ratings_link\nbooks_series_link      feeds\n'.split()
  1871.         for table in tables:
  1872.             rows = db.conn.get('SELECT * FROM %s ORDER BY id ASC' % table)
  1873.             for row in rows:
  1874.                 self.conn.execute('INSERT INTO %s VALUES(%s)' % (table, ','.join(repeat('?', len(row)))), row)
  1875.             
  1876.         
  1877.         self.conn.commit()
  1878.         self.refresh('timestamp', True)
  1879.         for i, book in enumerate(books):
  1880.             progress.setLabelText(header + _(u'Copying <b>%s</b>') % book[1])
  1881.             id = book[0]
  1882.             self.set_path(id, True)
  1883.             formats = db.formats(id, index_is_id = True)
  1884.             if not formats:
  1885.                 formats = []
  1886.             else:
  1887.                 formats = formats.split(',')
  1888.             for format in formats:
  1889.                 data = db.format(id, format, index_is_id = True)
  1890.                 if data:
  1891.                     self.add_format(id, format, cStringIO.StringIO(data), index_is_id = True)
  1892.                     continue
  1893.             
  1894.             cover = db.cover(id, index_is_id = True)
  1895.             if cover:
  1896.                 self.set_cover(id, cover)
  1897.             
  1898.             progress.setValue(i + 1)
  1899.         
  1900.         self.conn.commit()
  1901.         progress.setLabelText(_('Compacting database'))
  1902.         self.vacuum()
  1903.         progress.reset()
  1904.         return len(books)
  1905.  
  1906.     
  1907.     def find_books_in_directory(self, dirpath, single_book_per_directory):
  1908.         dirpath = os.path.abspath(dirpath)
  1909.         if single_book_per_directory:
  1910.             formats = []
  1911.             for path in os.listdir(dirpath):
  1912.                 path = os.path.abspath(os.path.join(dirpath, path))
  1913.                 if os.path.isdir(path) or not os.access(path, os.R_OK):
  1914.                     continue
  1915.                 
  1916.                 ext = os.path.splitext(path)[1]
  1917.                 if not ext:
  1918.                     continue
  1919.                 
  1920.                 ext = ext[1:].lower()
  1921.                 if ext not in BOOK_EXTENSIONS and ext != 'opf':
  1922.                     continue
  1923.                 
  1924.                 formats.append(path)
  1925.             
  1926.             yield formats
  1927.         else:
  1928.             books = { }
  1929.             for path in os.listdir(dirpath):
  1930.                 path = os.path.abspath(os.path.join(dirpath, path))
  1931.                 if os.path.isdir(path) or not os.access(path, os.R_OK):
  1932.                     continue
  1933.                 
  1934.                 ext = os.path.splitext(path)[1]
  1935.                 if not ext:
  1936.                     continue
  1937.                 
  1938.                 ext = ext[1:].lower()
  1939.                 if ext not in BOOK_EXTENSIONS:
  1940.                     continue
  1941.                 
  1942.                 key = os.path.splitext(path)[0]
  1943.                 if not books.has_key(key):
  1944.                     books[key] = []
  1945.                 
  1946.                 books[key].append(path)
  1947.             
  1948.             for formats in books.values():
  1949.                 yield formats
  1950.             
  1951.  
  1952.     
  1953.     def import_book_directory_multiple(self, dirpath, callback = None):
  1954.         duplicates = []
  1955.         for formats in self.find_books_in_directory(dirpath, False):
  1956.             mi = metadata_from_formats(formats)
  1957.             if mi.title is None:
  1958.                 continue
  1959.             
  1960.             if self.has_book(mi):
  1961.                 duplicates.append((mi, formats))
  1962.                 continue
  1963.             
  1964.             self.import_book(mi, formats)
  1965.             if callable(callback):
  1966.                 if callback(mi.title):
  1967.                     break
  1968.                 
  1969.             callback(mi.title)
  1970.         
  1971.         return duplicates
  1972.  
  1973.     
  1974.     def import_book_directory(self, dirpath, callback = None):
  1975.         dirpath = os.path.abspath(dirpath)
  1976.         formats = self.find_books_in_directory(dirpath, True)
  1977.         formats = list(formats)[0]
  1978.         if not formats:
  1979.             return None
  1980.         mi = metadata_from_formats(formats)
  1981.         if mi.title is None:
  1982.             return None
  1983.         if self.has_book(mi):
  1984.             return [
  1985.                 (mi, formats)]
  1986.         self.import_book(mi, formats)
  1987.  
  1988.     
  1989.     def recursive_import(self, root, single_book_per_directory = True, callback = None):
  1990.         root = os.path.abspath(root)
  1991.         duplicates = []
  1992.         for dirpath in os.walk(root):
  1993.             res = None if single_book_per_directory else self.import_book_directory_multiple(dirpath[0], callback = callback)
  1994.             if res is not None:
  1995.                 duplicates.extend(res)
  1996.             
  1997.             if callable(callback):
  1998.                 if callback(''):
  1999.                     break
  2000.                 
  2001.             callback('')
  2002.         
  2003.         return duplicates
  2004.  
  2005.     
  2006.     def get_custom_recipes(self):
  2007.         for id, title, script in self.conn.get('SELECT id,title,script FROM feeds'):
  2008.             yield (id, title, script)
  2009.         
  2010.  
  2011.     
  2012.     def check_integrity(self, callback):
  2013.         callback(0, _('Checking SQL integrity...'))
  2014.         self.clean()
  2015.         user_version = self.user_version
  2016.         sql = '\n'.join(self.conn.dump())
  2017.         self.conn.close()
  2018.         dest = self.dbpath + '.tmp'
  2019.         if os.path.exists(dest):
  2020.             os.remove(dest)
  2021.         
  2022.         conn = None
  2023.         
  2024.         try:
  2025.             ndb = DBThread(dest, None)
  2026.             ndb.connect()
  2027.             conn = ndb.conn
  2028.             conn.execute('create table temp_sequence(id INTEGER PRIMARY KEY AUTOINCREMENT)')
  2029.             conn.commit()
  2030.             conn.executescript(sql)
  2031.             conn.commit()
  2032.             conn.execute('pragma user_version=%d' % user_version)
  2033.             conn.commit()
  2034.             conn.execute('drop table temp_sequence')
  2035.             conn.commit()
  2036.             conn.close()
  2037.         except:
  2038.             if conn is not None:
  2039.                 
  2040.                 try:
  2041.                     conn.close()
  2042.  
  2043.             
  2044.             if os.path.exists(dest):
  2045.                 os.remove(dest)
  2046.             
  2047.             raise 
  2048.  
  2049.         os.remove(self.dbpath)
  2050.         shutil.copyfile(dest, self.dbpath)
  2051.         self.connect()
  2052.         self.field_metadata.remove_dynamic_categories()
  2053.         self.field_metadata.remove_custom_fields()
  2054.         self.initialize_dynamic()
  2055.         self.refresh()
  2056.         if os.path.exists(dest):
  2057.             os.remove(dest)
  2058.         
  2059.         callback(0.1, _('Checking for missing files.'))
  2060.         bad = { }
  2061.         us = self.data.universal_set()
  2062.         total = float(len(us))
  2063.         for i, id in enumerate(us):
  2064.             formats = self.data.get(id, self.FIELD_MAP['formats'], row_is_id = True)
  2065.             actual_formats = self.formats(id, index_is_id = True)
  2066.             for fmt in formats:
  2067.                 if fmt in actual_formats:
  2068.                     continue
  2069.                 
  2070.                 if id not in bad:
  2071.                     bad[id] = []
  2072.                 
  2073.                 bad[id].append(fmt)
  2074.             
  2075.             callback(0.1 + 0.9 * (1 + i) / total, _('Checked id') + ' %d' % id)
  2076.         
  2077.         for id in bad:
  2078.             for fmt in bad[id]:
  2079.                 self.conn.execute('DELETE FROM data WHERE book=? AND format=?', (id, fmt.upper()))
  2080.             
  2081.         
  2082.         self.conn.commit()
  2083.         self.refresh_ids(list(bad.keys()))
  2084.         return bad
  2085.  
  2086.  
  2087.