diff --git a/recipes/cosmopolitan_uk.recipe b/recipes/cosmopolitan_uk.recipe new file mode 100644 index 0000000000..21317063ab --- /dev/null +++ b/recipes/cosmopolitan_uk.recipe @@ -0,0 +1,51 @@ +import re +from calibre.web.feeds.news import BasicNewsRecipe +#from calibre import __appname__ +from calibre.utils.magick import Image +class AdvancedUserRecipe1306097511(BasicNewsRecipe): + title = u'Cosmopolitan UK' + description = 'Fashion, beauty and Gossip for women from COSMOPOLITAN -UK' + + __author__ = 'Dave Asbury' + # greyscale code by Starson + cover_url = 'http://www.cosmopolitan.magazine.co.uk/files/4613/2085/8988/Cosmo_Cover3.jpg' + no_stylesheets = True + oldest_article = 7 + max_articles_per_feed = 20 + remove_empty_feeds = True + remove_javascript = True + + preprocess_regexps = [ + (re.compile(r'.*?', re.IGNORECASE | re.DOTALL), lambda match: '')] + language = 'en_GB' + + + masthead_url = 'http://www.cosmopolitan.co.uk/cm/cosmopolitanuk/site_images/header/cosmouk_logo_home.gif' + + + keep_only_tags = [ + dict(attrs={'class' : ['dateAuthor', 'publishDate']}), + dict(name='div',attrs ={'id' : ['main_content']}) + ] + remove_tags = [ + dict(name='div',attrs={'class' : ['blogInfo','viral_toolbar','comment_number','prevEntry nav']}), + dict(name='div',attrs={'class' : 'blog_module_about_the_authors'}), + dict(attrs={'id': ['breadcrumbs','comment','related_links_list','right_rail','content_sec_fb_more','content_sec_mostpopularstories','content-sec_fb_frame_viewfb_bot']}), + dict(attrs={'class' : ['read_liked_that_header','fb_back_next_area']}) + ] + + feeds = [ + (u'Love & Sex', u'http://www.cosmopolitan.co.uk/love-sex/rss/'), (u'Men', u'http://cosmopolitan.co.uk/men/rss/'), (u'Fashion', u'http://cosmopolitan.co.uk/fashion/rss/'), (u'Hair & Beauty', u'http://cosmopolitan.co.uk/beauty-hair/rss/'), (u'LifeStyle', u'http://cosmopolitan.co.uk/lifestyle/rss/'), (u'Cosmo On Campus', u'http://cosmopolitan.co.uk/campus/rss/'), (u'Celebrity Gossip', u'http://cosmopolitan.co.uk/celebrity-gossip/rss/')] + + def postprocess_html(self, soup, first): + #process all the images + for tag in soup.findAll(lambda tag: tag.name.lower()=='img' and tag.has_key('src')): + iurl = tag['src'] + img = Image() + img.open(iurl) + if img < 0: + raise RuntimeError('Out of memory') + img.type = "GrayscaleType" + img.save(iurl) + return soup + diff --git a/recipes/daily_writing_tips.recipe b/recipes/daily_writing_tips.recipe new file mode 100644 index 0000000000..836f8ec4d1 --- /dev/null +++ b/recipes/daily_writing_tips.recipe @@ -0,0 +1,18 @@ +from calibre.web.feeds.news import BasicNewsRecipe + +class DailyWritingTips(BasicNewsRecipe): + title = u'Daily Writing Tips' + language = 'en_GB' + __author__ = 'NotTaken' + oldest_article = 7 #days + max_articles_per_feed = 40 + use_embedded_content = True + no_stylesheets = True + auto_cleanup = False + encoding = 'utf-8' + + + feeds = [ +('Latest tips', + 'http://feeds2.feedburner.com/DailyWritingTips'), +] diff --git a/recipes/icons/skylife.png b/recipes/icons/skylife.png new file mode 100644 index 0000000000..6cfcf5c797 Binary files /dev/null and b/recipes/icons/skylife.png differ diff --git a/recipes/rstones.recipe b/recipes/rstones.recipe index fa09701e15..46b817876c 100644 --- a/recipes/rstones.recipe +++ b/recipes/rstones.recipe @@ -29,22 +29,7 @@ class RollingStones(BasicNewsRecipe): max_articles_per_feed = 25 use_embedded_content = False no_stylesheets = True - - remove_javascript = True - ##################################################################################### - # cleanup section # - ##################################################################################### - keep_only_tags = [ - dict(name='div', attrs={'class':['c65l']}), - dict(name='div', attrs={'id':['col1']}), - - - ] - remove_tags = [ - dict(name='div', attrs={'class': ['storyActions upper','storyActions lowerArticleNav']}), - dict(name='div', attrs={'id': ['comments','related']}), - ] - + auto_cleanup = True feeds = [ (u'News', u'http://www.rollingstone.com/siteServices/rss/allNews'), @@ -58,25 +43,7 @@ class RollingStones(BasicNewsRecipe): - def get_article_url(self, article): - return article.get('guid', None) - - - def append_page(self, soup, appendtag, position): - ''' - Some are the articles are multipage so the below function - will get the articles that have - ''' - pager = soup.find('li',attrs={'class':'next'}) - if pager: - nexturl = pager.a['href'] - soup2 = self.index_to_soup(nexturl) - texttag = soup2.find('div', attrs={'id':'storyTextContainer'}) - for it in texttag.findAll(style=True): - del it['style'] - newpos = len(texttag.contents) - self.append_page(soup2,texttag,newpos) - texttag.extract() - appendtag.insert(position,texttag) + def print_version(self, url): + return url +'?print=true' diff --git a/recipes/skylife.recipe b/recipes/skylife.recipe new file mode 100644 index 0000000000..7abd2f1f42 --- /dev/null +++ b/recipes/skylife.recipe @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- + +from calibre.web.feeds.news import BasicNewsRecipe + +class THY (BasicNewsRecipe): + + title = u'Skylife' + __author__ = u'thomass' + description = ' Türk Hava Yollarının yayınladığı aylık kültür dergisi (Fotoğrafları da içermesini isterseniz keep_only_tag''da belirttiğim kodu da ekleyin) ' + oldest_article =32 + max_articles_per_feed =100 + no_stylesheets = True + #delay = 1 + #use_embedded_content = False + encoding = 'utf-8' + publisher = 'thomass' + category = 'genel kültür, gezi,Türkçe' + language = 'tr' + publication_type = 'magazine' + + conversion_options = { + 'comment' : description + , 'tags' : category + , 'publisher' : publisher + , 'language' : language + } + keep_only_tags = [dict(name='h3', attrs={'id':['hpbaslik']}),dict(name='p', attrs={'id':['pyayin','hspot','picerik']})] #Fotoğrafları da eklemek için: dict(name='div', attrs={'id':['divResimler']}) + masthead_url = 'http://www.turkishairlines.com/static/img/skylife/logo.png' + remove_empty_feeds= True + remove_attributes = ['width','height'] + + feeds = [( u'SKYLIFE', u'http://feed43.com/7783278414103376.xml')] diff --git a/recipes/techdirt.recipe b/recipes/techdirt.recipe new file mode 100644 index 0000000000..a936e1a216 --- /dev/null +++ b/recipes/techdirt.recipe @@ -0,0 +1,20 @@ +from calibre.web.feeds.news import BasicNewsRecipe + +class TechDirt(BasicNewsRecipe): + title = u'Tech Dirt' + language = 'en' + __author__ = 'Krittika Goyal' + oldest_article = 7 #days + max_articles_per_feed = 25 + use_embedded_content = False + + no_stylesheets = True + auto_cleanup = True + encoding = 'latin1' + + + feeds = [ +('News', + 'http://feeds.feedburner.com/techdirt/feed'), +] + diff --git a/resources/default_tweaks.py b/resources/default_tweaks.py index 224038b0f9..972552ad09 100644 --- a/resources/default_tweaks.py +++ b/resources/default_tweaks.py @@ -219,7 +219,7 @@ per_language_title_sort_articles = { r'Una\s+', r'Unos\s+', r'Unas\s+'), # French 'fra' : (r'Le\s+', r'La\s+', r"L'", r'Les\s+', r'Un\s+', r'Une\s+', - r'Des\s+'), + r'Des\s+', r'De\s+La\s+', r'De\s+', r"D'"), # Italian 'ita' : (r'Lo\s+', r'Il\s+', r"L'", r'La\s+', r'Gli\s+', r'I\s+', r'Le\s+', ), @@ -230,7 +230,8 @@ per_language_title_sort_articles = { 'ron' : (r'Un\s+', r'O\s+', r'Nişte\s+', ), # German 'deu' : (r'Der\s+', r'Die\s+', r'Das\s+', r'Den\s+', r'Ein\s+', - r'Eine\s+', r'Einen\s+', ), + r'Eine\s+', r'Einen\s+', r'Dem\s+', r'Des\s+', r'Einem\s+', + r'Eines\s+'), # Dutch 'nld' : (r'De\s+', r'Het\s+', r'Een\s+', r"'n\s+", r"'s\s+", r'Ene\s+', r'Ener\s+', r'Enes\s+', r'Den\s+', r'Der\s+', r'Des\s+', diff --git a/src/calibre/customize/__init__.py b/src/calibre/customize/__init__.py index d087eb5351..e4f8bf99bf 100644 --- a/src/calibre/customize/__init__.py +++ b/src/calibre/customize/__init__.py @@ -449,7 +449,7 @@ class CatalogPlugin(Plugin): # {{{ ['author_sort','authors','comments','cover','formats', 'id','isbn','ondevice','pubdate','publisher','rating', 'series_index','series','size','tags','timestamp', - 'title_sort','title','uuid']) + 'title_sort','title','uuid','languages']) all_custom_fields = set(db.custom_field_keys()) all_fields = all_std_fields.union(all_custom_fields) diff --git a/src/calibre/devices/android/driver.py b/src/calibre/devices/android/driver.py index 0ad36a460a..f3aa465476 100644 --- a/src/calibre/devices/android/driver.py +++ b/src/calibre/devices/android/driver.py @@ -167,12 +167,12 @@ class ANDROID(USBMS): 'MB525', 'ANDROID2.3', 'SGH-I997', 'GT-I5800_CARD', 'MB612', 'GT-S5830_CARD', 'GT-S5570_CARD', 'MB870', 'MID7015A', 'ALPANDIGITAL', 'ANDROID_MID', 'VTAB1008', 'EMX51_BBG_ANDROI', - 'UMS', '.K080', 'P990', 'LTE'] + 'UMS', '.K080', 'P990', 'LTE', 'MB853'] WINDOWS_CARD_A_MEM = ['ANDROID_PHONE', 'GT-I9000_CARD', 'SGH-I897', 'FILE-STOR_GADGET', 'SGH-T959', 'SAMSUNG_ANDROID', 'GT-P1000_CARD', 'A70S', 'A101IT', '7', 'INCREDIBLE', 'A7EB', 'SGH-T849_CARD', '__UMS_COMPOSITE', 'SGH-I997_CARD', 'MB870', 'ALPANDIGITAL', - 'ANDROID_MID', 'P990_SD_CARD', '.K080', 'LTE_CARD'] + 'ANDROID_MID', 'P990_SD_CARD', '.K080', 'LTE_CARD', 'MB853'] OSX_MAIN_MEM = 'Android Device Main Memory' diff --git a/src/calibre/devices/apple/driver.py b/src/calibre/devices/apple/driver.py index d7fc6e791a..d6dd5ccf73 100644 --- a/src/calibre/devices/apple/driver.py +++ b/src/calibre/devices/apple/driver.py @@ -1306,7 +1306,8 @@ class ITUNES(DriverBase): if DEBUG: self.log.info(" ITUNES._add_new_copy()") - self._update_epub_metadata(fpath, metadata) + if fpath.rpartition('.')[2].lower() == 'epub': + self._update_epub_metadata(fpath, metadata) db_added = None lb_added = None diff --git a/src/calibre/devices/hanvon/driver.py b/src/calibre/devices/hanvon/driver.py index 2d6b825c37..fbee2565a0 100644 --- a/src/calibre/devices/hanvon/driver.py +++ b/src/calibre/devices/hanvon/driver.py @@ -175,7 +175,7 @@ class ODYSSEY(N516): FORMATS = ['epub', 'fb2', 'html', 'pdf', 'txt'] - EBOOK_DIR_MAIN = 'calibre' + EBOOK_DIR_MAIN = 'Digital Editions' def get_main_ebook_dir(self, for_upload=False): if for_upload: diff --git a/src/calibre/ebooks/epub/output.py b/src/calibre/ebooks/epub/output.py index bb515f95a4..52bbfb4a08 100644 --- a/src/calibre/ebooks/epub/output.py +++ b/src/calibre/ebooks/epub/output.py @@ -132,9 +132,11 @@ class EPUBOutput(OutputFormatPlugin): def upshift_markup(self): # {{{ 'Upgrade markup to comply with XHTML 1.1 where possible' - from calibre.ebooks.oeb.base import XPath + from calibre.ebooks.oeb.base import XPath, XML for x in self.oeb.spine: root = x.data + if (not root.get(XML('lang'))) and (root.get('lang')): + root.set(XML('lang'), root.get('lang')) body = XPath('//h:body')(root) if body: body = body[0] diff --git a/src/calibre/ebooks/metadata/sources/base.py b/src/calibre/ebooks/metadata/sources/base.py index 532c97b624..f04291eae9 100644 --- a/src/calibre/ebooks/metadata/sources/base.py +++ b/src/calibre/ebooks/metadata/sources/base.py @@ -121,7 +121,18 @@ def cap_author_token(token): # Normalize tokens of the form J.K. to J. K. parts = token.split('.') return '. '.join(map(capitalize, parts)).strip() + scots_name = None + for x in ('mc', 'mac'): + if (token.lower().startswith(x) and len(token) > len(x) and + ( + token[len(x)] == upper(token[len(x)]) or + lt == token + )): + scots_name = len(x) + break ans = capitalize(token) + if scots_name is not None: + ans = ans[:scots_name] + upper(ans[scots_name]) + ans[scots_name+1:] for x in ('-', "'"): idx = ans.find(x) if idx > -1 and len(ans) > idx+2: diff --git a/src/calibre/ebooks/metadata/sources/identify.py b/src/calibre/ebooks/metadata/sources/identify.py index 4987b8cead..be12aae800 100644 --- a/src/calibre/ebooks/metadata/sources/identify.py +++ b/src/calibre/ebooks/metadata/sources/identify.py @@ -305,7 +305,8 @@ class ISBNMerge(object): ans.pubdate = r.pubdate break if getattr(ans.pubdate, 'year', None) == min_year: - min_date = datetime(min_year, ans.pubdate.month, ans.pubdate.day) + min_date = datetime(min_year, ans.pubdate.month, ans.pubdate.day, + tzinfo=utc_tz) else: min_date = datetime(min_year, 1, 2, tzinfo=utc_tz) ans.pubdate = min_date diff --git a/src/calibre/gui2/dialogs/metadata_bulk.py b/src/calibre/gui2/dialogs/metadata_bulk.py index 84bf7f6f57..928be3843c 100644 --- a/src/calibre/gui2/dialogs/metadata_bulk.py +++ b/src/calibre/gui2/dialogs/metadata_bulk.py @@ -164,7 +164,14 @@ class MyBlockingBusy(QDialog): # {{{ self.db.set_title(id, titlecase(title), notify=False) if do_title_sort: title = self.db.title(id, index_is_id=True) - self.db.set_title_sort(id, title_sort(title), notify=False) + if languages: + lang = languages[0] + else: + lang = self.db.languages(id, index_is_id=True) + if lang: + lang = lang.partition(',')[0] + self.db.set_title_sort(id, title_sort(title, lang=lang), + notify=False) if au: self.db.set_authors(id, string_to_authors(au), notify=False) if cover_action == 'remove': diff --git a/src/calibre/gui2/duplicates.py b/src/calibre/gui2/duplicates.py index cc6da1e995..6e45b0e9e6 100644 --- a/src/calibre/gui2/duplicates.py +++ b/src/calibre/gui2/duplicates.py @@ -8,4 +8,3 @@ __copyright__ = '2011, Kovid Goyal ' __docformat__ = 'restructuredtext en' - diff --git a/src/calibre/gui2/metadata/single_download.py b/src/calibre/gui2/metadata/single_download.py index 70b32a78c6..1c0f3c8042 100644 --- a/src/calibre/gui2/metadata/single_download.py +++ b/src/calibre/gui2/metadata/single_download.py @@ -28,7 +28,8 @@ from calibre.ebooks.metadata.sources.identify import (identify, urls_from_identifiers) from calibre.ebooks.metadata.book.base import Metadata from calibre.gui2 import error_dialog, NONE -from calibre.utils.date import utcnow, fromordinal, format_date +from calibre.utils.date import (utcnow, fromordinal, format_date, + UNDEFINED_DATE, as_utc) from calibre.library.comments import comments_to_html from calibre import force_unicode # }}} @@ -201,7 +202,12 @@ class ResultsModel(QAbstractTableModel): # {{{ elif col == 1: key = attrgetter('title') elif col == 2: - key = attrgetter('pubdate') + def dategetter(x): + x = getattr(x, 'pubdate', None) + if x is None: + x = UNDEFINED_DATE + return as_utc(x) + key = dategetter elif col == 3: key = attrgetter('has_cached_cover_url') elif key == 4: diff --git a/src/calibre/gui2/store/stores/mobileread/mobileread_plugin.py b/src/calibre/gui2/store/stores/mobileread/mobileread_plugin.py index 1aabe86c50..9e41aa45a1 100644 --- a/src/calibre/gui2/store/stores/mobileread/mobileread_plugin.py +++ b/src/calibre/gui2/store/stores/mobileread/mobileread_plugin.py @@ -21,13 +21,14 @@ from calibre.gui2.store.stores.mobileread.cache_update_thread import CacheUpdate from calibre.gui2.store.stores.mobileread.store_dialog import MobileReadStoreDialog class MobileReadStore(BasicStoreConfig, StorePlugin): - - def genesis(self): + + def __init__(self, *args, **kwargs): + StorePlugin.__init__(self, *args, **kwargs) self.lock = Lock() - + def open(self, parent=None, detail_item=None, external=False): url = 'http://www.mobileread.com/' - + if external or self.config.get('open_external', False): open_url(QUrl(detail_item if detail_item else url)) else: @@ -44,7 +45,7 @@ class MobileReadStore(BasicStoreConfig, StorePlugin): def search(self, query, max_results=10, timeout=60): books = self.get_book_list() - + if not books: return @@ -56,24 +57,25 @@ class MobileReadStore(BasicStoreConfig, StorePlugin): book.drm = SearchResult.DRM_UNLOCKED yield book - def update_cache(self, parent=None, timeout=10, force=False, suppress_progress=False): + def update_cache(self, parent=None, timeout=10, force=False, + suppress_progress=False): if self.lock.acquire(False): try: update_thread = CacheUpdateThread(self.config, self.seralize_books, timeout) if not suppress_progress: progress = CacheProgressDialog(parent) progress.set_message(_('Updating MobileRead book cache...')) - + update_thread.total_changed.connect(progress.set_total) update_thread.update_progress.connect(progress.set_progress) update_thread.update_details.connect(progress.set_details) progress.rejected.connect(update_thread.abort) - + progress.open() update_thread.start() while update_thread.is_alive() and not progress.canceled: QCoreApplication.processEvents() - + if progress.isVisible(): progress.accept() return not progress.canceled @@ -84,7 +86,7 @@ class MobileReadStore(BasicStoreConfig, StorePlugin): def get_book_list(self): return self.deseralize_books(self.config.get('book_list', [])) - + def seralize_books(self, books): sbooks = [] for b in books: @@ -95,7 +97,7 @@ class MobileReadStore(BasicStoreConfig, StorePlugin): data['formats'] = b.formats sbooks.append(data) return sbooks - + def deseralize_books(self, sbooks): books = [] for s in sbooks: diff --git a/src/calibre/gui2/ui.py b/src/calibre/gui2/ui.py index c12a15829a..63db7a561c 100644 --- a/src/calibre/gui2/ui.py +++ b/src/calibre/gui2/ui.py @@ -195,7 +195,8 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{ for ac in self.iactions.values(): ac.do_genesis() - self.donate_action = QAction(QIcon(I('donate.png')), _('&Donate to support calibre'), self) + self.donate_action = QAction(QIcon(I('donate.png')), + _('&Donate to support calibre'), self) for st in self.istores.values(): st.do_genesis() MainWindowMixin.__init__(self, db) diff --git a/src/calibre/library/catalog.py b/src/calibre/library/catalog.py index 53432af7b7..a9c4b1a309 100644 --- a/src/calibre/library/catalog.py +++ b/src/calibre/library/catalog.py @@ -29,7 +29,8 @@ from calibre.utils.zipfile import ZipFile FIELDS = ['all', 'title', 'title_sort', 'author_sort', 'authors', 'comments', 'cover', 'formats','id', 'isbn', 'ondevice', 'pubdate', 'publisher', - 'rating', 'series_index', 'series', 'size', 'tags', 'timestamp', 'uuid'] + 'rating', 'series_index', 'series', 'size', 'tags', 'timestamp', + 'uuid', 'languages'] #Allowed fields for template TEMPLATE_ALLOWED_FIELDS = [ 'author_sort', 'authors', 'id', 'isbn', 'pubdate', 'title_sort', @@ -601,7 +602,7 @@ class BIBTEX(CatalogPlugin): # {{{ bibtexc, db, citation_bibtex, addfiles_bibtex)) # }}} -class EPUB_MOBI(CatalogPlugin): +class EPUB_MOBI(CatalogPlugin): # {{{ 'ePub catalog generator' Option = namedtuple('Option', 'option, default, dest, action, help') @@ -5177,3 +5178,4 @@ Author '{0}': # returns to gui2.actions.catalog:catalog_generated() return catalog.error +# }}} diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index 3782149512..f99830ca5a 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -3378,7 +3378,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): prefix = self.library_path FIELDS = set(['title', 'sort', 'authors', 'author_sort', 'publisher', 'rating', 'timestamp', 'size', 'tags', 'comments', 'series', 'series_index', - 'uuid', 'pubdate', 'last_modified', 'identifiers']) + 'uuid', 'pubdate', 'last_modified', 'identifiers', 'languages']) for x in self.custom_column_num_map: FIELDS.add(x) data = [] diff --git a/src/calibre/library/server/content.py b/src/calibre/library/server/content.py index 983dae8aae..dd3a964c27 100644 --- a/src/calibre/library/server/content.py +++ b/src/calibre/library/server/content.py @@ -195,14 +195,25 @@ class ContentServer(object): return data - def get_format(self, id, format): format = format.upper() + fm = self.db.format_metadata(id, format, allow_cache=False) + if not fm: + raise cherrypy.HTTPError(404, 'book: %d does not have format: %s'%(id, format)) + mi = newmi = self.db.get_metadata(id, index_is_id=True) + + cherrypy.response.headers['Last-Modified'] = \ + self.last_modified(max(fm['mtime'], mi.last_modified)) + fmt = self.db.format(id, format, index_is_id=True, as_file=True, mode='rb') if fmt is None: raise cherrypy.HTTPError(404, 'book: %d does not have format: %s'%(id, format)) - mi = newmi = self.db.get_metadata(id, index_is_id=True) + mt = guess_type('dummy.'+format.lower())[0] + if mt is None: + mt = 'application/octet-stream' + cherrypy.response.headers['Content-Type'] = mt + if format == 'EPUB': # Get the original metadata @@ -221,19 +232,19 @@ class ContentServer(object): set_metadata(fmt, newmi, format.lower()) fmt.seek(0) - mt = guess_type('dummy.'+format.lower())[0] - if mt is None: - mt = 'application/octet-stream' - au = authors_to_string(mi.authors if mi.authors else [_('Unknown')]) - title = mi.title if mi.title else _('Unknown') + fmt.seek(0, 2) + cherrypy.response.headers['Content-Length'] = fmt.tell() + fmt.seek(0) + + au = authors_to_string(newmi.authors if newmi.authors else + [_('Unknown')]) + title = newmi.title if newmi.title else _('Unknown') fname = u'%s - %s_%s.%s'%(title[:30], au[:30], id, format.lower()) fname = ascii_filename(fname).replace('"', '_') - cherrypy.response.headers['Content-Type'] = mt cherrypy.response.headers['Content-Disposition'] = \ b'attachment; filename="%s"'%fname + cherrypy.response.body = fmt cherrypy.response.timeout = 3600 - cherrypy.response.headers['Last-Modified'] = \ - self.last_modified(self.db.format_last_modified(id, format)) return fmt # }}} diff --git a/src/calibre/manual/faq.rst b/src/calibre/manual/faq.rst index 3700451b2d..b58b87cc5e 100644 --- a/src/calibre/manual/faq.rst +++ b/src/calibre/manual/faq.rst @@ -265,7 +265,7 @@ How do I use |app| with my Android phone/tablet? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ There are two ways that you can connect your Android device to calibre. Using a USB cable-- or wirelessly, over the air. -The USB cable method only works if your Android device can act as a USB disk, which some Android tablets cannot. +**The USB cable method only works if your Android device can act as a USB disk, that means in windows it must have a drive letter, like K:**. Using a USB cable ^^^^^^^^^^^^^^^^^^^^ diff --git a/src/calibre/web/feeds/recipes/collection.py b/src/calibre/web/feeds/recipes/collection.py index 6b9c3a2129..46d6c2db81 100644 --- a/src/calibre/web/feeds/recipes/collection.py +++ b/src/calibre/web/feeds/recipes/collection.py @@ -13,7 +13,7 @@ from datetime import timedelta from lxml import etree from lxml.builder import ElementMaker -from calibre import browser +from calibre import browser, force_unicode from calibre.utils.date import parse_date, now as nowf, utcnow, tzlocal, \ isoformat, fromordinal @@ -66,8 +66,9 @@ def serialize_collection(mapping_of_recipe_classes): x.title.decode('ascii') ''' for urn in sorted(mapping_of_recipe_classes.keys(), - key=lambda key: getattr(mapping_of_recipe_classes[key], 'title', - 'zzz')): + key=lambda key: force_unicode( + getattr(mapping_of_recipe_classes[key], 'title', 'zzz'), + 'utf-8')): recipe = serialize_recipe(urn, mapping_of_recipe_classes[urn]) collection.append(recipe) collection.set('count', str(len(collection)))