From 2a50706faba9e53f44d06af3ec2ad4d1d7d44def Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Thu, 12 Aug 2010 12:02:49 +0100 Subject: [PATCH 1/7] Ensure case changes are taken into account for folders in case-insensitive file systems --- src/calibre/library/database2.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index 6a259a655f..b82d7ef08b 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -433,6 +433,27 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): parent = os.path.dirname(spath) if len(os.listdir(parent)) == 0: self.rmtree(parent, permanent=True) + curpath = self.library_path + if not self.is_case_sensitive: + # On case-insensitive systems, title and author renames that only + # change case don't cause any changes to the directories in the file + # system. This can lead to having the directory names not match the + # title/author, which leads to trouble when libraries are copied to + # a case-sensitive system. The following code fixes this by checking + # each segment. If they are different (must be because of case), + # then rename the segment to some temp file name, then rename it + # back to the correct name. Note that the code above correctly + # handles files in the directories, so no need to do them here. + for oldseg,newseg in zip(current_path.split('/'), path.split('/')): + if oldseg != newseg: + while True: + # need a temp name in the current segment for renames + tempname = 'TEMP.%f'%time.time() + if not os.path.exists(os.path.join(curpath, tempname)): + break + os.rename(os.path.join(curpath, oldseg), tempname) + os.rename(tempname, os.path.join(curpath, newseg)) + curpath = os.path.join(curpath, newseg) def add_listener(self, listener): ''' From 10554d0d4dc8c50e04da45666e2fe1c83a4f1289 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Thu, 12 Aug 2010 12:17:44 +0100 Subject: [PATCH 2/7] Real name changes (not simplly change of case) raised an exception. Fix that. --- src/calibre/library/database2.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index b82d7ef08b..7c630a4043 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -440,12 +440,12 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): # system. This can lead to having the directory names not match the # title/author, which leads to trouble when libraries are copied to # a case-sensitive system. The following code fixes this by checking - # each segment. If they are different (must be because of case), - # then rename the segment to some temp file name, then rename it - # back to the correct name. Note that the code above correctly - # handles files in the directories, so no need to do them here. + # each segment. If they are different because of case, then rename + # the segment to some temp file name, then rename it back to the + # correct name. Note that the code above correctly handles files in + # the directories, so no need to do them here. for oldseg,newseg in zip(current_path.split('/'), path.split('/')): - if oldseg != newseg: + if oldseg.lower() == newseg.lower() and oldseg != newseg: while True: # need a temp name in the current segment for renames tempname = 'TEMP.%f'%time.time() From 6eade64d807b9af1f254585eccd560eb10f68a5d Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 12 Aug 2010 10:57:47 -0600 Subject: [PATCH 3/7] Fix unicode handling when editing tweaks --- src/calibre/gui2/dialogs/config/__init__.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/calibre/gui2/dialogs/config/__init__.py b/src/calibre/gui2/dialogs/config/__init__.py index 0f66ed5dd4..3073a54774 100644 --- a/src/calibre/gui2/dialogs/config/__init__.py +++ b/src/calibre/gui2/dialogs/config/__init__.py @@ -515,15 +515,15 @@ class ConfigDialog(ResizableDialog, Ui_Dialog): self.reset_confirmation_button.clicked.connect(self.reset_confirmation) deft, curt = read_raw_tweaks() - self.current_tweaks.setPlainText(curt) - self.default_tweaks.setPlainText(deft) + self.current_tweaks.setPlainText(curt.decode('utf-8')) + self.default_tweaks.setPlainText(deft.decode('utf-8')) self.restore_tweaks_to_default_button.clicked.connect(self.restore_tweaks_to_default) self.category_view.setCurrentIndex(self.category_view.model().index_for_name(initial_category)) def restore_tweaks_to_default(self, *args): deft, curt = read_raw_tweaks() - self.current_tweaks.setPlainText(deft) + self.current_tweaks.setPlainText(deft.decode('utf-8')) def reset_confirmation(self): @@ -698,8 +698,7 @@ class ConfigDialog(ResizableDialog, Ui_Dialog): self.input_order.setCurrentRow(idx-1) def set_tweaks(self): - raw = unicode(self.current_tweaks.toPlainText()) - raw = re.sub(r'(?m)^#.*fileencoding.*', '# ', raw) + raw = unicode(self.current_tweaks.toPlainText()).encode('utf-8') try: exec raw except: From 75a0ac806e1d7e797f348e0c2b527784218f6d89 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 12 Aug 2010 11:25:54 -0600 Subject: [PATCH 4/7] Fix #6480 (Esquire receipe returning blank articles) --- resources/recipes/esquire.recipe | 39 ++++++++++---------------------- 1 file changed, 12 insertions(+), 27 deletions(-) diff --git a/resources/recipes/esquire.recipe b/resources/recipes/esquire.recipe index 96338d0b48..d9c189b473 100644 --- a/resources/recipes/esquire.recipe +++ b/resources/recipes/esquire.recipe @@ -1,7 +1,5 @@ -#!/usr/bin/env python - __license__ = 'GPL v3' -__copyright__ = '2009, Darko Miletic ' +__copyright__ = '2009-2010, Darko Miletic ' ''' www.esquire.com @@ -9,7 +7,6 @@ www.esquire.com from calibre import strftime from calibre.web.feeds.news import BasicNewsRecipe -from calibre.ebooks.BeautifulSoup import Tag class Esquire(BasicNewsRecipe): title = 'Esquire' @@ -22,23 +19,21 @@ class Esquire(BasicNewsRecipe): no_stylesheets = True encoding = 'cp1250' use_embedded_content = False - language = 'en' - - lang = 'en-US' - cover_url = strftime('http://www.esquire.com/cm/esquire/cover-images/%Y_') + strftime('%m').strip('0') + '.jpg' + language = 'en' + publication_type = 'magazine' + masthead_url = 'http://www.esquire.com/cm/shared/site_images/print_this/esquire_logo.gif' conversion_options = { - 'comment' : description - , 'tags' : category - , 'publisher' : publisher - , 'language' : lang - , 'pretty_print' : True + 'comment' : description + , 'tags' : category + , 'publisher' : publisher + , 'language' : language } - keep_only_tags = [dict(name='div', attrs={'id':'content'})] - - remove_tags = [dict(name=['object','link','embed','iframe'])] - + keep_only_tags = [dict(name='div', attrs={'id':['article_header','article_content']})] + remove_tags = [dict(name=['object','link','embed','iframe','base'])] + remove_attributes = ['width','height'] + feeds = [ (u'Style' , u'http://www.esquire.com/style/rss/' ) ,(u'Women' , u'http://www.esquire.com/women/rss/' ) @@ -47,17 +42,7 @@ class Esquire(BasicNewsRecipe): ,(u'Frontpage', u'http://www.esquire.com/rss/' ) ] - - def print_version(self, url): - rest = url.rpartition('?')[0] - article = rest.rpartition('/')[2] - return 'http://www.esquire.com/print-this/' + article - def preprocess_html(self, soup): - soup.html['xml:lang'] = self.lang - soup.html['lang'] = self.lang - mlang = Tag(soup,'meta',[("http-equiv","Content-Language"),("content",self.lang)]) - soup.head.insert(0,mlang) for item in soup.findAll(style=True): del item['style'] return soup From 32e296dd2dd2fe20a021b792bed3580402e6285f Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 12 Aug 2010 13:17:48 -0600 Subject: [PATCH 5/7] Financial Express by DM --- resources/images/news/fe_india.png | Bin 0 -> 370 bytes resources/recipes/fe_india.recipe | 46 +++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 resources/images/news/fe_india.png create mode 100644 resources/recipes/fe_india.recipe diff --git a/resources/images/news/fe_india.png b/resources/images/news/fe_india.png new file mode 100644 index 0000000000000000000000000000000000000000..759f2594cd44a36bbe353b7116f11b45cdabd411 GIT binary patch literal 370 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b zK-vS0-A-oPfdtD69Mgd`SU*F|v9*VRoY|f(jv*GOdxH-O9#G)mUYC-dq+WJu=9|N} zl$6ZM?YImD7G|h-w7hKLxXyY-DoEfI^WP_TqBQM}_CB-W?sBH1Orc6mTme6)7-rxVsncbnGk%Z}w1U_2w;Xs^`2HP7)#{374m!8Nlu zKU*KtH?zuCqqdpi#9@|YOU{_%dr4ju;cUC%bhwR1f=2y~`uiEBhjN@7W> zRdP`(kYX@0Ff`CLu+TL$3NbXcGBmR?HqkXOvobIcs}xK@(U6;;l9^VCqQTV4$k58r W2%=#wm!%a@1B0ilpUXO@geCw{iF)(^ literal 0 HcmV?d00001 diff --git a/resources/recipes/fe_india.recipe b/resources/recipes/fe_india.recipe new file mode 100644 index 0000000000..7b5f380bdf --- /dev/null +++ b/resources/recipes/fe_india.recipe @@ -0,0 +1,46 @@ +__license__ = 'GPL v3' +__copyright__ = '2010, Darko Miletic ' +''' +financialexpress.com +''' + +from calibre.web.feeds.news import BasicNewsRecipe + +class FE_India(BasicNewsRecipe): + title = 'The Financial Express' + __author__ = 'Darko Miletic' + description = 'Financial news from India' + publisher = 'The Indian Express Limited' + category = 'news, politics, finances, India' + oldest_article = 30 + max_articles_per_feed = 200 + no_stylesheets = True + encoding = 'cp1252' + use_embedded_content = False + language = 'en_IN' + remove_empty_feeds = True + masthead_url = 'http://static.expressindia.com/frontend/fe/images/fe_logo.jpg' + publication_type = 'magazine' + extra_css = ' body{font-family: Arial,Helvetica,sans-serif } ' + + conversion_options = { + 'comment' : description + , 'tags' : category + , 'publisher' : publisher + , 'language' : language + } + + keep_only_tags = [dict(attrs={'class':'txt'})] + remove_attributes = ['width','height'] + + feeds = [(u'Articles', u'http://www.expressindia.com/syndications/fe.xml')] + + def print_version(self, url): + article_raw = url.rpartition('/')[0] + article_id = article_raw.rpartition('/')[2] + return 'http://www.financialexpress.com/printer/news/' + article_id + '/' + + def preprocess_html(self, soup): + for item in soup.findAll(style=True): + del item['style'] + return soup From cadd1b6c111b4b8e9233d9c63b25d14f3e2e953e Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Fri, 13 Aug 2010 04:38:49 +0100 Subject: [PATCH 6/7] fix bug #6487 --- src/calibre/gui2/dialogs/config/create_custom_column.py | 8 +++----- src/calibre/library/custom_columns.py | 6 +++++- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/calibre/gui2/dialogs/config/create_custom_column.py b/src/calibre/gui2/dialogs/config/create_custom_column.py index 34091d893f..fdf093b6d5 100644 --- a/src/calibre/gui2/dialogs/config/create_custom_column.py +++ b/src/calibre/gui2/dialogs/config/create_custom_column.py @@ -100,11 +100,11 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn): def accept(self): - col = unicode(self.column_name_box.text()).lower() + col = unicode(self.column_name_box.text()) if not col: return self.simple_error('', _('No lookup name was provided')) - if re.match('^\w*$', col) is None or not col[0].isalpha(): - return self.simple_error('', _('The label must contain only letters, digits and underscores, and start with a letter')) + if re.match('^\w*$', col) is None or not col[0].isalpha() or col.lower() != col: + return self.simple_error('', _('The lookup name must contain only lower case letters, digits and underscores, and start with a letter')) col_heading = unicode(self.column_heading_box.text()) col_type = self.column_types[self.column_type_box.currentIndex()]['datatype'] if col_type == '*text': @@ -130,8 +130,6 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn): bad_head = True if bad_head: return self.simple_error('', _('The heading %s is already used')%col_heading) - if ':' in col or ' ' in col or col.lower() != col: - return self.simple_error('', _('The lookup name must be lower case and cannot contain ":"s or spaces')) date_format = {} if col_type == 'datetime': diff --git a/src/calibre/library/custom_columns.py b/src/calibre/library/custom_columns.py index 5b459c6d2a..b8e0f8d3b6 100644 --- a/src/calibre/library/custom_columns.py +++ b/src/calibre/library/custom_columns.py @@ -6,7 +6,7 @@ __license__ = 'GPL v3' __copyright__ = '2010, Kovid Goyal ' __docformat__ = 'restructuredtext en' -import json +import json, re from functools import partial from math import floor @@ -430,6 +430,10 @@ class CustomColumns(object): def create_custom_column(self, label, name, datatype, is_multiple, editable=True, display={}): + if not label: + raise ValueError(_('No label was provided')) + if re.match('^\w*$', label) is None or not label[0].isalpha() or label.lower() != label: + raise ValueError(_('The label must contain only lower case letters, digits and underscores, and start with a letter')) if datatype not in self.CUSTOM_DATA_TYPES: raise ValueError('%r is not a supported data type'%datatype) normalized = datatype not in ('datetime', 'comments', 'int', 'bool', From a558775b2ea3ec2a7038aee477b73165a47ce1db Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 13 Aug 2010 00:02:23 -0600 Subject: [PATCH 7/7] Set screen size to 540x780 in Kobo output profile --- src/calibre/customize/profiles.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/customize/profiles.py b/src/calibre/customize/profiles.py index 85eae21b8d..1563f764ca 100644 --- a/src/calibre/customize/profiles.py +++ b/src/calibre/customize/profiles.py @@ -426,7 +426,7 @@ class KoboReaderOutput(OutputProfile): description = _('This profile is intended for the Kobo Reader.') - screen_size = (590, 775) + screen_size = (540, 718) comic_screen_size = (540, 718) dpi = 168.451 fbase = 12