diff --git a/resources/recipes/sportsillustrated.recipe b/resources/recipes/sportsillustrated.recipe
index dd1df16ac7..f5a7b4c32b 100644
--- a/resources/recipes/sportsillustrated.recipe
+++ b/resources/recipes/sportsillustrated.recipe
@@ -1,5 +1,5 @@
from calibre.web.feeds.recipes import BasicNewsRecipe
-from calibre.ebooks.BeautifulSoup import BeautifulSoup
+#from calibre.ebooks.BeautifulSoup import BeautifulSoup
from urllib import quote
class SportsIllustratedRecipe(BasicNewsRecipe) :
@@ -91,7 +91,7 @@ class SportsIllustratedRecipe(BasicNewsRecipe) :
# expire : no idea what value to use
# All this comes from the Javascript function that redirects to the print version. It's called PT() and is defined in the file 48.js
- def preprocess_html(self, soup):
+ '''def preprocess_html(self, soup):
header = soup.find('div', attrs = {'class' : 'siv_artheader'})
homeMadeSoup = BeautifulSoup('
')
body = homeMadeSoup.body
@@ -115,4 +115,5 @@ class SportsIllustratedRecipe(BasicNewsRecipe) :
body.append(para)
return homeMadeSoup
+ '''
diff --git a/src/calibre/devices/sne/driver.py b/src/calibre/devices/sne/driver.py
index bb8d34c59c..04e5cd0d76 100644
--- a/src/calibre/devices/sne/driver.py
+++ b/src/calibre/devices/sne/driver.py
@@ -33,6 +33,6 @@ class SNE(USBMS):
STORAGE_CARD_VOLUME_LABEL = 'SNE Storage Card'
EBOOK_DIR_MAIN = EBOOK_DIR_CARD_A = 'Books'
- SUPPORTS_SUB_DIRS = True
+ SUPPORTS_SUB_DIRS = False
diff --git a/src/calibre/ebooks/rtf/input.py b/src/calibre/ebooks/rtf/input.py
index 92ac8a2519..d1a6b7c88a 100644
--- a/src/calibre/ebooks/rtf/input.py
+++ b/src/calibre/ebooks/rtf/input.py
@@ -286,7 +286,6 @@ class RTFInput(InputFormatPlugin):
try:
xml = self.generate_xml(stream.name)
except RtfInvalidCodeException, e:
- raise
raise ValueError(_('This RTF file has a feature calibre does not '
'support. Convert it to HTML first and then try it.\n%s')%e)
diff --git a/src/calibre/ebooks/rtf2xml/ParseRtf.py b/src/calibre/ebooks/rtf2xml/ParseRtf.py
index cdd9a3d088..d673836210 100755
--- a/src/calibre/ebooks/rtf2xml/ParseRtf.py
+++ b/src/calibre/ebooks/rtf2xml/ParseRtf.py
@@ -226,10 +226,6 @@ class ParseRtf:
try:
return_value = process_tokens_obj.process_tokens()
except InvalidRtfException, msg:
- try:
- os.remove(self.__temp_file)
- except OSError:
- pass
#Check to see if the file is correctly encoded
encode_obj = default_encoding.DefaultEncoding(
in_file = self.__temp_file,
@@ -241,14 +237,17 @@ class ParseRtf:
check_encoding_obj = check_encoding.CheckEncoding(
bug_handler = RtfInvalidCodeException,
)
- enc = encode_obj.get_codepage()
- if enc != 'mac_roman':
- enc = 'cp' + enc
+ enc = 'cp' + encode_obj.get_codepage()
+ msg = 'Exception in token processing'
if check_encoding_obj.check_encoding(self.__file, enc):
file_name = self.__file if isinstance(self.__file, str) \
else self.__file.encode('utf-8')
msg = 'File %s does not appear to be correctly encoded.\n' % file_name
- raise InvalidRtfException, msg
+ try:
+ os.remove(self.__temp_file)
+ except OSError:
+ pass
+ raise InvalidRtfException, msg
delete_info_obj = delete_info.DeleteInfo(
in_file = self.__temp_file,
copy = self.__copy,
diff --git a/src/calibre/ebooks/rtf2xml/default_encoding.py b/src/calibre/ebooks/rtf2xml/default_encoding.py
index 53887e0d90..3ddfbcd321 100755
--- a/src/calibre/ebooks/rtf2xml/default_encoding.py
+++ b/src/calibre/ebooks/rtf2xml/default_encoding.py
@@ -74,9 +74,6 @@ class DefaultEncoding:
if not self.__datafetched:
self._encoding()
self.__datafetched = True
- if self.__platform == 'Macintosh':
- code_page = self.__code_page
- else:
code_page = 'ansicpg' + self.__code_page
return self.__platform, code_page, self.__default_num
@@ -94,49 +91,60 @@ class DefaultEncoding:
def _encoding(self):
with open(self.__file, 'r') as read_obj:
+ cpfound = False
if not self.__fetchraw:
for line in read_obj:
self.__token_info = line[:16]
if self.__token_info == 'mi 3:
- msg = 'flag problem\n'
+ msg = 'Flag problem\n'
raise self.__bug_handler, msg
return True
elif self.__token_info in self.__allowable :
@@ -173,8 +171,8 @@ class DeleteInfo:
Return True for all control words.
Return False otherwise.
"""
- if self.__delete_count == self.__cb_count and self.__token_info ==\
- 'cb33\n
+
def __collect_tokens_func(self, line):
"""
Requires:
@@ -194,18 +227,19 @@ class Info:
att = line[6:16]
value = line[20:-1]
att_changed = self.__token_dict.get(att)
- if att_changed == None:
+ if att_changed is None:
if self.__run_level > 3:
- msg = 'no dictionary match for %s\n' % att
+ msg = 'No dictionary match for %s\n' % att
raise self.__bug_handler, msg
else:
self.__text_string += '<%s>%s' % (att_changed, value)
+
def __single_field_func(self, line, tag):
value = line[20:-1]
self.__write_obj.write(
- 'mi%s\n' % (tag, tag, value)
+ 'mi%s\n' % (tag, tag, value)
)
+
def __after_info_table_func(self, line):
"""
Requires:
@@ -217,6 +251,7 @@ class Info:
the file.
"""
self.__write_obj.write(line)
+
def fix_info(self):
"""
Requires:
@@ -234,20 +269,15 @@ class Info:
information table, simply write the line to the output file.
"""
self.__initiate_values()
- read_obj = open(self.__file, 'r')
- self.__write_obj = open(self.__write_to, 'w')
- line_to_read = 1
- while line_to_read:
- line_to_read = read_obj.readline()
- line = line_to_read
- self.__token_info = line[:16]
- action = self.__state_dict.get(self.__state)
- if action == None:
- sys.stderr.write('no no matching state in module styles.py\n')
- sys.stderr.write(self.__state + '\n')
- action(line)
- read_obj.close()
- self.__write_obj.close()
+ with open(self.__file, 'r') as read_obj:
+ with open(self.__write_to, 'wb') as self.__write_obj:
+ for line in read_obj:
+ self.__token_info = line[:16]
+ action = self.__state_dict.get(self.__state)
+ if action is None:
+ sys.stderr.write('No matching state in module styles.py\n')
+ sys.stderr.write(self.__state + '\n')
+ action(line)
copy_obj = copy.Copy(bug_handler = self.__bug_handler)
if self.__copy:
copy_obj.copy_file(self.__write_to, "info.data")
diff --git a/src/calibre/ebooks/rtf2xml/process_tokens.py b/src/calibre/ebooks/rtf2xml/process_tokens.py
index 9460af07fc..c6cf124425 100755
--- a/src/calibre/ebooks/rtf2xml/process_tokens.py
+++ b/src/calibre/ebooks/rtf2xml/process_tokens.py
@@ -70,7 +70,7 @@ class ProcessTokens:
';' : ('mc', ';', self.ms_sub_func),
# this must be wrong
'-' : ('mc', '-', self.ms_sub_func),
- 'line' : ('mi', 'hardline-break', self.hardline_func), #calibre
+ 'line' : ('mi', 'hardline-break', self.direct_conv_func), #calibre
# misc => ml
'*' : ('ml', 'asterisk__', self.default_func),
':' : ('ml', 'colon_____', self.default_func),
@@ -78,7 +78,6 @@ class ProcessTokens:
'backslash' : ('nu', '\\', self.text_func),
'ob' : ('nu', '{', self.text_func),
'cb' : ('nu', '}', self.text_func),
- #'line' : ('nu', ' ', self.text_func), calibre
# paragraph formatting => pf
'page' : ('pf', 'page-break', self.default_func),
'par' : ('pf', 'par-end___', self.default_func),
@@ -231,11 +230,15 @@ class ProcessTokens:
'trhdr' : ('tb', 'row-header', self.default_func),
# preamble => pr
# document information => di
+ # TODO integrate \userprops
'info' : ('di', 'doc-info__', self.default_func),
+ 'title' : ('di', 'title_____', self.default_func),
'author' : ('di', 'author____', self.default_func),
'operator' : ('di', 'operator__', self.default_func),
- 'title' : ('di', 'title_____', self.default_func),
+ 'manager' : ('di', 'manager___', self.default_func),
+ 'company' : ('di', 'company___', self.default_func),
'keywords' : ('di', 'keywords__', self.default_func),
+ 'category' : ('di', 'category__', self.default_func),
'doccomm' : ('di', 'doc-notes_', self.default_func),
'comment' : ('di', 'doc-notes_', self.default_func),
'subject' : ('di', 'subject___', self.default_func),
@@ -244,11 +247,19 @@ class ProcessTokens:
'mo' : ('di', 'month_____', self.default_func),
'dy' : ('di', 'day_______', self.default_func),
'min' : ('di', 'minute____', self.default_func),
+ 'sec' : ('di', 'second____', self.default_func),
'revtim' : ('di', 'revis-time', self.default_func),
+ 'edmins' : ('di', 'edit-time_', self.default_func),
+ 'printim' : ('di', 'print-time', self.default_func),
+ 'buptim' : ('di', 'backuptime', self.default_func),
'nofwords' : ('di', 'num-of-wor', self.default_func),
'nofchars' : ('di', 'num-of-chr', self.default_func),
+ 'nofcharsws' : ('di', 'numofchrws', self.default_func),
'nofpages' : ('di', 'num-of-pag', self.default_func),
- 'edmins' : ('di', 'edit-time_', self.default_func),
+ 'version' : ('di', 'version___', self.default_func),
+ 'vern' : ('di', 'intern-ver', self.default_func),
+ 'hlinkbase' : ('di', 'linkbase__', self.default_func),
+ 'id' : ('di', 'internalID', self.default_func),
# headers and footers => hf
'headerf' : ('hf', 'head-first', self.default_func),
'headerl' : ('hf', 'head-left_', self.default_func),
@@ -605,7 +616,7 @@ class ProcessTokens:
def ms_sub_func(self, pre, token, num):
return 'tx ", input_file)
input_file = self.__utf_ud.sub("\\{\\uc0 \g<1>\\}", input_file)
#remove \n in bin data
@@ -127,7 +131,7 @@ class Tokenize:
# this is for older RTF
#line = re.sub(self.__par_exp, '\\par ', line)
#return filter(lambda x: len(x) > 0, \
- #(self.__remove_line.sub('', x) for x in tokens))
+ #(self.__remove_line.sub('', x) for x in tokens))
def __compile_expressions(self):
SIMPLE_RPL = {
@@ -153,8 +157,6 @@ class Tokenize:
# put a backslash in front of to eliminate special cases and
# make processing easier
"}": "\\}",
- # this is for older RTF
- r'\\$': '\\par ',
}
self.__replace_spchar = MReplace(SIMPLE_RPL)
#add ;? in case of char following \u
@@ -168,10 +170,12 @@ class Tokenize:
#why keep backslash whereas \is replaced before?
#remove \n from endline char
self.__splitexp = re.compile(r"(\\[{}]|\n|\\[^\s\\{}&]+(?:[ \t\r\f\v])?)")
+ #this is for old RTF
+ self.__par_exp = re.compile(r'\\\n+')
+ # self.__par_exp = re.compile(r'\\$')
#self.__bin_exp = re.compile(r"\\bin(-?\d{1,8}) {0,1}")
#self.__utf_exp = re.compile(r"^\\u(-?\d{3,6})")
#self.__splitexp = re.compile(r"(\\[\\{}]|{|}|\n|\\[^\s\\{}&]+(?:\s)?)")
- #self.__par_exp = re.compile(r'\\$')
#self.__remove_line = re.compile(r'\n+')
#self.__mixed_exp = re.compile(r"(\\[a-zA-Z]+\d+)(\D+)")
##self.num_exp = re.compile(r"(\*|:|[a-zA-Z]+)(.*)")
@@ -199,7 +203,24 @@ class Tokenize:
copy_obj = copy.Copy(bug_handler = self.__bug_handler)
if self.__copy:
copy_obj.copy_file(self.__write_to, "tokenize.data")
+ # if self.__out_file:
+ # self.__file = self.__out_file
copy_obj.rename(self.__write_to, self.__file)
os.remove(self.__write_to)
- #self.__special_tokens = [ '_', '~', "'", '{', '}' ]
\ No newline at end of file
+ #self.__special_tokens = [ '_', '~', "'", '{', '}' ]
+
+# import sys
+# def main(args=sys.argv):
+ # if len(args) < 1:
+ # print 'No file'
+ # return
+ # file = 'data_tokens.txt'
+ # if len(args) == 3:
+ # file = args[2]
+ # to = Tokenize(args[1], Exception, out_file = file)
+ # to.tokenize()
+
+
+# if __name__ == '__main__':
+ # sys.exit(main())
\ No newline at end of file
diff --git a/src/calibre/gui2/__init__.py b/src/calibre/gui2/__init__.py
index e699551150..6a9becee50 100644
--- a/src/calibre/gui2/__init__.py
+++ b/src/calibre/gui2/__init__.py
@@ -505,7 +505,7 @@ class FileDialog(QObject):
self.selected_files = []
if mode == QFileDialog.AnyFile:
f = unicode(QFileDialog.getSaveFileName(parent, title, initial_dir, ftext, ""))
- if f and os.path.exists(f):
+ if f:
self.selected_files.append(f)
elif mode == QFileDialog.ExistingFile:
f = unicode(QFileDialog.getOpenFileName(parent, title, initial_dir, ftext, ""))
diff --git a/src/calibre/gui2/actions/catalog.py b/src/calibre/gui2/actions/catalog.py
index d75b0dfa5a..6d3bb539a2 100644
--- a/src/calibre/gui2/actions/catalog.py
+++ b/src/calibre/gui2/actions/catalog.py
@@ -28,7 +28,7 @@ class GenerateCatalogAction(InterfaceAction):
if not ids:
return error_dialog(self.gui, _('No books selected'),
- _('No books selected to generate catalog for'),
+ _('No books selected for catalog generation'),
show=True)
db = self.gui.library_view.model().db
@@ -55,9 +55,9 @@ class GenerateCatalogAction(InterfaceAction):
def catalog_generated(self, job):
if job.result:
- # Search terms nulled catalog results
- return error_dialog(self.gui, _('No books found'),
- _("No books to catalog\nCheck job details"),
+ # Error during catalog generation
+ return error_dialog(self.gui, _('Catalog generation terminated'),
+ job.result,
show=True)
if job.failed:
return self.gui.job_exception(job)
diff --git a/src/calibre/library/catalog.py b/src/calibre/library/catalog.py
index c045ccf686..087d40c4eb 100644
--- a/src/calibre/library/catalog.py
+++ b/src/calibre/library/catalog.py
@@ -1144,7 +1144,9 @@ class EPUB_MOBI(CatalogPlugin):
def error(self):
def fget(self):
return self.__error
- return property(fget=fget)
+ def fset(self, val):
+ self.__error = val
+ return property(fget=fget,fset=fset)
@dynamic_property
def generateForKindle(self):
def fget(self):
@@ -1411,6 +1413,88 @@ class EPUB_MOBI(CatalogPlugin):
except:
pass
+ def fetchBooksByAuthor(self):
+ '''
+ Generate a list of titles sorted by author from the database
+ return = Success
+ '''
+
+ self.updateProgressFullStep("Sorting database")
+
+ '''
+ # Sort titles case-insensitive, by author
+ self.booksByAuthor = sorted(self.booksByTitle,
+ key=lambda x:(x['author_sort'].upper(), x['author_sort'].upper()))
+ '''
+
+ self.booksByAuthor = list(self.booksByTitle)
+ self.booksByAuthor.sort(self.author_compare)
+
+ if False and self.verbose:
+ self.opts.log.info("fetchBooksByAuthor(): %d books" % len(self.booksByAuthor))
+ self.opts.log.info(" %-30s %-20s %s" % ('title', 'series', 'series_index'))
+ for title in self.booksByAuthor:
+ self.opts.log.info((u" %-30s %-20s%5s " % \
+ (title['title'][:30],
+ title['series'][:20] if title['series'] else '',
+ title['series_index'],
+ )).encode('utf-8'))
+ raise SystemExit
+
+ # Build the unique_authors set from existing data
+ authors = [(record['author'], record['author_sort'].capitalize()) for record in self.booksByAuthor]
+
+ # authors[] contains a list of all book authors, with multiple entries for multiple books by author
+ # authors[]: (([0]:friendly [1]:sort))
+ # unique_authors[]: (([0]:friendly [1]:sort [2]:book_count))
+ books_by_current_author = 0
+ current_author = authors[0]
+ multiple_authors = False
+ unique_authors = []
+ for (i,author) in enumerate(authors):
+ if author != current_author:
+ # Note that current_author and author are tuples: (friendly, sort)
+ multiple_authors = True
+
+ if author != current_author and i:
+ # Warn, exit if friendly matches previous, but sort doesn't
+ if author[0] == current_author[0]:
+ error_msg = _('''
+\n*** Metadata error ***
+Inconsistent Author Sort values for Author '{0}', unable to continue building catalog.
+Select all books by '{0}', apply correct Author Sort value in Edit Metadata dialog,
+then rebuild the catalog.\n''').format(author[0])
+
+ self.opts.log.warn(error_msg)
+ self.error = error_msg
+ return False
+
+ # New author, save the previous author/sort/count
+ unique_authors.append((current_author[0], icu_title(current_author[1]),
+ books_by_current_author))
+ current_author = author
+ books_by_current_author = 1
+ elif i==0 and len(authors) == 1:
+ # Allow for single-book lists
+ unique_authors.append((current_author[0], icu_title(current_author[1]),
+ books_by_current_author))
+ else:
+ books_by_current_author += 1
+ else:
+ # Add final author to list or single-author dataset
+ if (current_author == author and len(authors) > 1) or not multiple_authors:
+ unique_authors.append((current_author[0], icu_title(current_author[1]),
+ books_by_current_author))
+
+ if False and self.verbose:
+ self.opts.log.info("\nfetchBooksByauthor(): %d unique authors" % len(unique_authors))
+ for author in unique_authors:
+ self.opts.log.info((u" %-50s %-25s %2d" % (author[0][0:45], author[1][0:20],
+ author[2])).encode('utf-8'))
+
+ self.authors = unique_authors
+ return True
+
def fetchBooksByTitle(self):
self.updateProgressFullStep("Fetching database")
@@ -1562,90 +1646,9 @@ class EPUB_MOBI(CatalogPlugin):
title['title_sort'][0:40])).decode('mac-roman'))
return True
else:
+ self.error = _("No books found to catalog.\nCheck 'Excluded books' criteria in E-book options.")
return False
- def fetchBooksByAuthor(self):
- '''
- Generate a list of titles sorted by author from the database
- return = Success
- '''
-
- self.updateProgressFullStep("Sorting database")
-
- '''
- # Sort titles case-insensitive, by author
- self.booksByAuthor = sorted(self.booksByTitle,
- key=lambda x:(x['author_sort'].upper(), x['author_sort'].upper()))
- '''
-
- self.booksByAuthor = list(self.booksByTitle)
- self.booksByAuthor.sort(self.author_compare)
-
- if False and self.verbose:
- self.opts.log.info("fetchBooksByAuthor(): %d books" % len(self.booksByAuthor))
- self.opts.log.info(" %-30s %-20s %s" % ('title', 'series', 'series_index'))
- for title in self.booksByAuthor:
- self.opts.log.info((u" %-30s %-20s%5s " % \
- (title['title'][:30],
- title['series'][:20] if title['series'] else '',
- title['series_index'],
- )).encode('utf-8'))
- raise SystemExit
-
- # Build the unique_authors set from existing data
- authors = [(record['author'], record['author_sort'].capitalize()) for record in self.booksByAuthor]
-
- # authors[] contains a list of all book authors, with multiple entries for multiple books by author
- # authors[]: (([0]:friendly [1]:sort))
- # unique_authors[]: (([0]:friendly [1]:sort [2]:book_count))
- books_by_current_author = 0
- current_author = authors[0]
- multiple_authors = False
- unique_authors = []
- for (i,author) in enumerate(authors):
- if author != current_author:
- # Note that current_author and author are tuples: (friendly, sort)
- multiple_authors = True
-
- if author != current_author and i:
- # Warn, exit if friendly matches previous, but sort doesn't
- if author[0] == current_author[0]:
- error_msg = _('''
-\n*** Metadata error ***
-Inconsistent Author Sort values for Author '{0}', unable to continue building catalog.
-Select all books by '{0}', apply correct Author Sort value in Edit Metadata dialog,
-then rebuild the catalog.
-*** Terminating catalog generation ***\n''').format(author[0])
-
- self.opts.log.warn(error_msg)
- return False
-
- # New author, save the previous author/sort/count
- unique_authors.append((current_author[0], icu_title(current_author[1]),
- books_by_current_author))
- current_author = author
- books_by_current_author = 1
- elif i==0 and len(authors) == 1:
- # Allow for single-book lists
- unique_authors.append((current_author[0], icu_title(current_author[1]),
- books_by_current_author))
- else:
- books_by_current_author += 1
- else:
- # Add final author to list or single-author dataset
- if (current_author == author and len(authors) > 1) or not multiple_authors:
- unique_authors.append((current_author[0], icu_title(current_author[1]),
- books_by_current_author))
-
- if False and self.verbose:
- self.opts.log.info("\nfetchBooksByauthor(): %d unique authors" % len(unique_authors))
- for author in unique_authors:
- self.opts.log.info((u" %-50s %-25s %2d" % (author[0][0:45], author[1][0:20],
- author[2])).encode('utf-8'))
-
- self.authors = unique_authors
- return True
-
def fetchBookmarks(self):
'''
Collect bookmarks for catalog entries
@@ -5069,6 +5072,8 @@ then rebuild the catalog.
abort_after_input_dump=False)
plumber.merge_ui_recommendations(recommendations)
plumber.run()
- return 0
+ # returns to gui2.actions.catalog:catalog_generated()
+ return None
else:
- return 1
+ # returns to gui2.actions.catalog:catalog_generated()
+ return catalog.error
diff --git a/src/calibre/library/cli.py b/src/calibre/library/cli.py
index 43891a64c4..31f5e73689 100644
--- a/src/calibre/library/cli.py
+++ b/src/calibre/library/cli.py
@@ -693,8 +693,12 @@ def command_catalog(args, dbpath):
}
with plugin:
- plugin.run(args[1], opts, get_db(dbpath, opts))
- return 0
+ ret = plugin.run(args[1], opts, get_db(dbpath, opts))
+ if ret is None:
+ ret = 0
+ else:
+ ret = 1
+ return ret
# end of GR additions
diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py
index df094347b8..33593e93fe 100644
--- a/src/calibre/library/database2.py
+++ b/src/calibre/library/database2.py
@@ -690,10 +690,10 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
mi = Metadata(None)
aut_list = row[fm['au_map']]
- if not aut_list:
- aut_list = []
+ if aut_list:
+ aut_list = [p.split(':::') for p in aut_list.split(':#:') if p]
else:
- aut_list = [p.split(':::') for p in aut_list.split(':#:')]
+ aut_list = []
aum = []
aus = {}
for (author, author_sort) in aut_list:
diff --git a/src/calibre/manual/faq.rst b/src/calibre/manual/faq.rst
index 2c0d2a6173..0e8c101620 100644
--- a/src/calibre/manual/faq.rst
+++ b/src/calibre/manual/faq.rst
@@ -437,6 +437,15 @@ My antivirus program claims |app| is a virus/trojan?
Your antivirus program is wrong. |app| is a completely open source product. You can actually browse the source code yourself (or hire someone to do it for you) to verify that it is not a virus. Please report the false identification to whatever company you buy your antivirus software from. If the antivirus program is preventing you from downloading/installing |app|, disable it temporarily, install |app| and then re-enable it.
+How do I backup |app|?
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The most important thing to backup is the |app| library folder, that contains all your books and metadata. This is the folder you chose for your |app| library when you ran |app| for the first time. You can get the path to the library folder by clicking the |app| icon on the main toolbar. You must backup this complete folder with all its files and sub-folders.
+
+You can switch |app| to using a backed up library folder by simply clicking the |app| icon on the toolbar and choosing your backup library folder.
+
+If you want to backup the |app| configuration/plugins, you have to backup the config directory. You can find this config directory via :guilabel:`Preferences->Miscellaneous`. Note that restoring configuration directories is not officially supported, but should work in most cases. Just copy the contents of the backup directory into the current configuration directory to restore.
+
How do I use purchased EPUB books with |app|?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Most purchased EPUB books have `DRM `_. This prevents |app| from opening them. You can still use |app| to store and transfer them to your e-book reader. First, you must authorize your reader on a windows machine with Adobe Digital Editions. Once this is done, EPUB books transferred with |app| will work fine on your reader. When you purchase an epub book from a website, you will get an ".acsm" file. This file should be opened with Adobe Digital Editions, which will then download the actual ".epub" e-book. The e-book file will be stored in the folder "My Digital Editions", from where you can add it to |app|.