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

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.6)
  3.  
  4. __license__ = 'GPL v3'
  5. __copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
  6. __docformat__ = 'restructuredtext en'
  7. import sys
  8. import os
  9. import cStringIO
  10. import re
  11. from textwrap import TextWrapper
  12. from calibre import terminal_controller, preferred_encoding, prints, isbytestring
  13. from calibre.utils.config import OptionParser, prefs, tweaks
  14. from calibre.ebooks.metadata.meta import get_metadata
  15. from calibre.library.database2 import LibraryDatabase2
  16. from calibre.ebooks.metadata.opf2 import OPFCreator, OPF
  17. from calibre.utils.date import isoformat
  18. FIELDS = set([
  19.     'title',
  20.     'authors',
  21.     'author_sort',
  22.     'publisher',
  23.     'rating',
  24.     'timestamp',
  25.     'size',
  26.     'tags',
  27.     'comments',
  28.     'series',
  29.     'series_index',
  30.     'formats',
  31.     'isbn',
  32.     'uuid',
  33.     'pubdate',
  34.     'cover'])
  35.  
  36. def send_message(msg = ''):
  37.     prints('Notifying calibre of the change')
  38.     RC = RC
  39.     import calibre.utils.ipc
  40.     import time
  41.     t = RC(print_error = False)
  42.     t.start()
  43.     time.sleep(3)
  44.     if t.done:
  45.         t.conn.send('refreshdb:' + msg)
  46.         t.conn.close()
  47.     
  48.  
  49.  
  50. def write_dirtied(db):
  51.     prints('Backing up metadata')
  52.     db.dump_metadata()
  53.  
  54.  
  55. def get_parser(usage):
  56.     parser = OptionParser(usage)
  57.     go = parser.add_option_group('GLOBAL OPTIONS')
  58.     go.add_option('--library-path', '--with-library', default = None, help = _('Path to the calibre library. Default is to use the path stored in the settings.'))
  59.     return parser
  60.  
  61.  
  62. def get_db(dbpath, options):
  63.     if options.library_path is not None:
  64.         dbpath = options.library_path
  65.     
  66.     dbpath = os.path.abspath(dbpath)
  67.     return LibraryDatabase2(dbpath)
  68.  
  69.  
  70. def do_list(db, fields, afields, sort_by, ascending, search_text, line_width, separator, prefix, subtitle = 'Books in the calibre database'):
  71.     if sort_by:
  72.         db.sort(sort_by, ascending)
  73.     
  74.     if search_text:
  75.         db.search(search_text)
  76.     
  77.     data = db.get_data_as_dict(prefix, authors_as_string = True)
  78.     fields = [
  79.         'id'] + fields
  80.     title_fields = fields
  81.     fields = [ _[1] if x[0] == '*' else x for x in fields ]
  82.     for f in data:
  83.         fmts = _[2]
  84.         f['formats'] = u'[%s]' % u','.join(fmts)
  85.     
  86.     widths = list(map((lambda x: 0), fields))
  87.     for record in data:
  88.         for f in record.keys():
  89.             record[f] = record[f].replace('\n', ' ')
  90.         
  91.     
  92.     for i in data:
  93.         for j, field in enumerate(fields):
  94.             widths[j] = max(widths[j], len(unicode(i[field])))
  95.         
  96.     
  97.     screen_width = [] if hasattr(record[f], 'isoformat') else [] if line_width < 0 else line_width
  98.     if not screen_width:
  99.         screen_width = 80
  100.     
  101.     field_width = screen_width // len(fields)
  102.     base_widths = (map,)((lambda x: min(x + 1, field_width)), widths)
  103.     while sum(base_widths) < screen_width:
  104.         adjusted = False
  105.         for i in range(len(widths)):
  106.             if base_widths[i] < widths[i]:
  107.                 base_widths[i] += min(screen_width - sum(base_widths), widths[i] - base_widths[i])
  108.                 adjusted = True
  109.                 break
  110.                 continue
  111.         
  112.         if not adjusted:
  113.             break
  114.             continue
  115.     widths = list(base_widths)
  116.     titles = (map,)((lambda x, y: '%-*s%s' % (x - len(separator), y, separator)), widths, title_fields)
  117.     print terminal_controller.GREEN + ''.join(titles) + terminal_controller.NORMAL
  118.     wrappers = map((lambda x: TextWrapper(x - 1)), widths)
  119.     o = cStringIO.StringIO()
  120.     for record in data:
  121.         text = [ wrappers[i].wrap(unicode(record[field]).encode('utf-8')) for i, field in enumerate(fields) ]
  122.         lines = max(map(len, text))
  123.         for l in range(lines):
  124.             for i, field in enumerate(text):
  125.                 ft = [] if l < len(text[i]) else ''
  126.                 filler = '%*s' % (widths[i] - len(ft) - 1, '')
  127.                 o.write(ft)
  128.                 o.write(filler + separator)
  129.             
  130.             print >>o
  131.         
  132.     
  133.     return o.getvalue()
  134.  
  135.  
  136. def list_option_parser(db = None):
  137.     fields = set(FIELDS)
  138.     if db is not None:
  139.         for f in db.custom_column_label_map:
  140.             fields.add('*' + f)
  141.         
  142.     
  143.     parser = get_parser(_('%prog list [options]\n\nList the books available in the calibre database.\n'))
  144.     parser.add_option('-f', '--fields', default = 'title,authors', help = _('The fields to display when listing books in the database. Should be a comma separated list of fields.\nAvailable fields: %s\nDefault: %%default. The special field "all" can be used to select all fields. Only has effect in the text output format.') % ','.join(sorted(fields)))
  145.     parser.add_option('--sort-by', default = None, help = _('The field by which to sort the results.\nAvailable fields: %s\nDefault: %%default') % ','.join(FIELDS))
  146.     parser.add_option('--ascending', default = False, action = 'store_true', help = _('Sort results in ascending order'))
  147.     parser.add_option('-s', '--search', default = None, help = _('Filter the results by the search query. For the format of the search query, please see the search related documentation in the User Manual. Default is to do no filtering.'))
  148.     parser.add_option('-w', '--line-width', default = -1, type = int, help = _('The maximum width of a single line in the output. Defaults to detecting screen size.'))
  149.     parser.add_option('--separator', default = ' ', help = _('The string used to separate fields. Default is a space.'))
  150.     parser.add_option('--prefix', default = None, help = _('The prefix for all file paths. Default is the absolute path to the library folder.'))
  151.     return parser
  152.  
  153.  
  154. def command_list(args, dbpath):
  155.     pre = get_parser('')
  156.     pargs = _[1]
  157.     opts = pre.parse_args(sys.argv[:1] + pargs)[0]
  158.     db = get_db(dbpath, opts)
  159.     parser = list_option_parser(db = db)
  160.     (opts, args) = parser.parse_args(sys.argv[:1] + args)
  161.     afields = set(FIELDS)
  162.     fields = [ str(f.strip().lower()) for f in opts.fields.split(',') ]
  163.     if not set(fields).issubset(afields):
  164.         parser.print_help()
  165.         print 
  166.         prints(_('Invalid fields. Available fields:'), ','.join(sorted(afields)), file = sys.stderr)
  167.         return 1
  168.     if opts.sort_by not in afields and opts.sort_by is not None:
  169.         parser.print_help()
  170.         print 
  171.         prints(_('Invalid sort field. Available fields:'), ','.join(afields), file = sys.stderr)
  172.         return 1
  173.     print do_list(db, fields, afields, opts.sort_by, opts.ascending, opts.search, opts.line_width, opts.separator, opts.prefix)
  174.     return 0
  175.  
  176.  
  177. class DevNull(object):
  178.     
  179.     def write(self, msg):
  180.         pass
  181.  
  182.  
  183. NULL = DevNull()
  184.  
  185. def do_add(db, paths, one_book_per_directory, recurse, add_duplicates):
  186.     orig = sys.stdout
  187.     
  188.     try:
  189.         files = []
  190.         dirs = []
  191.         for path in paths:
  192.             path = os.path.abspath(path)
  193.             if os.path.isdir(path):
  194.                 dirs.append(path)
  195.                 continue
  196.             if os.path.exists(path):
  197.                 files.append(path)
  198.                 continue
  199.             print path, 'not found'
  200.         
  201.         formats = []
  202.         metadata = []
  203.         for book in files:
  204.             format = os.path.splitext(book)[1]
  205.             format = None if format else None
  206.             if not format:
  207.                 continue
  208.             
  209.             stream = open(book, 'rb')
  210.             mi = get_metadata(stream, stream_type = format, use_libprs_metadata = True)
  211.             if not mi.title:
  212.                 mi.title = os.path.splitext(os.path.basename(book))[0]
  213.             
  214.             if not mi.authors:
  215.                 mi.authors = [
  216.                     _('Unknown')]
  217.             
  218.             formats.append(format)
  219.             metadata.append(mi)
  220.         
  221.         file_duplicates = []
  222.         if files:
  223.             file_duplicates = db.add_books(files, formats, metadata, add_duplicates = add_duplicates)
  224.             if file_duplicates:
  225.                 file_duplicates = file_duplicates[0]
  226.             
  227.         
  228.         dir_dups = []
  229.         for dir in dirs:
  230.             if recurse:
  231.                 dir_dups.extend(db.recursive_import(dir, single_book_per_directory = one_book_per_directory))
  232.                 continue
  233.             func = None if one_book_per_directory else db.import_book_directory_multiple
  234.             dups = func(dir)
  235.             if not dups:
  236.                 dups = []
  237.             
  238.             dir_dups.extend(dups)
  239.         
  240.         sys.stdout = sys.__stdout__
  241.         if add_duplicates:
  242.             for mi, formats in dir_dups:
  243.                 db.import_book(mi, formats)
  244.             
  245.         elif dir_dups or file_duplicates:
  246.             print >>sys.stderr, _('The following books were not added as they already exist in the database (see --duplicates option):')
  247.         
  248.         for mi, formats in dir_dups:
  249.             title = mi.title
  250.             if isinstance(title, unicode):
  251.                 title = title.encode(preferred_encoding)
  252.             
  253.             print >>sys.stderr, '\t', title + ':'
  254.             for path in formats:
  255.                 print >>sys.stderr, '\t\t ', path
  256.             
  257.         
  258.         if file_duplicates:
  259.             for path, mi in zip(file_duplicates[0], file_duplicates[2]):
  260.                 title = mi.title
  261.                 if isinstance(title, unicode):
  262.                     title = title.encode(preferred_encoding)
  263.                 
  264.                 print >>sys.stderr, '\t', title + ':'
  265.                 print >>sys.stderr, '\t\t ', path
  266.             
  267.         
  268.         write_dirtied(db)
  269.         send_message()
  270.     finally:
  271.         sys.stdout = orig
  272.  
  273.  
  274.  
  275. def add_option_parser():
  276.     parser = get_parser(_('%prog add [options] file1 file2 file3 ...\n\nAdd the specified files as books to the database. You can also specify directories, see\nthe directory related options below.\n'))
  277.     parser.add_option('-1', '--one-book-per-directory', action = 'store_true', default = False, help = _('Assume that each directory has only a single logical book and that all files in it are different e-book formats of that book'))
  278.     parser.add_option('-r', '--recurse', action = 'store_true', default = False, help = _('Process directories recursively'))
  279.     parser.add_option('-d', '--duplicates', action = 'store_true', default = False, help = _('Add books to database even if they already exist. Comparison is done based on book titles.'))
  280.     parser.add_option('-e', '--empty', action = 'store_true', default = False, help = _('Add an empty book (a book with no formats)'))
  281.     parser.add_option('-t', '--title', default = None, help = _('Set the title of the added empty book'))
  282.     parser.add_option('-a', '--authors', default = None, help = _('Set the authors of the added empty book'))
  283.     parser.add_option('-i', '--isbn', default = None, help = _('Set the ISBN of the added empty book'))
  284.     return parser
  285.  
  286.  
  287. def do_add_empty(db, title, authors, isbn):
  288.     MetaInformation = MetaInformation
  289.     string_to_authors = string_to_authors
  290.     import calibre.ebooks.metadata
  291.     mi = MetaInformation(None)
  292.     if title is not None:
  293.         mi.title = title
  294.     
  295.     if authors:
  296.         mi.authors = string_to_authors(authors)
  297.     
  298.     if isbn:
  299.         mi.isbn = isbn
  300.     
  301.     db.import_book(mi, [])
  302.     write_dirtied(db)
  303.     send_message()
  304.  
  305.  
  306. def command_add(args, dbpath):
  307.     parser = add_option_parser()
  308.     (opts, args) = parser.parse_args(sys.argv[:1] + args)
  309.     if opts.empty:
  310.         do_add_empty(get_db(dbpath, opts), opts.title, opts.authors, opts.isbn)
  311.         return 0
  312.     if len(args) < 2:
  313.         parser.print_help()
  314.         print 
  315.         print >>sys.stderr, _('You must specify at least one file to add')
  316.         return 1
  317.     do_add(get_db(dbpath, opts), args[1:], opts.one_book_per_directory, opts.recurse, opts.duplicates)
  318.     return 0
  319.  
  320.  
  321. def do_remove(db, ids):
  322.     for x in ids:
  323.         if isinstance(x, int):
  324.             db.delete_book(x)
  325.         else:
  326.             for y in x:
  327.                 db.delete_book(y)
  328.             
  329.         send_message()
  330.     
  331.     db.clean()
  332.  
  333.  
  334. def remove_option_parser():
  335.     return get_parser(_('%prog remove ids\n\nRemove the books identified by ids from the database. ids should be a comma separated list of id numbers (you can get id numbers by using the list command). For example, 23,34,57-85\n'))
  336.  
  337.  
  338. def command_remove(args, dbpath):
  339.     parser = remove_option_parser()
  340.     (opts, args) = parser.parse_args(sys.argv[:1] + args)
  341.     if len(args) < 2:
  342.         parser.print_help()
  343.         print 
  344.         print >>sys.stderr, _('You must specify at least one book to remove')
  345.         return 1
  346.     ids = []
  347.     for x in args[1].split(','):
  348.         y = x.split('-')
  349.         if len(y) > 1:
  350.             ids.append(range(int(y[0], int(y[1]))))
  351.             continue
  352.         len(args) < 2
  353.         ids.append(int(y[0]))
  354.     
  355.     do_remove(get_db(dbpath, opts), set(ids))
  356.     return 0
  357.  
  358.  
  359. def do_add_format(db, id, fmt, path):
  360.     db.add_format_with_hooks(id, fmt.upper(), path, index_is_id = True)
  361.  
  362.  
  363. def add_format_option_parser():
  364.     return get_parser(_('%prog add_format [options] id ebook_file\n\nAdd the ebook in ebook_file to the available formats for the logical book identified by id. You can get id by using the list command. If the format already exists, it is replaced.\n'))
  365.  
  366.  
  367. def command_add_format(args, dbpath):
  368.     parser = add_format_option_parser()
  369.     (opts, args) = parser.parse_args(sys.argv[:1] + args)
  370.     if len(args) < 3:
  371.         parser.print_help()
  372.         print 
  373.         print >>sys.stderr, _('You must specify an id and an ebook file')
  374.         return 1
  375.     id = int(args[1])
  376.     path = args[2]
  377.     fmt = os.path.splitext(args[2])[-1]
  378.     do_add_format(get_db(dbpath, opts), id, fmt[1:], path)
  379.     return 0
  380.  
  381.  
  382. def do_remove_format(db, id, fmt):
  383.     db.remove_format(id, fmt, index_is_id = True)
  384.  
  385.  
  386. def remove_format_option_parser():
  387.     return get_parser(_('\n%prog remove_format [options] id fmt\n\nRemove the format fmt from the logical book identified by id. You can get id by using the list command. fmt should be a file extension like LRF or TXT or EPUB. If the logical book does not have fmt available, do nothing.\n'))
  388.  
  389.  
  390. def command_remove_format(args, dbpath):
  391.     parser = remove_format_option_parser()
  392.     (opts, args) = parser.parse_args(sys.argv[:1] + args)
  393.     if len(args) < 3:
  394.         parser.print_help()
  395.         print 
  396.         print >>sys.stderr, _('You must specify an id and a format')
  397.         return 1
  398.     id = int(args[1])
  399.     fmt = args[2].upper()
  400.     do_remove_format(get_db(dbpath, opts), id, fmt)
  401.     return 0
  402.  
  403.  
  404. def do_show_metadata(db, id, as_opf):
  405.     if not db.has_id(id):
  406.         raise ValueError('Id #%d is not present in database.' % id)
  407.     db.has_id(id)
  408.     mi = db.get_metadata(id, index_is_id = True)
  409.     if as_opf:
  410.         mi = OPFCreator(os.getcwd(), mi)
  411.         mi.render(sys.stdout)
  412.     else:
  413.         print unicode(mi).encode(preferred_encoding)
  414.  
  415.  
  416. def show_metadata_option_parser():
  417.     parser = get_parser(_('\n%prog show_metadata [options] id\n\nShow the metadata stored in the calibre database for the book identified by id.\nid is an id number from the list command.\n'))
  418.     parser.add_option('--as-opf', default = False, action = 'store_true', help = _('Print metadata in OPF form (XML)'))
  419.     return parser
  420.  
  421.  
  422. def command_show_metadata(args, dbpath):
  423.     parser = show_metadata_option_parser()
  424.     (opts, args) = parser.parse_args(sys.argv[1:] + args)
  425.     if len(args) < 2:
  426.         parser.print_help()
  427.         print 
  428.         print >>sys.stderr, _('You must specify an id')
  429.         return 1
  430.     id = int(args[1])
  431.     do_show_metadata(get_db(dbpath, opts), id, opts.as_opf)
  432.     return 0
  433.  
  434.  
  435. def do_set_metadata(db, id, stream):
  436.     mi = OPF(stream).to_book_metadata()
  437.     db.set_metadata(id, mi)
  438.     db.clean()
  439.     do_show_metadata(db, id, False)
  440.     write_dirtied(db)
  441.     send_message()
  442.  
  443.  
  444. def set_metadata_option_parser():
  445.     return get_parser(_('\n%prog set_metadata [options] id /path/to/metadata.opf\n\nSet the metadata stored in the calibre database for the book identified by id\nfrom the OPF file metadata.opf. id is an id number from the list command. You\ncan get a quick feel for the OPF format by using the --as-opf switch to the\nshow_metadata command.\n'))
  446.  
  447.  
  448. def command_set_metadata(args, dbpath):
  449.     parser = set_metadata_option_parser()
  450.     (opts, args) = parser.parse_args(sys.argv[1:] + args)
  451.     if len(args) < 3:
  452.         parser.print_help()
  453.         print 
  454.         print >>sys.stderr, _('You must specify an id and a metadata file')
  455.         return 1
  456.     id = int(args[1])
  457.     opf = open(args[2], 'rb')
  458.     do_set_metadata(get_db(dbpath, opts), id, opf)
  459.     return 0
  460.  
  461.  
  462. def do_export(db, ids, dir, opts):
  463.     if ids is None:
  464.         ids = list(db.all_ids())
  465.     
  466.     save_to_disk = save_to_disk
  467.     import calibre.library.save_to_disk
  468.     failures = save_to_disk(db, ids, dir, opts = opts)
  469.     if failures:
  470.         prints('Failed to save the following books:')
  471.         for id, title, tb in failures:
  472.             prints(str(id) + ':', title)
  473.             prints('\t' + '\n\t'.join(tb.splitlines()))
  474.             prints(' ')
  475.         
  476.     
  477.  
  478.  
  479. def export_option_parser():
  480.     parser = get_parser(_('%prog export [options] ids\n\nExport the books specified by ids (a comma separated list) to the filesystem.\nThe export operation saves all formats of the book, its cover and metadata (in\nan opf file). You can get id numbers from the list command.\n'))
  481.     parser.add_option('--all', default = False, action = 'store_true', help = _('Export all books in database, ignoring the list of ids.'))
  482.     parser.add_option('--to-dir', default = '.', help = _('Export books to the specified directory. Default is') + ' %default')
  483.     parser.add_option('--single-dir', default = False, action = 'store_true', help = _('Export all books into a single directory'))
  484.     config = config
  485.     import calibre.library.save_to_disk
  486.     c = config()
  487.     for pref in [
  488.         'asciiize',
  489.         'update_metadata',
  490.         'write_opf',
  491.         'save_cover']:
  492.         opt = c.get_option(pref)
  493.         switch = '--dont-' + pref.replace('_', '-')
  494.         parser.add_option(switch, default = True, action = 'store_false', help = opt.help + ' ' + _('Specifying this switch will turn this behavior off.'), dest = pref)
  495.     
  496.     for pref in [
  497.         'timefmt',
  498.         'template',
  499.         'formats']:
  500.         opt = c.get_option(pref)
  501.         switch = '--' + pref
  502.         parser.add_option(switch, default = opt.default, help = opt.help, dest = pref)
  503.     
  504.     for pref in ('replace_whitespace', 'to_lowercase'):
  505.         opt = c.get_option(pref)
  506.         switch = '--' + pref.replace('_', '-')
  507.         parser.add_option(switch, default = False, action = 'store_true', help = opt.help)
  508.     
  509.     return parser
  510.  
  511.  
  512. def command_export(args, dbpath):
  513.     parser = export_option_parser()
  514.     (opts, args) = parser.parse_args(sys.argv[1:] + args)
  515.     if len(args) < 2 and not (opts.all):
  516.         parser.print_help()
  517.         print 
  518.         print >>sys.stderr, _('You must specify some ids or the %s option') % '--all'
  519.         return 1
  520.     ids = not (opts.all) if opts.all else map(int, args[1].split(','))
  521.     dir = os.path.abspath(os.path.expanduser(opts.to_dir))
  522.     do_export(get_db(dbpath, opts), ids, dir, opts)
  523.     return 0
  524.  
  525.  
  526. def do_add_custom_column(db, label, name, datatype, is_multiple, display):
  527.     num = db.create_custom_column(label, name, datatype, is_multiple, display = display)
  528.     prints('Custom column created with id: %d' % num)
  529.  
  530.  
  531. def add_custom_column_option_parser():
  532.     CustomColumns = CustomColumns
  533.     import calibre.library.custom_columns
  534.     parser = get_parser(_('%prog add_custom_column [options] label name datatype\n\nCreate a custom column. label is the machine friendly name of the column. Should\nnot contain spaces or colons. name is the human friendly name of the column.\ndatatype is one of: {0}\n').format(', '.join(CustomColumns.CUSTOM_DATA_TYPES)))
  535.     parser.add_option('--is-multiple', default = False, action = 'store_true', help = _('This column stores tag like data (i.e. multiple comma separated values). Only applies if datatype is text.'))
  536.     parser.add_option('--display', default = '{}', help = _('A dictionary of options to customize how the data in this column will be interpreted.'))
  537.     return parser
  538.  
  539.  
  540. def command_add_custom_column(args, dbpath):
  541.     import json
  542.     parser = add_custom_column_option_parser()
  543.     (opts, args) = parser.parse_args(args)
  544.     if len(args) < 3:
  545.         parser.print_help()
  546.         print 
  547.         print >>sys.stderr, _('You must specify label, name and datatype')
  548.         return 1
  549.     do_add_custom_column(get_db(dbpath, opts), args[0], args[1], args[2], opts.is_multiple, json.loads(opts.display))
  550.     db = get_db(dbpath, opts)
  551.     db.prefs['field_metadata'] = db.field_metadata.all_metadata()
  552.     return 0
  553.  
  554.  
  555. def catalog_option_parser(args):
  556.     available_catalog_formats = available_catalog_formats
  557.     plugin_for_catalog_format = plugin_for_catalog_format
  558.     import calibre.customize.ui
  559.     Log = Log
  560.     import calibre.utils.logging
  561.     
  562.     def add_plugin_parser_options(fmt, parser, log):
  563.         plugin = plugin_for_catalog_format(fmt)
  564.         for option in plugin.cli_options:
  565.             if option.action:
  566.                 parser.add_option(option.option, default = option.default, dest = option.dest, action = option.action, help = option.help)
  567.                 continue
  568.             parser.add_option(option.option, default = option.default, dest = option.dest, help = option.help)
  569.         
  570.         return plugin
  571.  
  572.     
  573.     def print_help(parser, log):
  574.         help = parser.format_help().encode(preferred_encoding, 'replace')
  575.         log(help)
  576.  
  577.     
  578.     def validate_command_line(parser, args, log):
  579.         if not len(args) or args[0].startswith('-'):
  580.             print_help(parser, log)
  581.             log.error("\n\nYou must specify a catalog output file of the form 'path/to/destination.extension'\nTo review options for an output format, type 'calibredb catalog <.extension> --help'\nFor example, 'calibredb catalog .xml --help'\n")
  582.             raise SystemExit(1)
  583.         args[0].startswith('-')
  584.         output = os.path.abspath(args[0])
  585.         file_extension = output[output.rfind('.') + 1:].lower()
  586.         if file_extension not in available_catalog_formats():
  587.             print_help(parser, log)
  588.             log.error("No catalog plugin available for extension '%s'.\n" % file_extension + 'Catalog plugins available for %s\n' % ', '.join(available_catalog_formats()))
  589.             raise SystemExit(1)
  590.         file_extension not in available_catalog_formats()
  591.         return (output, file_extension)
  592.  
  593.     log = Log()
  594.     parser = get_parser(_('\n    %prog catalog /path/to/destination.(csv|epub|mobi|xml ...) [options]\n\n    Export a catalog in format specified by path/to/destination extension.\n    Options control how entries are displayed in the generated catalog ouput.\n    '))
  595.     (output, fmt) = validate_command_line(parser, args, log)
  596.     parser.add_option('-i', '--ids', default = None, dest = 'ids', help = _('Comma-separated list of database IDs to catalog.\nIf declared, --search is ignored.\nDefault: all'))
  597.     parser.add_option('-s', '--search', default = None, dest = 'search_text', help = _('Filter the results by the search query. For the format of the search query, please see the search-related documentation in the User Manual.\nDefault: no filtering'))
  598.     parser.add_option('-v', '--verbose', default = False, action = 'store_true', dest = 'verbose', help = _('Show detailed output information. Useful for debugging'))
  599.     plugin = add_plugin_parser_options(fmt, parser, log)
  600.     return (parser, plugin, log)
  601.  
  602.  
  603. def command_catalog(args, dbpath):
  604.     (parser, plugin, log) = catalog_option_parser(args)
  605.     (opts, args) = parser.parse_args(sys.argv[1:])
  606.     if len(args) < 2:
  607.         parser.print_help()
  608.         print 
  609.         print >>sys.stderr, _('Error: You must specify a catalog output file')
  610.         return 1
  611.     opts.connected_device = {
  612.         'is_device_connected': False,
  613.         'kind': None,
  614.         'name': None,
  615.         'save_template': None,
  616.         'serial': None,
  617.         'storage': None }
  618.     plugin.__enter__()
  619.     
  620.     try:
  621.         plugin.run(args[1], opts, get_db(dbpath, opts))
  622.     finally:
  623.         pass
  624.  
  625.     return 0
  626.  
  627.  
  628. def parse_series_string(db, label, value):
  629.     val = unicode(value).strip()
  630.     s_index = None
  631.     pat = re.compile('\\[([.0-9]+)\\]')
  632.     match = pat.search(val)
  633.     if match is not None:
  634.         val = pat.sub('', val).strip()
  635.         s_index = float(match.group(1))
  636.     elif val:
  637.         if tweaks['series_index_auto_increment'] == 'next':
  638.             s_index = db.get_next_cc_series_num_for(val, label = label)
  639.         else:
  640.             s_index = 1
  641.     
  642.     return (val, s_index)
  643.  
  644.  
  645. def do_set_custom(db, col, id_, val, append):
  646.     if db.custom_column_label_map[col]['datatype'] == 'series':
  647.         (val, s_index) = parse_series_string(db, col, val)
  648.         db.set_custom(id_, val, extra = s_index, label = col, append = append)
  649.         prints('Data set to: %r[%4.2f]' % (db.get_custom(id_, label = col, index_is_id = True), db.get_custom_extra(id_, label = col, index_is_id = True)))
  650.     else:
  651.         db.set_custom(id_, val, label = col, append = append)
  652.         prints('Data set to: %r' % db.get_custom(id_, label = col, index_is_id = True))
  653.  
  654.  
  655. def set_custom_option_parser():
  656.     parser = get_parser(_('\n    %prog set_custom [options] column id value\n\n    Set the value of a custom column for the book identified by id.\n    You can get a list of ids using the list command.\n    You can get a list of custom column names using the custom_columns\n    command.\n    '))
  657.     parser.add_option('-a', '--append', default = False, action = 'store_true', help = _('If the column stores multiple values, append the specified values to the existing ones, instead of replacing them.'))
  658.     return parser
  659.  
  660.  
  661. def command_set_custom(args, dbpath):
  662.     parser = set_custom_option_parser()
  663.     (opts, args) = parser.parse_args(args)
  664.     if len(args) < 3:
  665.         parser.print_help()
  666.         print 
  667.         print >>sys.stderr, _('Error: You must specify a field name, id and value')
  668.         return 1
  669.     do_set_custom(get_db(dbpath, opts), args[0], int(args[1]), args[2], opts.append)
  670.     return 0
  671.  
  672.  
  673. def do_custom_columns(db, details):
  674.     pformat = pformat
  675.     import pprint
  676.     cols = db.custom_column_label_map
  677.     for col, data in cols.items():
  678.         if details:
  679.             prints(col)
  680.             print 
  681.             prints(pformat(data))
  682.             print '\n'
  683.             continue
  684.         prints(col, '(%d)' % data['num'])
  685.     
  686.  
  687.  
  688. def custom_columns_option_parser():
  689.     parser = get_parser(_('\n    %prog custom_columns [options]\n\n    List available custom columns. Shows column labels and ids.\n    '))
  690.     parser.add_option('-d', '--details', default = False, action = 'store_true', help = _('Show details for each column.'))
  691.     return parser
  692.  
  693.  
  694. def command_custom_columns(args, dbpath):
  695.     parser = custom_columns_option_parser()
  696.     (opts, args) = parser.parse_args(args)
  697.     do_custom_columns(get_db(dbpath, opts), opts.details)
  698.     return 0
  699.  
  700.  
  701. def do_remove_custom_column(db, label, force):
  702.     if not force:
  703.         q = raw_input(_('You will lose all data in the column: %r. Are you sure (y/n)? ') % label)
  704.         if q.lower().strip() != _('y'):
  705.             return None
  706.     
  707.     db.delete_custom_column(label = label)
  708.     prints('Column %r removed.' % label)
  709.  
  710.  
  711. def remove_custom_column_option_parser():
  712.     parser = get_parser(_('\n    %prog remove_custom_column [options] label\n\n    Remove the custom column identified by label. You can see available\n    columns with the custom_columns command.\n    '))
  713.     parser.add_option('-f', '--force', default = False, action = 'store_true', help = _('Do not ask for confirmation'))
  714.     return parser
  715.  
  716.  
  717. def command_remove_custom_column(args, dbpath):
  718.     parser = remove_custom_column_option_parser()
  719.     (opts, args) = parser.parse_args(args)
  720.     if len(args) < 1:
  721.         parser.print_help()
  722.         print 
  723.         prints(_('Error: You must specify a column label'), file = sys.stderr)
  724.         return 1
  725.     do_remove_custom_column(get_db(dbpath, opts), args[0], opts.force)
  726.     db = get_db(dbpath, opts)
  727.     db.prefs['field_metadata'] = db.field_metadata.all_metadata()
  728.     return 0
  729.  
  730.  
  731. def saved_searches_option_parser():
  732.     parser = get_parser(_('\n    %prog saved_searches [options] list\n    %prog saved_searches add name search\n    %prog saved_searches remove name\n\n    Manage the saved searches stored in this database.\n    If you try to add a query with a name that already exists, it will be\n    replaced.\n    '))
  733.     return parser
  734.  
  735.  
  736. def command_saved_searches(args, dbpath):
  737.     parser = saved_searches_option_parser()
  738.     (opts, args) = parser.parse_args(args)
  739.     if len(args) < 1:
  740.         parser.print_help()
  741.         print 
  742.         prints(_('Error: You must specify an action (add|remove|list)'), file = sys.stderr)
  743.         return 1
  744.     saved_searches = saved_searches
  745.     import calibre.utils.search_query_parser
  746.     db = get_db(dbpath, opts)
  747.     db
  748.     ss = saved_searches()
  749.     if args[0] == 'list':
  750.         for name in ss.names():
  751.             prints(_('Name:'), name)
  752.             prints(_('Search string:'), ss.lookup(name))
  753.             print 
  754.         
  755.     elif args[0] == 'add':
  756.         if len(args) < 3:
  757.             parser.print_help()
  758.             print 
  759.             prints(_('Error: You must specify a name and a search string'), file = sys.stderr)
  760.             return 1
  761.         ss.add(args[1], args[2])
  762.         prints(args[1], _('added'))
  763.     elif args[0] == 'remove':
  764.         if len(args) < 2:
  765.             parser.print_help()
  766.             print 
  767.             prints(_('Error: You must specify a name'), file = sys.stderr)
  768.             return 1
  769.         ss.delete(args[1])
  770.         prints(args[1], _('removed'))
  771.     else:
  772.         parser.print_help()
  773.         print 
  774.         prints(_('Error: Action %s not recognized, must be one of: (add|remove|list)') % args[1], file = sys.stderr)
  775.         return 1
  776.     return len(args) < 2
  777.  
  778.  
  779. def check_library_option_parser():
  780.     CHECKS = CHECKS
  781.     import calibre.library.check_library
  782.     parser = ', '.join([]([]([ c[0] for c in CHECKS ])))
  783.     parser.add_option('-c', '--csv', default = False, action = 'store_true', help = _('Output in CSV'))
  784.     parser.add_option('-r', '--report', default = None, dest = 'report', help = _('Comma-separated list of reports.\nDefault: all'))
  785.     parser.add_option('-e', '--ignore_extensions', default = None, dest = 'exts', help = _('Comma-separated list of extensions to ignore.\nDefault: all'))
  786.     parser.add_option('-n', '--ignore_names', default = None, dest = 'names', help = _('Comma-separated list of names to ignore.\nDefault: all'))
  787.     return parser
  788.  
  789.  
  790. def command_check_library(args, dbpath):
  791.     CheckLibrary = CheckLibrary
  792.     CHECKS = CHECKS
  793.     import calibre.library.check_library
  794.     parser = check_library_option_parser()
  795.     (opts, args) = parser.parse_args(args)
  796.     if len(args) != 0:
  797.         parser.print_help()
  798.         return 1
  799.     if opts.library_path is not None:
  800.         dbpath = opts.library_path
  801.     
  802.     if isbytestring(dbpath):
  803.         dbpath = dbpath.decode(preferred_encoding)
  804.     
  805.     
  806.     def print_one(checker, check):
  807.         attr = check[0]
  808.         list = getattr(checker, attr, None)
  809.         if list is None:
  810.             return None
  811.         if opts.csv:
  812.             for i in list:
  813.                 print check[1] + ',' + i[0] + ',' + i[1]
  814.             
  815.         else:
  816.             print check[1]
  817.             for i in list:
  818.                 print '    %-40.40s - %-40.40s' % (i[0], i[1])
  819.             
  820.  
  821.     db = LibraryDatabase2(dbpath)
  822.     checker = CheckLibrary(dbpath, db)
  823.     checker.scan_library(names, exts)
  824.     for check in checks:
  825.         print_one(checker, check)
  826.     
  827.  
  828.  
  829. def restore_database_option_parser():
  830.     parser = get_parser(_('%prog restore_database [options]\n\nRestore this database from the metadata stored in OPF files in each\ndirectory of the calibre library. This is useful if your metadata.db file\nhas been corrupted.\n\nWARNING: This command completely regenerates your database. You will lose\nall saved searches, user categories, plugboards, stored per-book conversion\nsettings, and custom recipes. Restored metadata will only be as accurate as\nwhat is found in the OPF files.\n    '))
  831.     parser.add_option('-r', '--really-do-it', default = False, action = 'store_true', help = _('Really do the recovery. The command will not run unless this option is specified.'))
  832.     return parser
  833.  
  834.  
  835. def command_restore_database(args, dbpath):
  836.     Restore = Restore
  837.     import calibre.library.restore
  838.     parser = restore_database_option_parser()
  839.     (opts, args) = parser.parse_args(args)
  840.     if len(args) != 0:
  841.         parser.print_help()
  842.         return 1
  843.     if not opts.really_do_it:
  844.         prints(_('You must provide the --really-do-it option to do a recovery'), end = '\n\n')
  845.         parser.print_help()
  846.         return 1
  847.     if isbytestring(dbpath):
  848.         dbpath = dbpath.decode(preferred_encoding)
  849.     
  850.     
  851.     class Progress(object):
  852.         
  853.         def __init__(self):
  854.             self.total = 1
  855.  
  856.         
  857.         def __call__(self, msg, step):
  858.             if msg is None:
  859.                 self.total = float(step)
  860.             else:
  861.                 prints(msg, '...', '%d%%' % int(100 * (step / self.total)))
  862.  
  863.  
  864.     r = Restore(dbpath, progress_callback = Progress())
  865.     r.start()
  866.     r.join()
  867.     if r.tb is not None:
  868.         prints('Restoring database failed with error:')
  869.         prints(r.tb)
  870.     else:
  871.         prints('Restoring database succeeded')
  872.         prints('old database saved as', r.olddb)
  873.         if r.errors_occurred:
  874.             name = 'calibre_db_restore_report.txt'
  875.             open('calibre_db_restore_report.txt', 'wb').write(r.report.encode('utf-8'))
  876.             prints('Some errors occurred. A detailed report was saved to', name)
  877.         
  878.  
  879.  
  880. def list_categories_option_parser():
  881.     parser = get_parser(_('%prog list_categories [options]\n\nProduce a report of the category information in the database. The\ninformation is the equivalent of what is shown in the tags pane.\n'))
  882.     parser.add_option('-i', '--item_count', default = False, action = 'store_true', help = _('Output only the number of items in a category instead of the counts per item within the category'))
  883.     parser.add_option('-c', '--csv', default = False, action = 'store_true', help = _('Output in CSV'))
  884.     parser.add_option('-q', '--quote', default = '"', help = _('The character to put around the category value in CSV mode. Default is quotes (").'))
  885.     parser.add_option('-r', '--categories', default = '', dest = 'report', help = _('Comma-separated list of category lookup names.\nDefault: all'))
  886.     parser.add_option('-w', '--idth', default = -1, type = int, help = _('The maximum width of a single line in the output. Defaults to detecting screen size.'))
  887.     parser.add_option('-s', '--separator', default = ',', help = _('The string used to separate fields in CSV mode. Default is a comma.'))
  888.     return parser
  889.  
  890.  
  891. def command_list_categories(args, dbpath):
  892.     parser = list_categories_option_parser()
  893.     (opts, args) = parser.parse_args(args)
  894.     if len(args) != 0:
  895.         parser.print_help()
  896.         return 1
  897.     if opts.library_path is not None:
  898.         dbpath = opts.library_path
  899.     
  900.     if isbytestring(dbpath):
  901.         dbpath = dbpath.decode(preferred_encoding)
  902.     
  903.     db = LibraryDatabase2(dbpath)
  904.     category_data = db.get_categories()
  905.     data = []
  906.     report_on = _[1]
  907.     categories = _[2]
  908.     categories.sort(cmp = (lambda x, y: None(None, cmp if x[0] != '#' else x[1:] if y[0] != '#' else y[1:])))
  909.     fields = [
  910.         'category',
  911.         'tag_name',
  912.         'count',
  913.         'rating']
  914.     
  915.     def do_list():
  916.         separator = ' '
  917.         widths = list(map((lambda x: 0), fields))
  918.         for i in data:
  919.             for j, field in enumerate(fields):
  920.                 widths[j] = max(widths[j], max(len(field), len(unicode(i[field]))))
  921.             
  922.         
  923.         screen_width = None if opts.line_width < 0 else opts.line_width
  924.         if not screen_width:
  925.             screen_width = 80
  926.         
  927.         field_width = screen_width // len(fields)
  928.         base_widths = (map,)((lambda x: min(x + 1, field_width)), widths)
  929.         while sum(base_widths) < screen_width:
  930.             adjusted = False
  931.             for i in range(len(widths)):
  932.                 if base_widths[i] < widths[i]:
  933.                     base_widths[i] += min(screen_width - sum(base_widths), widths[i] - base_widths[i])
  934.                     adjusted = True
  935.                     break
  936.                     continue
  937.             
  938.             if not adjusted:
  939.                 break
  940.                 continue
  941.         widths = list(base_widths)
  942.         titles = (map,)((lambda x, y: '%-*s%s' % (x - len(separator), y, separator)), widths, fields)
  943.         print terminal_controller.GREEN + ''.join(titles) + terminal_controller.NORMAL
  944.         wrappers = map((lambda x: TextWrapper(x - 1)), widths)
  945.         o = cStringIO.StringIO()
  946.         for record in data:
  947.             text = [ wrappers[i].wrap(unicode(record[field]).encode('utf-8')) for i, field in enumerate(fields) ]
  948.             lines = max(map(len, text))
  949.             for l in range(lines):
  950.                 for i, field in enumerate(text):
  951.                     ft = [] if l < len(text[i]) else ''
  952.                     filler = '%*s' % (widths[i] - len(ft) - 1, '')
  953.                     o.write(ft)
  954.                     o.write(filler + separator)
  955.                 
  956.                 print >>o
  957.             
  958.         
  959.         print o.getvalue()
  960.  
  961.     
  962.     def do_csv():
  963.         lf = '{category},"{tag_name}",{count},{rating}'
  964.         lf = lf.replace(',', opts.separator).replace('\\t', '\t').replace('\\n', '\n')
  965.         lf = lf.replace('"', opts.quote)
  966.         for d in data:
  967.             print lf.format(**d)
  968.         
  969.  
  970.     if opts.csv:
  971.         do_csv()
  972.     else:
  973.         do_list()
  974.  
  975. COMMANDS = ('list', 'add', 'remove', 'add_format', 'remove_format', 'show_metadata', 'set_metadata', 'export', 'catalog', 'saved_searches', 'add_custom_column', 'custom_columns', 'remove_custom_column', 'set_custom', 'restore_database', 'check_library', 'list_categories')
  976.  
  977. def option_parser():
  978.     parser = OptionParser(_('%%prog command [options] [arguments]\n\n%%prog is the command line interface to the calibre books database.\n\ncommand is one of:\n  %s\n\nFor help on an individual command: %%prog command --help\n') % '\n  '.join(COMMANDS))
  979.     return parser
  980.  
  981.  
  982. def main(args = sys.argv):
  983.     parser = option_parser()
  984.     if len(args) < 2:
  985.         parser.print_help()
  986.         return 1
  987.     if args[1] not in COMMANDS:
  988.         if args[1] == '--version':
  989.             parser.print_version()
  990.             return 0
  991.         parser.print_help()
  992.         return 1
  993.     command = eval('command_' + args[1])
  994.     dbpath = prefs['library_path']
  995.     return command(args[2:], dbpath)
  996.  
  997. if __name__ == '__main__':
  998.     sys.exit(main())
  999.  
  1000.