From e558634fb7ce4fe65b53bc122c5ab63685480090 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 19 May 2010 07:28:16 -0600 Subject: [PATCH 01/13] EMG by Darko Miletic. Fixes #5569 (New recipes for emg portal in Serbian and English) --- resources/images/news/emg_en.png | Bin 0 -> 682 bytes resources/images/news/emg_rs.recipe | Bin 0 -> 682 bytes resources/recipes/emg_en.recipe | 45 ++++++++++++++++++++++++ resources/recipes/emg_rs.recipe | 51 ++++++++++++++++++++++++++++ 4 files changed, 96 insertions(+) create mode 100644 resources/images/news/emg_en.png create mode 100644 resources/images/news/emg_rs.recipe create mode 100644 resources/recipes/emg_en.recipe create mode 100644 resources/recipes/emg_rs.recipe diff --git a/resources/images/news/emg_en.png b/resources/images/news/emg_en.png new file mode 100644 index 0000000000000000000000000000000000000000..74930f2b646cb9e8faec6a18bec0ab771ef51530 GIT binary patch literal 682 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b zK-vS0-A-oPfdtD69Mgd`SU*F|v9*U87#J^mx;TbdoIW~vZ+3{I$kF=pXKQzND+*1} z@Sc^>A)le*cqytYAyGLjs-smbjO`D%k7Aq46b>=2ZDL7^j%yw{`6OL9r`XfD*u&+k}%kD07uw&9MO9J9-AwKPNHGi#b($)ve)Fuu2) zRG|0t)Va@h-XsdD{GR%~PWHilmb7V1GbE$Ur|+z(nDG9r**Qz4_ZAnnx26eM>?@ac zzP;PPIO}fexxQ}~!@N$-JGskly_u?~`g@z{DsN}3$j(WNyct#X-?+ct^s$ZHytpaW zyLL~%x6UZpc>Rm_D?jC~J$h(n;JbZ4m<}9xBD4JcUX}-U{-*NXt2y>#@zlPK$D8wa zR%A?&_O}bttXw8;|A1$g_R^5{L;tlW9zXNpT#@dg+sD5#8kDA}U!8Qk!RyJ64 z@-{N3wOzdFKK+&U9Eo*>Sxb&Rm|9u2N-$p9@NC(wPB)nwB1=DJX&*e3aU_>vl7hsp zUlGici+37uzpu-9@@tZcrr)heOQQYlHT{~RJOln;X?moh;n#G|zC!nQeQsQ3CaZtS z-f3ITF}v!w2y}Fv{BPv7?fjWgm5=v?*B)hjzq#9fDof(E#BCGK&MweftHu83{pBCh zmr5VYTr4;aN;9e@t`Q|Ei6yC4$wjF^iowXh&_LI~RM*fl#K7Fj$kfWjNY}v3%D}+( q{{eLr4Y~O#nQ4_M8Z500O|1;gAQ~ofTEqf1FnGH9xvX^) literal 0 HcmV?d00001 diff --git a/resources/images/news/emg_rs.recipe b/resources/images/news/emg_rs.recipe new file mode 100644 index 0000000000000000000000000000000000000000..74930f2b646cb9e8faec6a18bec0ab771ef51530 GIT binary patch literal 682 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b zK-vS0-A-oPfdtD69Mgd`SU*F|v9*U87#J^mx;TbdoIW~vZ+3{I$kF=pXKQzND+*1} z@Sc^>A)le*cqytYAyGLjs-smbjO`D%k7Aq46b>=2ZDL7^j%yw{`6OL9r`XfD*u&+k}%kD07uw&9MO9J9-AwKPNHGi#b($)ve)Fuu2) zRG|0t)Va@h-XsdD{GR%~PWHilmb7V1GbE$Ur|+z(nDG9r**Qz4_ZAnnx26eM>?@ac zzP;PPIO}fexxQ}~!@N$-JGskly_u?~`g@z{DsN}3$j(WNyct#X-?+ct^s$ZHytpaW zyLL~%x6UZpc>Rm_D?jC~J$h(n;JbZ4m<}9xBD4JcUX}-U{-*NXt2y>#@zlPK$D8wa zR%A?&_O}bttXw8;|A1$g_R^5{L;tlW9zXNpT#@dg+sD5#8kDA}U!8Qk!RyJ64 z@-{N3wOzdFKK+&U9Eo*>Sxb&Rm|9u2N-$p9@NC(wPB)nwB1=DJX&*e3aU_>vl7hsp zUlGici+37uzpu-9@@tZcrr)heOQQYlHT{~RJOln;X?moh;n#G|zC!nQeQsQ3CaZtS z-f3ITF}v!w2y}Fv{BPv7?fjWgm5=v?*B)hjzq#9fDof(E#BCGK&MweftHu83{pBCh zmr5VYTr4;aN;9e@t`Q|Ei6yC4$wjF^iowXh&_LI~RM*fl#K7Fj$kfWjNY}v3%D}+( q{{eLr4Y~O#nQ4_M8Z500O|1;gAQ~ofTEqf1FnGH9xvX^) literal 0 HcmV?d00001 diff --git a/resources/recipes/emg_en.recipe b/resources/recipes/emg_en.recipe new file mode 100644 index 0000000000..fc76dc2c97 --- /dev/null +++ b/resources/recipes/emg_en.recipe @@ -0,0 +1,45 @@ +__license__ = 'GPL v3' +__copyright__ = '2010, Darko Miletic ' +''' +emg.rs/en/news +''' + +from calibre.web.feeds.news import BasicNewsRecipe + +class emportal_en(BasicNewsRecipe): + title = 'Ekonom:east News' + __author__ = 'Darko Miletic' + description = 'Daily business news from Serbia.' + publisher = 'Ekonom:east Media Group' + category = 'Business, SEE, Serbia, Belgrade, news, Ekonomist, EMportal' + oldest_article = 2 + max_articles_per_feed = 200 + no_stylesheets = True + encoding = 'utf8' + use_embedded_content = False + language = 'en' + remove_empty_feeds = True + masthead_url = 'http://www.emg.rs/img/emportal-rss.png' + extra_css = ' body{font-family: Arial,Helvetica,sans-serif } ' + + conversion_options = { + 'comment' : description + , 'tags' : category + , 'publisher' : publisher + , 'language' : language + } + + remove_tags = [dict(attrs={'class':['text-share']})] + keep_only_tags = [dict(attrs={'class':'text'})] + remove_tags_after = dict(attrs={'class':'text-share'}) + remove_attributes = ['width','height'] + + feeds = [(u'Serbia' , u'http://www.emg.rs/en/news/serbia/rss.xml')] + + def print_version(self, url): + return url.replace('.html','.print.html') + + def preprocess_html(self, soup): + for item in soup.findAll(style=True): + del item['style'] + return soup diff --git a/resources/recipes/emg_rs.recipe b/resources/recipes/emg_rs.recipe new file mode 100644 index 0000000000..7dfc0769ce --- /dev/null +++ b/resources/recipes/emg_rs.recipe @@ -0,0 +1,51 @@ +__license__ = 'GPL v3' +__copyright__ = '2010, Darko Miletic ' +''' +emg.rs/vesti +''' + +import re +from calibre.web.feeds.news import BasicNewsRecipe + +class emportal_rs(BasicNewsRecipe): + title = 'Ekonom:east Vesti' + __author__ = 'Darko Miletic' + description = 'Vasa dnevna doza poslovnih informacija iz Srbije, regiona i sveta. Vesti, Berze, Dogadaji, Casopisi.' + publisher = 'Ekonom:east Media Group' + category = 'Ekonom:east Media Group, Ekonomist, Budelar, Bankar, EMportal, Preduzeca, Moja Posla, EU praktikum, ekonomija, Srbija, Beograd, investicije, finansije, energetika, berza' + oldest_article = 2 + max_articles_per_feed = 200 + no_stylesheets = True + encoding = 'utf8' + use_embedded_content = False + language = 'sr' + remove_empty_feeds = True + masthead_url = 'http://www.emg.rs/img/emportal-rss.png' + extra_css = ' @font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)} body{font-family: Arial,Helvetica,sans1,sans-serif } .article_description{font-family: sans1, sans-serif} ' + + conversion_options = { + 'comment' : description + , 'tags' : category + , 'publisher' : publisher + , 'language' : language + } + + preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')] + remove_tags = [dict(attrs={'class':['text-share']})] + keep_only_tags = [dict(attrs={'class':'text'})] + remove_tags_after = dict(attrs={'class':'text-share'}) + remove_attributes = ['width','height'] + + feeds = [ + (u'Srbija' , u'http://www.emg.rs/vesti/srbija/rss.xml') + ,(u'Region' , u'http://www.emg.rs/vesti/region/rss.xml') + ,(u'Svet' , u'http://www.emg.rs/vesti/svet/rss.xml' ) + ] + + def print_version(self, url): + return url.replace('.html','.print.html') + + def preprocess_html(self, soup): + for item in soup.findAll(style=True): + del item['style'] + return soup From 7d7ab9bfebc46c40191f381171311b75cd8489ea Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 19 May 2010 10:53:25 -0600 Subject: [PATCH 02/13] La Stampa by Gabriele Marini --- resources/recipes/la_stampa.recipe | 67 ++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 resources/recipes/la_stampa.recipe diff --git a/resources/recipes/la_stampa.recipe b/resources/recipes/la_stampa.recipe new file mode 100644 index 0000000000..b9d8a469aa --- /dev/null +++ b/resources/recipes/la_stampa.recipe @@ -0,0 +1,67 @@ +#!/usr/bin/env python +__license__ = 'GPL v3' +__author__ = 'Gabriele Marini, based on Darko Miletic' +__copyright__ = '2009, Darko Miletic ' +__description__ = 'La Stampa 05/05/2010' + +''' +http://www.lastampa.it/ +''' +from calibre.web.feeds.news import BasicNewsRecipe + +class LaStampa(BasicNewsRecipe): + + title = u'La Stampa' + language = 'it' + __author__ = 'Gabriele Marini' + oldest_article = 15 + max_articles_per_feed = 50 + recursion = 100 + cover_url = 'http://www.lastampa.it/edicola/PDF/1.pdf' + use_embedded_content = False + remove_javascript = True + no_stylesheets = True + + conversion_options = {'linearize_tables':True} + remove_attributes = ['colspan'] + + extra_css = ' .boxocchiello2{font-size: small} .catenaccio{font-style: italic} .titoloRub{font-size: xx-large; font-weight: bold } .titologir{font-size: xx-large; font-weight: bold } .sezione{font-weight: bold} ' + + + def get_article_url(self, article): + link = article.get('links') + if link: + return link[0]['href'] + + keep_only_tags = [dict(attrs={'class':['boxocchiello2','titoloRub','titologir','catenaccio','sezione','articologirata']}), + dict(name='div', attrs={'id':'corpoarticolo'}) + ] + + remove_tags = [dict(name='div', attrs={'id':'menutop'}), + dict(name='div', attrs={'id':'fwnetblocco'}), + dict(name='table', attrs={'id':'strumenti'}), + dict(name='table', attrs={'id':'imgesterna'}), + dict(name='a', attrs={'class':'linkblu'}), + dict(name='a', attrs={'class':'link'}), + dict(name='span', attrs={'class':['boxocchiello','boxocchiello2','sezione']}) + ] + + feeds = [ + (u'Home', u'http://www.lastampa.it/redazione/rss_home.xml'), + (u'Editoriali', u'http://www.lastampa.it/cmstp/rubriche/oggetti/rss.asp?ID_blog=25'), + (u'Politica', u'http://www.lastampa.it/redazione/cmssezioni/politica/rss_politica.xml'), + (u'ArciItaliana', u'http://www.lastampa.it/cmstp/rubriche/oggetti/rss.asp?ID_blog=14'), + (u'Cronache', u'http://www.lastampa.it/redazione/cmssezioni/cronache/rss_cronache.xml'), + (u'Esteri', u'http://www.lastampa.it/redazione/cmssezioni/esteri/rss_esteri.xml'), + (u'Danni Collaterali', u'http://www.lastampa.it/cmstp/rubriche/oggetti/rss.asp?ID_blog=90'), + (u'Economia', u'http://www.lastampa.it/redazione/cmssezioni/economia/rss_economia.xml'), + (u'Tecnologia ', u'http://www.lastampa.it/cmstp/rubriche/oggetti/rss.asp?ID_blog=30'), + (u'Spettacoli', u'http://www.lastampa.it/redazione/cmssezioni/spettacoli/rss_spettacoli.xml'), + (u'Sport', u'http://www.lastampa.it/sport/rss_home.xml'), + (u'Torino', u'http://rss.feedsportal.com/c/32418/f/466938/index.rss'), + (u'Motori', u'http://www.lastampa.it/cmstp/rubriche/oggetti/rss.asp?ID_blog=57'), + (u'Scienza', u'http://www.lastampa.it/cmstp/rubriche/oggetti/rss.asp?ID_blog=38'), + (u'Fotografia', u'http://rss.feedsportal.com/c/32418/f/478449/index.rss'), + (u'Scuola', u'http://www.lastampa.it/cmstp/rubriche/oggetti/rss.asp?ID_blog=60'), + (u'Tempo Libero', u'http://www.lastampa.it/tempolibero/rss_home.xml') + ] From ec7167ef856b5126a607adf951ce0cbfa10e9016 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 19 May 2010 12:36:52 -0600 Subject: [PATCH 03/13] Remove PRS500 driver and initial implementation of SONY XML cache update --- src/calibre/customize/builtins.py | 2 - src/calibre/devices/interface.py | 13 ++ src/calibre/devices/prs505/driver.py | 14 +- src/calibre/devices/prs505/sony_cache.py | 202 +++++++++++++++++++++-- src/calibre/devices/usbms/books.py | 38 ++++- src/calibre/gui2/wizard/__init__.py | 12 +- 6 files changed, 253 insertions(+), 28 deletions(-) diff --git a/src/calibre/customize/builtins.py b/src/calibre/customize/builtins.py index 17239256df..9a32774f5f 100644 --- a/src/calibre/customize/builtins.py +++ b/src/calibre/customize/builtins.py @@ -442,7 +442,6 @@ from calibre.devices.irexdr.driver import IREXDR1000, IREXDR800 from calibre.devices.jetbook.driver import JETBOOK from calibre.devices.kindle.driver import KINDLE, KINDLE2, KINDLE_DX from calibre.devices.nook.driver import NOOK -from calibre.devices.prs500.driver import PRS500 from calibre.devices.prs505.driver import PRS505, PRS700 from calibre.devices.android.driver import ANDROID, S60 from calibre.devices.nokia.driver import N770, N810 @@ -512,7 +511,6 @@ plugins += [ NOOK, PRS505, PRS700, - PRS500, ANDROID, S60, N770, diff --git a/src/calibre/devices/interface.py b/src/calibre/devices/interface.py index 40cac4d615..be58bc9b0c 100644 --- a/src/calibre/devices/interface.py +++ b/src/calibre/devices/interface.py @@ -418,3 +418,16 @@ class BookList(list): ''' raise NotImplementedError() + def get_collections(self, collection_attributes): + ''' + Return a dictionary of collections created from collection_attributes. + Each entry in the dictionary is of the form collection name:[list of + books] + + The list of books is sorted by book title, except for collections + created from series, in which case series_index is used. + + :param collection_attributes: A list of attributes of the Book object + ''' + raise NotImplementedError() + diff --git a/src/calibre/devices/prs505/driver.py b/src/calibre/devices/prs505/driver.py index 0bf2a1de82..846ca9593d 100644 --- a/src/calibre/devices/prs505/driver.py +++ b/src/calibre/devices/prs505/driver.py @@ -71,7 +71,7 @@ class PRS505(USBMS): return fname def initialize_XML_cache(self): - paths = {} + paths, prefixes = {}, {} for prefix, path, source_id in [ ('main', MEDIA_XML, 0), ('card_a', CACHE_XML, 1), @@ -80,10 +80,11 @@ class PRS505(USBMS): prefix = getattr(self, '_%s_prefix'%prefix) if prefix is not None and os.path.exists(prefix): paths[source_id] = os.path.join(prefix, *(path.split('/'))) + prefixes[source_id] = prefix d = os.path.dirname(paths[source_id]) if not os.path.exists(d): os.makedirs(d) - return XMLCache(paths) + return XMLCache(paths, prefixes) def books(self, oncard=None, end_session=True): bl = USBMS.books(self, oncard=oncard, end_session=end_session) @@ -96,10 +97,15 @@ class PRS505(USBMS): blists = {} for i in c.paths: blists[i] = booklists[i] - c.update(blists) + opts = self.settings() + collections = ['series', 'tags'] + if opts.extra_customization: + collections = opts.extra_customization.split(',') + + c.update(blists, collections) c.write() - USBMS.sync_booklists(self, booklists, end_session) + USBMS.sync_booklists(self, booklists, end_session=end_session) class PRS700(PRS505): diff --git a/src/calibre/devices/prs505/sony_cache.py b/src/calibre/devices/prs505/sony_cache.py index b81867dc7f..5b11b89a0a 100644 --- a/src/calibre/devices/prs505/sony_cache.py +++ b/src/calibre/devices/prs505/sony_cache.py @@ -5,17 +5,18 @@ __license__ = 'GPL v3' __copyright__ = '2010, Kovid Goyal ' __docformat__ = 'restructuredtext en' -import os +import os, time from pprint import pprint from base64 import b64decode +from uuid import uuid4 from lxml import etree -from calibre import prints +from calibre import prints, guess_type from calibre.devices.errors import DeviceError from calibre.constants import DEBUG from calibre.ebooks.chardet import xml_to_unicode -from calibre.ebooks.metadata import string_to_authors +from calibre.ebooks.metadata import string_to_authors, authors_to_string EMPTY_CARD_CACHE = '''\ @@ -23,12 +24,43 @@ EMPTY_CARD_CACHE = '''\ ''' +MIME_MAP = { + "lrf" : "application/x-sony-bbeb", + 'lrx' : 'application/x-sony-bbeb', + "rtf" : "application/rtf", + "pdf" : "application/pdf", + "txt" : "text/plain" , + 'epub': 'application/epub+zip', + } + +DAY_MAP = dict(Sun=0, Mon=1, Tue=2, Wed=3, Thu=4, Fri=5, Sat=6) +MONTH_MAP = dict(Jan=1, Feb=2, Mar=3, Apr=4, May=5, Jun=6, Jul=7, Aug=8, Sep=9, Oct=10, Nov=11, Dec=12) +INVERSE_DAY_MAP = dict(zip(DAY_MAP.values(), DAY_MAP.keys())) +INVERSE_MONTH_MAP = dict(zip(MONTH_MAP.values(), MONTH_MAP.keys())) + +def strptime(src): + src = src.strip() + src = src.split() + src[0] = str(DAY_MAP[src[0][:-1]])+',' + src[2] = str(MONTH_MAP[src[2]]) + return time.strptime(' '.join(src), '%w, %d %m %Y %H:%M:%S %Z') + +def strftime(epoch, zone=time.gmtime): + src = time.strftime("%w, %d %m %Y %H:%M:%S GMT", zone(epoch)).split() + src[0] = INVERSE_DAY_MAP[int(src[0][:-1])]+',' + src[2] = INVERSE_MONTH_MAP[int(src[2])] + return ' '.join(src) + +def uuid(): + return str(uuid4()).replace('-', '', 1).upper() + class XMLCache(object): - def __init__(self, paths): + def __init__(self, paths, prefixes): if DEBUG: pprint(paths) self.paths = paths + self.prefixes = prefixes parser = etree.XMLParser(recover=True) self.roots = {} for source_id, path in paths.items(): @@ -50,7 +82,9 @@ class XMLCache(object): recs = self.roots[0].xpath('//*[local-name()="records"]') if not recs: - raise DeviceError('The SONY XML database is corrupted (no )') + raise DeviceError('The SONY XML database is corrupted (no' + ' ). Try disconnecting an reconnecting' + ' your reader.') self.record_roots = {} self.record_roots.update(self.roots) self.record_roots[0] = recs[0] @@ -75,11 +109,63 @@ class XMLCache(object): for i, root in self.record_roots.items(): self.purge_broken_playlist_items(root) for playlist in root.xpath('//*[local-name()="playlist"]'): - if len(playlist) == 0: + if len(playlist) == 0 or not playlist.get('title', None): if DEBUG: - prints('Removing playlist:', playlist.get('id', None)) + prints('Removing playlist:', playlist.get('id', None), + playlist.get('title', None)) playlist.getparent().remove(playlist) + def ensure_unique_playlist_titles(self): + for i, root in self.record_roots.items(): + seen = set([]) + for playlist in root.xpath('//*[local-name()="playlist"]'): + title = playlist.get('title', None) + if title is None: + title = _('Unnamed') + playlist.set('title', title) + if title in seen: + for i in range(2, 1000): + if title+str(i) not in seen: + title = title+str(i) + playlist.set('title', title) + break + else: + seen.add(title) + + def get_playlist_map(self): + ans = {} + self.ensure_unique_playlist_titles() + self.prune_empty_playlists() + for i, root in self.record_roots.items(): + for playlist in root.xpath('//*[local-name()="playlist"]'): + items = [] + for item in playlist: + id_ = item.get('id', None) + records = root.xpath( + '//*[local-name()="text" and @id="%s"]'%id_) + if records: + items.append(records[0]) + ans[i] = {playlist.get('title'):items} + return ans + + def get_or_create_playlist(self, bl_idx, title): + root = self.record_roots[bl_idx] + for playlist in root.xpath('//*[local-name()="playlist"]'): + if playlist.get('title', None) == title: + return playlist + ans = root.makelement('{%s}playlist'%self.namespaces[bl_idx], + nsmap=root.nsmap, attrib={ + 'uuid' : uuid(), + 'title': title, + 'id' : str(self.max_id(root)+1), + 'sourceid': '1' + }) + tail = '\n\t\t' if bl_idx == 0 else '\n\t' + ans.tail = tail + if len(root) > 0: + root.iterchildren(reversed=True).next().tail = tail + root.append(ans) + return ans # }}} def fix_ids(self): # {{{ @@ -189,11 +275,107 @@ class XMLCache(object): break # }}} - def update(self, booklists): - pass + # Update XML Cache {{{ + def update(self, booklists, collections_attributes): + playlist_map = self.get_playlist_map() + + for i, booklist in booklists.items(): + root = self.record_roots[i] + for book in booklist: + path = os.path.join(self.prefixes[i], *(book.lpath.split('/'))) + record = self.book_by_lpath(book.lpath, root) + if record is None: + record = self.create_text_record(root, i, book.lpath) + self.update_record(record, book, path, i) + bl_pmap = playlist_map[i] + self.update_playlists(i, root, booklist, bl_pmap, + collections_attributes) + + tail = '\n\t' if i == 0 else '\n' + if len(root) > 0: + root.iterchildren(reversed=True).next().tail = tail + + self.fix_ids() + + def update_playlists(self, bl_index, root, booklist, playlist_map, + collections_attributes): + collections = booklist.get_collections(collections_attributes) + for category, books in collections: + records = [self.book_by_lpath(b.lpath) for b in books] + # Remove any books that were not found, although this + # *should* never happen + if DEBUG and None in records: + prints('WARNING: Some elements in the JSON cache were not' + 'found in the XML cache') + records = [x for x in records if x is not None] + for rec in records: + if rec.get('id', None) is None: + rec.set('id', str(self.max_id(root)+1)) + ids = [x.get('id', None) for x in records] + if None in ids: + if DEBUG: + prints('WARNING: Some elements do not have ids') + ids = [x for x in ids if x is not None] + + playlist = self.get_or_create_playlist(bl_index, category) + playlist_ids = [] + for item in playlist: + id_ = item.get('id', None) + if id_ is not None: + playlist_ids.append(id_) + for item in list(playlist): + playlist.remove(item) + + extra_ids = [x for x in playlist_ids if x not in ids] + tail = '\n\t\t\t' if bl_index == 0 else '\n\t\t' + playlist.tail = tail + for id_ in ids + extra_ids: + item = playlist.makeelement( + '{%s}item'%self.namespaces[bl_index], + nsmap=playlist.nsmap, attrib={'id':id_}) + item.tail = tail + if len(playlist) > 0: + root.iterchildren(reversed=True).next().tail = tail[:-1] + + + def create_text_record(self, root, bl_id, lpath): + namespace = self.namespaces[bl_id] + id_ = self.max_id(root)+1 + attrib = { + 'page':'0', 'part':'0','pageOffset':'0','scale':'0', + 'id':str(id_), 'sourceid':'1', 'path':lpath} + ans = root.makeelement('{%s}text'%namespace, attrib=attrib, nsmap=root.nsmap) + tail = '\n\t\t' if bl_id == 0 else '\n\t' + ans.tail = tail + if len(root) > 0: + root.iterchildren(reversed=True).next().tail = tail + root.append(ans) + return ans + + def update_text_record(self, record, book, path, bl_index): + timestamp = 'ctime' if bl_index == 0 else 'mtime' + timestamp = getattr(os.path, 'get'+timestamp)(path) + date = strftime(timestamp) + record.set('date', date) + record.set('size', os.stat(path).st_size) + record.set('title', book.title) + record.set('author', authors_to_string(book.authors)) + ext = os.path.splitext(path)[1] + if ext: + ext = ext[1:].lower() + mime = MIME_MAP.get(ext, None) + if mime is None: + mime = guess_type('a.'+ext)[0] + if mime is not None: + record.set('mime', mime) + if 'sourceid' not in record.attrib: + record.set('sourceid', '1') + if 'id' not in record.attrib: + num = self.max_id(record.getroottree().getroot()) + record.set('id', str(num+1)) + # }}} def write(self): - return for i, path in self.paths.items(): raw = etree.tostring(self.roots[i], encoding='utf-8', xml_declaration=True) diff --git a/src/calibre/devices/usbms/books.py b/src/calibre/devices/usbms/books.py index 5ae2c20df7..97c911283b 100644 --- a/src/calibre/devices/usbms/books.py +++ b/src/calibre/devices/usbms/books.py @@ -4,9 +4,7 @@ __license__ = 'GPL 3' __copyright__ = '2009, John Schember ' __docformat__ = 'restructuredtext en' -import os -import re -import time +import os, re, time, sys from calibre.ebooks.metadata import MetaInformation from calibre.devices.mime import mime_type_ext @@ -110,6 +108,9 @@ class Book(MetaInformation): if isbytestring(val): enc = filesystem_encoding if attr == 'lpath' else preferred_encoding val = val.decode(enc, 'replace') + elif isinstance(val, (list, tuple)): + val = [x.decode(preferred_encoding, 'replace') if + isbytestring(x) else x for x in val] json[attr] = val return json @@ -129,3 +130,34 @@ class BookList(_BookList): def remove_book(self, book): self.remove(book) + + def get_collections(self, collection_attributes): + collections = {} + series_categories = set([]) + for attr in collection_attributes: + for book in self: + val = getattr(book, attr, None) + if not val: continue + if isbytestring(val): + val = val.decode(preferred_encoding, 'replace') + if isinstance(val, (list, tuple)): + val = list(val) + elif isinstance(val, unicode): + val = [val] + for category in val: + if category not in collections: + collections[category] = [] + collections[category].append(book) + if attr == 'series': + series_categories.add(category) + for category, books in collections.items(): + def tgetter(x): + return getattr(x, 'title_sort', 'zzzz') + books.sort(cmp=lambda x,y:cmp(tgetter(x), tgetter(y))) + if category in series_categories: + # Ensures books are sub sorted by title + def getter(x): + return getattr(x, 'series_index', sys.maxint) + books.sort(cmp=lambda x,y:cmp(getter(x), getter(y))) + return collections + diff --git a/src/calibre/gui2/wizard/__init__.py b/src/calibre/gui2/wizard/__init__.py index 0a395e9eb8..0ac6c0a00b 100644 --- a/src/calibre/gui2/wizard/__init__.py +++ b/src/calibre/gui2/wizard/__init__.py @@ -78,18 +78,12 @@ class KindleDX(Kindle): name = 'Kindle DX' id = 'kindledx' -class Sony500(Device): +class Sony505(Device): output_profile = 'sony' - name = 'SONY PRS 500' - output_format = 'LRF' - manufacturer = 'SONY' - id = 'prs500' - -class Sony505(Sony500): - + name = 'SONY Reader 6" and Touch Editions' output_format = 'EPUB' - name = 'SONY Reader 6" and Touch Edition' + manufacturer = 'SONY' id = 'prs505' class Kobo(Device): From 84aad4d27c5a036392ca9bd2de960ef9e0a8c06c Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 19 May 2010 12:46:21 -0600 Subject: [PATCH 04/13] Fix #5572 (Palm Pre not detected) --- src/calibre/devices/misc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/devices/misc.py b/src/calibre/devices/misc.py index 23cb05aeb4..b0d5718d7f 100644 --- a/src/calibre/devices/misc.py +++ b/src/calibre/devices/misc.py @@ -20,7 +20,7 @@ class PALMPRE(USBMS): FORMATS = ['mobi', 'prc', 'pdb', 'txt'] VENDOR_ID = [0x0830] - PRODUCT_ID = [0x8004, 0x8002] + PRODUCT_ID = [0x8004, 0x8002, 0x0101] BCD = [0x0316] VENDOR_NAME = 'PALM' From 6fcff2462e65e061caf6b1427f569bf09c7b289d Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 19 May 2010 13:12:17 -0600 Subject: [PATCH 05/13] ... --- resources/recipes/digitalspy_uk.recipe | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/recipes/digitalspy_uk.recipe b/resources/recipes/digitalspy_uk.recipe index ac54c3790d..a8cda3efc2 100644 --- a/resources/recipes/digitalspy_uk.recipe +++ b/resources/recipes/digitalspy_uk.recipe @@ -31,7 +31,7 @@ class DigitalSpyUK(BasicNewsRecipe): remove_tags = [dict(name=['link'])] remove_attributes = ['height','width'] - keep_only_tags = [dict(name='div',attrs={'id':'article'})] + keep_only_tags = [dict(name='div',attrs={'id':'content'})] feeds = [ (u'News' , u'http://www.digitalspy.co.uk/rss/zones/gb/all.xml' ) From 6b9696867f1a46b764e8d61520068fc0a557bc3e Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 19 May 2010 13:35:56 -0600 Subject: [PATCH 06/13] Fix various typos --- src/calibre/devices/prs505/driver.py | 3 ++- src/calibre/devices/prs505/sony_cache.py | 11 ++++++----- src/calibre/devices/usbms/driver.py | 3 +-- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/calibre/devices/prs505/driver.py b/src/calibre/devices/prs505/driver.py index 846ca9593d..794bf66600 100644 --- a/src/calibre/devices/prs505/driver.py +++ b/src/calibre/devices/prs505/driver.py @@ -96,7 +96,8 @@ class PRS505(USBMS): c = self.initialize_XML_cache() blists = {} for i in c.paths: - blists[i] = booklists[i] + if booklists[i] is not None: + blists[i] = booklists[i] opts = self.settings() collections = ['series', 'tags'] if opts.extra_customization: diff --git a/src/calibre/devices/prs505/sony_cache.py b/src/calibre/devices/prs505/sony_cache.py index 5b11b89a0a..61ae610c26 100644 --- a/src/calibre/devices/prs505/sony_cache.py +++ b/src/calibre/devices/prs505/sony_cache.py @@ -137,6 +137,7 @@ class XMLCache(object): self.ensure_unique_playlist_titles() self.prune_empty_playlists() for i, root in self.record_roots.items(): + ans[i] = {} for playlist in root.xpath('//*[local-name()="playlist"]'): items = [] for item in playlist: @@ -153,7 +154,7 @@ class XMLCache(object): for playlist in root.xpath('//*[local-name()="playlist"]'): if playlist.get('title', None) == title: return playlist - ans = root.makelement('{%s}playlist'%self.namespaces[bl_idx], + ans = root.makeelement('{%s}playlist'%self.namespaces[bl_idx], nsmap=root.nsmap, attrib={ 'uuid' : uuid(), 'title': title, @@ -286,7 +287,7 @@ class XMLCache(object): record = self.book_by_lpath(book.lpath, root) if record is None: record = self.create_text_record(root, i, book.lpath) - self.update_record(record, book, path, i) + self.update_text_record(record, book, path, i) bl_pmap = playlist_map[i] self.update_playlists(i, root, booklist, bl_pmap, collections_attributes) @@ -300,8 +301,8 @@ class XMLCache(object): def update_playlists(self, bl_index, root, booklist, playlist_map, collections_attributes): collections = booklist.get_collections(collections_attributes) - for category, books in collections: - records = [self.book_by_lpath(b.lpath) for b in books] + for category, books in collections.items(): + records = [self.book_by_lpath(b.lpath, root) for b in books] # Remove any books that were not found, although this # *should* never happen if DEBUG and None in records: @@ -357,7 +358,7 @@ class XMLCache(object): timestamp = getattr(os.path, 'get'+timestamp)(path) date = strftime(timestamp) record.set('date', date) - record.set('size', os.stat(path).st_size) + record.set('size', str(os.stat(path).st_size)) record.set('title', book.title) record.set('author', authors_to_string(book.authors)) ext = os.path.splitext(path)[1] diff --git a/src/calibre/devices/usbms/driver.py b/src/calibre/devices/usbms/driver.py index 7b3531abf6..76996481a5 100644 --- a/src/calibre/devices/usbms/driver.py +++ b/src/calibre/devices/usbms/driver.py @@ -98,11 +98,10 @@ class USBMS(CLI, Device): import traceback traceback.print_exc() return changed - if isinstance(ebook_dirs, basestring): ebook_dirs = [ebook_dirs] for ebook_dir in ebook_dirs: - ebook_dir = self.path_to_unicode(filesystem_encoding) + ebook_dir = self.path_to_unicode(ebook_dir) ebook_dir = self.normalize_path( \ os.path.join(prefix, *(ebook_dir.split('/'))) \ if ebook_dir else prefix) From a1971fdfda37c84b3d1cfb480cab1ad684556a80 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 19 May 2010 14:16:39 -0600 Subject: [PATCH 07/13] Fix id rebasing and add helpful method for test scripts --- src/calibre/devices/__init__.py | 28 ++++++++++++++++++++++++ src/calibre/devices/prs505/sony_cache.py | 14 +++++++----- 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/src/calibre/devices/__init__.py b/src/calibre/devices/__init__.py index bcbd9b1640..fd6eaae79f 100644 --- a/src/calibre/devices/__init__.py +++ b/src/calibre/devices/__init__.py @@ -27,6 +27,34 @@ def strftime(epoch, zone=time.gmtime): src[2] = INVERSE_MONTH_MAP[int(src[2])] return ' '.join(src) +def get_connected_device(): + from calibre.customize.ui import device_plugins + from calibre.devices.scanner import DeviceScanner + dev = None + scanner = DeviceScanner() + scanner.scan() + connected_devices = [] + for d in device_plugins(): + ok, det = scanner.is_device_connected(d) + if ok: + dev = d + dev.reset(log_packets=False, detected_device=det) + connected_devices.append(dev) + + if dev is None: + print >>sys.stderr, 'Unable to find a connected ebook reader.' + return + + for d in connected_devices: + try: + d.open() + except: + continue + else: + dev = d + break + return dev + def debug(ioreg_to_tmp=False, buf=None): from calibre.customize.ui import device_plugins from calibre.devices.scanner import DeviceScanner, win_pnp_drives diff --git a/src/calibre/devices/prs505/sony_cache.py b/src/calibre/devices/prs505/sony_cache.py index 61ae610c26..5342ec5079 100644 --- a/src/calibre/devices/prs505/sony_cache.py +++ b/src/calibre/devices/prs505/sony_cache.py @@ -173,7 +173,7 @@ class XMLCache(object): def ensure_numeric_ids(root): idmap = {} - for x in root.xpath('//*[@id]'): + for x in root.xpath('child::*[@id]'): id_ = x.get('id') try: id_ = int(id_) @@ -206,7 +206,9 @@ class XMLCache(object): for item in root.xpath('//*[@sourceid]'): sid = pl_sourceid if item.tag.endswith('playlist') else sourceid item.set('sourceid', str(sid)) - items = root.xpath('//*[@id]') + # Only rebase ids of nodes that are immediate children of the + # record root (that way playlist/itemnodes are unaffected + items = root.xpath('child::*[@id]') items.sort(cmp=lambda x,y:cmp(int(x.get('id')), int(y.get('id')))) idmap = {} for i, item in enumerate(items): @@ -214,13 +216,13 @@ class XMLCache(object): new = base + i if old != new: item.set('id', str(new)) - idmap[old] = str(new) + idmap[old] = str(new) return idmap self.prune_empty_playlists() for i in sorted(self.roots.keys()): - root = self.roots[i] + root = self.record_roots[i] if i == 0: ensure_media_xml_base_ids(root) @@ -281,6 +283,8 @@ class XMLCache(object): playlist_map = self.get_playlist_map() for i, booklist in booklists.items(): + if DEBUG: + prints('Updating booklist:', i) root = self.record_roots[i] for book in booklist: path = os.path.join(self.prefixes[i], *(book.lpath.split('/'))) @@ -378,7 +382,7 @@ class XMLCache(object): def write(self): for i, path in self.paths.items(): - raw = etree.tostring(self.roots[i], encoding='utf-8', + raw = etree.tostring(self.roots[i], encoding='UTF-8', xml_declaration=True) with open(path, 'wb') as f: f.write(raw) From e124ef7513c045552114edd1edc473875c5671d1 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 19 May 2010 15:21:01 -0600 Subject: [PATCH 08/13] Whitespace in XML cleanup and actually commit playlist that exists in JSON to XML --- src/calibre/devices/prs505/sony_cache.py | 49 ++++++++++++++---------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/src/calibre/devices/prs505/sony_cache.py b/src/calibre/devices/prs505/sony_cache.py index 5342ec5079..02afe6c10d 100644 --- a/src/calibre/devices/prs505/sony_cache.py +++ b/src/calibre/devices/prs505/sony_cache.py @@ -161,10 +161,6 @@ class XMLCache(object): 'id' : str(self.max_id(root)+1), 'sourceid': '1' }) - tail = '\n\t\t' if bl_idx == 0 else '\n\t' - ans.tail = tail - if len(root) > 0: - root.iterchildren(reversed=True).next().tail = tail root.append(ans) return ans # }}} @@ -296,10 +292,6 @@ class XMLCache(object): self.update_playlists(i, root, booklist, bl_pmap, collections_attributes) - tail = '\n\t' if i == 0 else '\n' - if len(root) > 0: - root.iterchildren(reversed=True).next().tail = tail - self.fix_ids() def update_playlists(self, bl_index, root, booklist, playlist_map, @@ -332,15 +324,12 @@ class XMLCache(object): playlist.remove(item) extra_ids = [x for x in playlist_ids if x not in ids] - tail = '\n\t\t\t' if bl_index == 0 else '\n\t\t' - playlist.tail = tail for id_ in ids + extra_ids: item = playlist.makeelement( '{%s}item'%self.namespaces[bl_index], nsmap=playlist.nsmap, attrib={'id':id_}) - item.tail = tail - if len(playlist) > 0: - root.iterchildren(reversed=True).next().tail = tail[:-1] + playlist.append(item) + def create_text_record(self, root, bl_id, lpath): @@ -350,18 +339,19 @@ class XMLCache(object): 'page':'0', 'part':'0','pageOffset':'0','scale':'0', 'id':str(id_), 'sourceid':'1', 'path':lpath} ans = root.makeelement('{%s}text'%namespace, attrib=attrib, nsmap=root.nsmap) - tail = '\n\t\t' if bl_id == 0 else '\n\t' - ans.tail = tail - if len(root) > 0: - root.iterchildren(reversed=True).next().tail = tail root.append(ans) return ans def update_text_record(self, record, book, path, bl_index): - timestamp = 'ctime' if bl_index == 0 else 'mtime' - timestamp = getattr(os.path, 'get'+timestamp)(path) + timestamp = os.path.getctime(path) date = strftime(timestamp) - record.set('date', date) + if date != record.get('date', None): + if DEBUG: + prints('Changing date of', path, 'from', + record.get('date', ''), 'to', date) + prints('\tctime', strftime(os.path.getctime(path))) + prints('\tmtime', strftime(os.path.getmtime(path))) + record.set('date', date) record.set('size', str(os.stat(path).st_size)) record.set('title', book.title) record.set('author', authors_to_string(book.authors)) @@ -380,12 +370,31 @@ class XMLCache(object): record.set('id', str(num+1)) # }}} + # Writing the XML files {{{ + def cleanup_whitespace(self, bl_index): + root = self.record_roots[bl_index] + level = 2 if bl_index == 0 else 1 + if len(root) > 0: + root.text = '\n'+'\t'*level + for child in root: + child.tail = '\n'+'\t'*level + if len(child) > 0: + child.text = '\n'+'\t'*(level+1) + for gc in child: + gc.tail = '\n'+'\t'*(level+1) + child.iterchildren(reversed=True).next().tail = '\n'+'\t'*level + root.iterchildren(reversed=True).next().tail = '\n'+'\t'*(level-1) + def write(self): for i, path in self.paths.items(): + self.cleanup_whitespace(i) raw = etree.tostring(self.roots[i], encoding='UTF-8', xml_declaration=True) + raw = raw.replace("", + '') with open(path, 'wb') as f: f.write(raw) + # }}} def book_by_lpath(self, lpath, root): matches = root.xpath(u'//*[local-name()="text" and @path="%s"]'%lpath) From 82534aeb0a057eca20871df1442613472372bead Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 19 May 2010 15:28:49 -0600 Subject: [PATCH 09/13] Documentation --- src/calibre/devices/prs505/sony_cache.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/calibre/devices/prs505/sony_cache.py b/src/calibre/devices/prs505/sony_cache.py index 02afe6c10d..7022e58350 100644 --- a/src/calibre/devices/prs505/sony_cache.py +++ b/src/calibre/devices/prs505/sony_cache.py @@ -18,6 +18,7 @@ from calibre.constants import DEBUG from calibre.ebooks.chardet import xml_to_unicode from calibre.ebooks.metadata import string_to_authors, authors_to_string +# Utility functions {{{ EMPTY_CARD_CACHE = '''\ @@ -54,6 +55,8 @@ def strftime(epoch, zone=time.gmtime): def uuid(): return str(uuid4()).replace('-', '', 1).upper() +# }}} + class XMLCache(object): def __init__(self, paths, prefixes): @@ -61,6 +64,8 @@ class XMLCache(object): pprint(paths) self.paths = paths self.prefixes = prefixes + + # Parse XML files {{{ parser = etree.XMLParser(recover=True) self.roots = {} for source_id, path in paths.items(): @@ -79,6 +84,7 @@ class XMLCache(object): raw, strip_encoding_pats=True, assume_utf8=True, verbose=DEBUG)[0], parser=parser) + # }}} recs = self.roots[0].xpath('//*[local-name()="records"]') if not recs: @@ -242,7 +248,8 @@ class XMLCache(object): self.roots[0].set('nextID', str(max_id+1)) # }}} - def update_booklist(self, bl, bl_index): # {{{ + # Update JSON from XML {{{ + def update_booklist(self, bl, bl_index): if bl_index not in self.record_roots: return root = self.record_roots[bl_index] @@ -274,7 +281,7 @@ class XMLCache(object): break # }}} - # Update XML Cache {{{ + # Update XML from JSON {{{ def update(self, booklists, collections_attributes): playlist_map = self.get_playlist_map() @@ -396,6 +403,7 @@ class XMLCache(object): f.write(raw) # }}} + # Utility methods {{{ def book_by_lpath(self, lpath, root): matches = root.xpath(u'//*[local-name()="text" and @path="%s"]'%lpath) if matches: @@ -441,4 +449,5 @@ class XMLCache(object): pprint(self.nsmaps) prints('Found namespaces:') pprint(self.namespaces) + # }}} From ec208049c547d73be9ee3807af21f4d583c2e5cc Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 19 May 2010 15:39:15 -0600 Subject: [PATCH 10/13] Improved Il Messagero and Leggo (it) --- resources/recipes/il_messaggero.recipe | 21 ++++++++++++++++++--- resources/recipes/leggo_it.recipe | 26 +++++++++++++++++++++----- 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/resources/recipes/il_messaggero.recipe b/resources/recipes/il_messaggero.recipe index e2c90075d5..93c35f4695 100644 --- a/resources/recipes/il_messaggero.recipe +++ b/resources/recipes/il_messaggero.recipe @@ -7,14 +7,14 @@ description = 'Italian daily newspaper - v1.01 (04, January 2010)' ''' http://www.messaggero.it/ ''' - +import time from calibre.web.feeds.news import BasicNewsRecipe class IlMessaggero(BasicNewsRecipe): __author__ = 'Gabriele Marini' description = 'Italian News' - cover_url = 'http://www.ilmessaggero.it/img_tst/logomsgr.gif' +# cover_url = 'http://www.ilmessaggero.it/img_tst/logomsgr.gif' title = u'Il Messaggero' publisher = 'Caltagirone Editore' category = 'News, politics, culture, economy, general interest' @@ -34,6 +34,21 @@ class IlMessaggero(BasicNewsRecipe): dict(name='h2', attrs={'class':'sottotitLettura'}), dict(name='span', attrs={'class':'testoArticoloG'}) ] + def get_cover_url(self): + cover = None + st = time.localtime() + year = str(st.tm_year) + month = "%.2d" % st.tm_mon + day = "%.2d" % st.tm_mday + cover='http://carta.ilmessaggero.it/' + year + month + day + '/jpeg/MSGR_20_CITTA_1.jpg' + br = BasicNewsRecipe.get_browser() + try: + br.open(cover) + except: + self.log("\nCover unavailable") + cover = 'http://www.ilmessaggero.it/img_tst/logomsgr.gif' + + return cover @@ -53,4 +68,4 @@ class IlMessaggero(BasicNewsRecipe): (u'Calcio', u'http://www.ilmessaggero.it/rss/sport_calcio.xml'), (u'Motori', u'http://www.ilmessaggero.it/rss/sport_motori.xml') ] - + diff --git a/resources/recipes/leggo_it.recipe b/resources/recipes/leggo_it.recipe index 1e2b5aca5b..5be21e7dfc 100644 --- a/resources/recipes/leggo_it.recipe +++ b/resources/recipes/leggo_it.recipe @@ -7,14 +7,14 @@ description = 'Italian daily newspaper - v1.00 05-05-2010' ''' http://www.leggo.it ''' - +import time from calibre.web.feeds.news import BasicNewsRecipe class LeggoIT(BasicNewsRecipe): __author__ = 'Gabriele Marini' description = 'Italian Free daily newspaper' - cover_url = 'http://www.leggo.it/img/logo-leggo2.gif' +# cover_url = 'http://www.leggo.it/img/logo-leggo2.gif' title = u'Leggo.it' publisher = 'Ced Caltagirone Editore S.p.A.' category = 'News, politics, culture, economy, general interest' @@ -33,8 +33,8 @@ class LeggoIT(BasicNewsRecipe): keep_only_tags = [ dict(name='h1',attrs={'class':'nero22'}), - dict(name='div',attrs={'id':'testodim'}) - ] + dict(name='div',attrs={'id':'testodim'}) + ] feeds = [ (u'Home Page', u'http://www.leggo.it/rss/home.xml'), (u'Italia', u'http://www.leggo.it/rss/italia.xml'), @@ -45,5 +45,21 @@ class LeggoIT(BasicNewsRecipe): (u'Spettacoli', u'http://www.leggo.it/rss/spettacoli.xml'), (u'Salute', u'http://www.leggo.it/rss/salute.xml'), (u'Scienza', u'http://www.leggo.it/rss/scienza.xml') - ] + ] + def get_cover_url(self): + cover = None + st = time.localtime() + year = str(st.tm_year) + month = "%.2d" % st.tm_mon + day = "%.2d" % st.tm_mday + cover='http://www.leggo.it/'+ year + month + day + '/jpeg/LEGGO_ROMA_1.jpg' + br = BasicNewsRecipe.get_browser() + try: + br.open(cover) + except: + self.log("\nCover unavailable") + cover = 'http://www.leggo.it/img/logo-leggo2.gif' + + return cover + From e34c95f5e5ecc3d19a7e5faadd9cfd83dce85541 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 19 May 2010 15:42:07 -0600 Subject: [PATCH 11/13] ... --- resources/recipes/politifact.recipe | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/recipes/politifact.recipe b/resources/recipes/politifact.recipe index d06f3a51e0..e3550ce7f1 100644 --- a/resources/recipes/politifact.recipe +++ b/resources/recipes/politifact.recipe @@ -1,4 +1,4 @@ -from calibre.wb.feeds.news import BasicNewsRecipe +from calibre.web.feeds.news import BasicNewsRecipe class PolitiFactCom(BasicNewsRecipe): title = u'Politifact' From fdb4d1f6db0647476bd70fbb335b8a1d1eeeb4ee Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 19 May 2010 15:45:30 -0600 Subject: [PATCH 12/13] ... --- resources/recipes/billorielly.recipe | 1 + resources/recipes/seanhannity.recipe | 1 + src/calibre/utils/localization.py | 1 + 3 files changed, 3 insertions(+) diff --git a/resources/recipes/billorielly.recipe b/resources/recipes/billorielly.recipe index e5eb700248..5944b496d5 100644 --- a/resources/recipes/billorielly.recipe +++ b/resources/recipes/billorielly.recipe @@ -9,6 +9,7 @@ class BillOReilly(BasicNewsRecipe): title = u"Bill O'Reilly" __author__ = 'Rob Lammert - rob.lammert[at]gmail.com' description = u"Articles from Bill O'Reilly's website and his Fox New's website" + language = 'en' oldest_article = 7.0 max_articles_per_feed = 100 recursions = 0 diff --git a/resources/recipes/seanhannity.recipe b/resources/recipes/seanhannity.recipe index 665b84de1f..c554dcd684 100644 --- a/resources/recipes/seanhannity.recipe +++ b/resources/recipes/seanhannity.recipe @@ -8,6 +8,7 @@ class SeanHannity(BasicNewsRecipe): __author__ = 'Rob Lammert - rob.lammert[at]gmail.com' description = u"Articles from Sean Hannity's website, www.hannity.com" oldest_article = 7.0 + language = 'en' max_articles_per_feed = 100 recursions = 0 encoding = 'utf8' diff --git a/src/calibre/utils/localization.py b/src/calibre/utils/localization.py index b34d0cdc7e..026547ee2e 100644 --- a/src/calibre/utils/localization.py +++ b/src/calibre/utils/localization.py @@ -106,6 +106,7 @@ _extra_lang_codes = { 'en_SG' : _('English (Singapore)'), 'en_YE' : _('English (Yemen)'), 'en_IE' : _('English (Ireland)'), + 'en_CN' : _('English (China)'), 'es_PY' : _('Spanish (Paraguay)'), 'de_AT' : _('German (AT)'), 'nl' : _('Dutch (NL)'), From c0f70a782088a2fcdfc44838aebefa1684d62623 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 19 May 2010 16:05:46 -0600 Subject: [PATCH 13/13] Another typo --- src/calibre/devices/prs505/sony_cache.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/calibre/devices/prs505/sony_cache.py b/src/calibre/devices/prs505/sony_cache.py index 7022e58350..674a2cbddd 100644 --- a/src/calibre/devices/prs505/sony_cache.py +++ b/src/calibre/devices/prs505/sony_cache.py @@ -61,6 +61,7 @@ class XMLCache(object): def __init__(self, paths, prefixes): if DEBUG: + prints('Building XMLCache...') pprint(paths) self.paths = paths self.prefixes = prefixes @@ -117,7 +118,7 @@ class XMLCache(object): for playlist in root.xpath('//*[local-name()="playlist"]'): if len(playlist) == 0 or not playlist.get('title', None): if DEBUG: - prints('Removing playlist:', playlist.get('id', None), + prints('Removing playlist id:', playlist.get('id', None), playlist.get('title', None)) playlist.getparent().remove(playlist) @@ -160,6 +161,8 @@ class XMLCache(object): for playlist in root.xpath('//*[local-name()="playlist"]'): if playlist.get('title', None) == title: return playlist + if DEBUG: + prints('Creating playlist:', title) ans = root.makeelement('{%s}playlist'%self.namespaces[bl_idx], nsmap=root.nsmap, attrib={ 'uuid' : uuid(), @@ -218,7 +221,7 @@ class XMLCache(object): new = base + i if old != new: item.set('id', str(new)) - idmap[old] = str(new) + idmap[str(old)] = str(new) return idmap self.prune_empty_playlists()