From 37651742cd7eb1db64cf9aef0e367b70eb2dc4b8 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 21 Sep 2012 13:10:59 +0530 Subject: [PATCH 01/26] Fix #1053858 (Typo (Mange) and missing quotation mark) --- src/calibre/gui2/device.py | 2 +- src/calibre/translations/calibre.pot | 10 +++++----- src/calibre/utils/formatter_functions.py | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/calibre/gui2/device.py b/src/calibre/gui2/device.py index d2f5704c6d..b56c40d402 100644 --- a/src/calibre/gui2/device.py +++ b/src/calibre/gui2/device.py @@ -853,7 +853,7 @@ class DeviceMixin(object): # {{{ self.connect_to_folder_named(tweaks['auto_connect_to_folder']) def allow_connect(self, name, icon): - return question_dialog(self, _('Mange the %s?')%name, + return question_dialog(self, _('Manage the %s?')%name, _('Detected the %s. Do you want calibre to manage it?')% name, show_copy_button=False, override_icon=QIcon(icon)) diff --git a/src/calibre/translations/calibre.pot b/src/calibre/translations/calibre.pot index fa0f160cb0..ccd6b65bc4 100644 --- a/src/calibre/translations/calibre.pot +++ b/src/calibre/translations/calibre.pot @@ -5,8 +5,8 @@ msgid "" msgstr "" "Project-Id-Version: calibre 0.8.70\n" -"POT-Creation-Date: 2012-09-21 09:52+IST\n" -"PO-Revision-Date: 2012-09-21 09:52+IST\n" +"POT-Creation-Date: 2012-09-21 13:10+IST\n" +"PO-Revision-Date: 2012-09-21 13:10+IST\n" "Last-Translator: Automatically generated\n" "Language-Team: LANGUAGE\n" "MIME-Version: 1.0\n" @@ -3485,7 +3485,7 @@ msgid "" "Fetch a cover image/social metadata for the book identified by ISBN from LibraryThing.com\n" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/opf2.py:1488 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/opf2.py:1491 #: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1279 #: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:958 #: /home/kovid/work/calibre/src/calibre/gui2/store/search/models.py:41 @@ -7827,7 +7827,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/device.py:856 #, python-format -msgid "Mange the %s?" +msgid "Manage the %s?" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/device.py:857 @@ -17967,7 +17967,7 @@ msgid "select(val, key) -- interpret the value as a comma-separated list of item msgstr "" #: /home/kovid/work/calibre/src/calibre/utils/formatter_functions.py:600 -msgid "approximate_formats() -- return a comma-separated list of formats that at one point were associated with the book. There is no guarantee that this list is correct, although it probably is. This function can be called in template program mode using the template \"{:'approximate_formats()'}. Note that format names are always uppercase, as in EPUB." +msgid "approximate_formats() -- return a comma-separated list of formats that at one point were associated with the book. There is no guarantee that this list is correct, although it probably is. This function can be called in template program mode using the template \"{:'approximate_formats()'}\". Note that format names are always uppercase, as in EPUB." msgstr "" #: /home/kovid/work/calibre/src/calibre/utils/formatter_functions.py:620 diff --git a/src/calibre/utils/formatter_functions.py b/src/calibre/utils/formatter_functions.py index 393ef876c9..67c75bdc79 100644 --- a/src/calibre/utils/formatter_functions.py +++ b/src/calibre/utils/formatter_functions.py @@ -602,7 +602,7 @@ class BuiltinApproximateFormats(BuiltinFormatterFunction): 'book. There is no guarantee that this list is correct, ' 'although it probably is. ' 'This function can be called in template program mode using ' - 'the template "{:\'approximate_formats()\'}. ' + 'the template "{:\'approximate_formats()\'}". ' 'Note that format names are always uppercase, as in EPUB.' ) From 54babcba513cd1bc8628b3debe4db0ebed1c954b Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 21 Sep 2012 16:42:00 +0530 Subject: [PATCH 02/26] Fix regression that broke customization of the smart device plugin --- src/calibre/devices/smart_device_app/driver.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/calibre/devices/smart_device_app/driver.py b/src/calibre/devices/smart_device_app/driver.py index 1931d68b82..b60f2ec7b1 100644 --- a/src/calibre/devices/smart_device_app/driver.py +++ b/src/calibre/devices/smart_device_app/driver.py @@ -902,10 +902,16 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin): return False def get_gui_name(self): - if self.client_device_kind: + if getattr(self, 'client_device_kind', None): return self.gui_name_template%(self.gui_name, self.client_device_kind) return self.gui_name + def config_widget(self): + from calibre.gui2.device_drivers.configwidget import ConfigWidget + cw = ConfigWidget(self.settings(), self.FORMATS, self.SUPPORTS_SUB_DIRS, + self.MUST_READ_METADATA, self.SUPPORTS_USE_AUTHOR_SORT, + self.EXTRA_CUSTOMIZATION_MESSAGE, self) + return cw @synchronous('sync_lock') def get_device_information(self, end_session=True): From f7b20bbea699c7bf18a870632e9622f1b1eb2e33 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 21 Sep 2012 16:50:58 +0530 Subject: [PATCH 03/26] MTP: Fix device that fails to open on linux not being blacklisted --- src/calibre/devices/mtp/unix/driver.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/calibre/devices/mtp/unix/driver.py b/src/calibre/devices/mtp/unix/driver.py index d86262c78b..599304e851 100644 --- a/src/calibre/devices/mtp/unix/driver.py +++ b/src/calibre/devices/mtp/unix/driver.py @@ -169,6 +169,7 @@ class MTP_DEVICE(MTPDeviceBase): try: self.dev = self.create_device(connected_device) except Exception as e: + self.blacklisted_devices.add(connected_device) raise OpenFailed('Failed to open %s: Error: %s'%( connected_device, as_unicode(e))) From 4ad6a16eaa9fbe4e97a02aa7f3d7f6772768007a Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 21 Sep 2012 20:46:50 +0530 Subject: [PATCH 04/26] Driver for Motorola MB526. Fixes #1054082 (Cannot connect Android smartphone) --- src/calibre/devices/android/driver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/devices/android/driver.py b/src/calibre/devices/android/driver.py index 18b2ab31e9..be3e7e0e03 100644 --- a/src/calibre/devices/android/driver.py +++ b/src/calibre/devices/android/driver.py @@ -230,7 +230,7 @@ class ANDROID(USBMS): 'THINKPAD_TABLET', 'SGH-T989', 'YP-G70', 'STORAGE_DEVICE', 'ADVANCED', 'SGH-I727', 'USB_FLASH_DRIVER', 'ANDROID', 'S5830I_CARD', 'MID7042', 'LINK-CREATE', '7035', 'VIEWPAD_7E', - 'NOVO7'] + 'NOVO7', 'MB526'] WINDOWS_CARD_A_MEM = ['ANDROID_PHONE', 'GT-I9000_CARD', 'SGH-I897', 'FILE-STOR_GADGET', 'SGH-T959_CARD', 'SGH-T959', 'SAMSUNG_ANDROID', 'GT-P1000_CARD', 'A70S', 'A101IT', '7', 'INCREDIBLE', 'A7EB', 'SGH-T849_CARD', From 5c425b2335f32630e92d25bbb682700911b943ca Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 21 Sep 2012 20:49:11 +0530 Subject: [PATCH 05/26] Fix #1054098 (Financial times UK issues with duplicate articles - updated recipe) --- recipes/financial_times_uk.recipe | 47 +++++++++++++++++-------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/recipes/financial_times_uk.recipe b/recipes/financial_times_uk.recipe index 16295905bc..4e5b522ae9 100644 --- a/recipes/financial_times_uk.recipe +++ b/recipes/financial_times_uk.recipe @@ -1,5 +1,5 @@ __license__ = 'GPL v3' -__copyright__ = '2010-2011, Darko Miletic ' +__copyright__ = '2010-2012, Darko Miletic ' ''' www.ft.com/uk-edition ''' @@ -51,10 +51,15 @@ class FinancialTimes(BasicNewsRecipe): return br keep_only_tags = [ - dict(name='div', attrs={'class':['fullstory fullstoryHeader', 'ft-story-header']}) - ,dict(name='div', attrs={'class':'standfirst'}) - ,dict(name='div', attrs={'id' :'storyContent'}) - ,dict(name='div', attrs={'class':['ft-story-body','index-detail']}) + dict(name='div' , attrs={'class':['fullstory fullstoryHeader', 'ft-story-header']}) + ,dict(name='div' , attrs={'class':'standfirst'}) + ,dict(name='div' , attrs={'id' :'storyContent'}) + ,dict(name='div' , attrs={'class':['ft-story-body','index-detail']}) + ,dict(name='div' , attrs={'class':['ft-story-body','index-detail']}) + ,dict(name='h2' , attrs={'class':'entry-title'} ) + ,dict(name='span', attrs={'class':lambda x: x and 'posted-on' in x.split()} ) + ,dict(name='span', attrs={'class':'author_byline'} ) + ,dict(name='div' , attrs={'class':'entry-content'} ) ] remove_tags = [ dict(name='div', attrs={'id':'floating-con'}) @@ -83,10 +88,9 @@ class FinancialTimes(BasicNewsRecipe): if self.test and count > 2: return articles rawlink = item['href'] - if rawlink.startswith('http://'): - url = rawlink - else: - url = self.PREFIX + rawlink + url = rawlink + if not rawlink.startswith('http://'): + url = self.PREFIX + rawlink urlverified = self.browser.open_novisit(url).geturl() # resolve redirect. title = self.tag_to_string(item) date = strftime(self.timefmt) @@ -106,20 +110,20 @@ class FinancialTimes(BasicNewsRecipe): wide = soup.find('div',attrs={'class':'wide'}) if not wide: return feeds - strest = wide.findAll('h3', attrs={'class':'section'}) - if not strest: + allsections = wide.findAll(attrs={'class':lambda x: x and 'footwell' in x.split()}) + if not allsections: return feeds - st = wide.findAll('h4',attrs={'class':'section-no-arrow'}) - if st: - st.extend(strest) count = 0 - for item in st: + for item in allsections: count = count + 1 if self.test and count > 2: return feeds - ftitle = self.tag_to_string(item) + fitem = item.h3 + if not fitem: + fitem = item.h4 + ftitle = self.tag_to_string(fitem) self.report_progress(0, _('Fetching feed')+' %s...'%(ftitle)) - feedarts = self.get_artlinks(item.parent.ul) + feedarts = self.get_artlinks(item.ul) feeds.append((ftitle,feedarts)) return feeds @@ -166,7 +170,8 @@ class FinancialTimes(BasicNewsRecipe): except: print "Retrying download..." count += 1 - self.temp_files.append(PersistentTemporaryFile('_fa.html')) - self.temp_files[-1].write(html) - self.temp_files[-1].close() - return self.temp_files[-1].name + tfile = PersistentTemporaryFile('_fa.html') + tfile.write(html) + tfile.close() + self.temp_files.append(tfile) + return tfile.name From 647aed399a466070c52ab265eae31b764225ddf2 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 22 Sep 2012 00:29:25 +0530 Subject: [PATCH 06/26] Explicitly disable MTP on windows XP, since some devices causes errors. See #1054273 --- src/calibre/devices/mtp/windows/driver.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/calibre/devices/mtp/windows/driver.py b/src/calibre/devices/mtp/windows/driver.py index 22079c287b..0bc8651192 100644 --- a/src/calibre/devices/mtp/windows/driver.py +++ b/src/calibre/devices/mtp/windows/driver.py @@ -13,7 +13,7 @@ from future_builtins import zip from itertools import chain from calibre import as_unicode, prints -from calibre.constants import plugins, __appname__, numeric_version +from calibre.constants import plugins, __appname__, numeric_version, isxp from calibre.ptempfile import SpooledTemporaryFile from calibre.devices.errors import OpenFailed, DeviceError, BlacklistedDevice from calibre.devices.mtp.base import MTPDeviceBase, debug @@ -55,7 +55,11 @@ class MTP_DEVICE(MTPDeviceBase): def startup(self): self.start_thread = threading.current_thread() - self.wpd, self.wpd_error = plugins['wpd'] + if isxp: + self.wpd = None + self.wpd_error = _('MTP devices are not supported on Windows XP') + else: + self.wpd, self.wpd_error = plugins['wpd'] if self.wpd is not None: try: self.wpd.init(__appname__, *(numeric_version[:3])) From 6da4736ab50f023b00bc9284c00dbd80feda396e Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 23 Sep 2012 09:51:48 +0530 Subject: [PATCH 07/26] Annoying PayPal --- manual/templates/layout.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/manual/templates/layout.html b/manual/templates/layout.html index 88e1605f92..b8389b0ac9 100644 --- a/manual/templates/layout.html +++ b/manual/templates/layout.html @@ -59,10 +59,10 @@

-
+ - +

From 6889e7b6332b3ec86016e5d94fe692bce5bab7c6 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 23 Sep 2012 09:53:06 +0530 Subject: [PATCH 08/26] Kindle driver: Prioritize sending MOBI over azw3 on e-ink devices so that people with older models can convert AZW3 to MOBI and just click send to device --- src/calibre/devices/kindle/driver.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/calibre/devices/kindle/driver.py b/src/calibre/devices/kindle/driver.py index a657b777f7..7821631e85 100644 --- a/src/calibre/devices/kindle/driver.py +++ b/src/calibre/devices/kindle/driver.py @@ -288,7 +288,7 @@ class KINDLE2(KINDLE): name = 'Kindle 2/3/4/Touch Device Interface' description = _('Communicate with the Kindle 2/3/4/Touch eBook reader.') - FORMATS = ['azw3'] + KINDLE.FORMATS + ['pdf', 'azw4', 'pobi'] + FORMATS = ['azw', 'mobi', 'azw3', 'prc', 'azw1', 'tpz', 'azw4', 'pobi', 'pdf', 'txt'] DELETE_EXTS = KINDLE.DELETE_EXTS + ['.mbp1', '.mbs', '.sdr', '.han'] # On the Touch, there's also .asc files, but not using the same basename (for X-Ray & End Actions), azw3f & azw3r files, but all of them are in the .sdr sidecar folder @@ -450,7 +450,7 @@ class KINDLE_DX(KINDLE2): name = 'Kindle DX Device Interface' description = _('Communicate with the Kindle DX eBook reader.') - FORMATS = KINDLE2.FORMATS[1:] + FORMATS = ['azw', 'mobi', 'prc', 'azw1', 'tpz', 'azw4', 'pobi', 'pdf', 'txt'] PRODUCT_ID = [0x0003] BCD = [0x0100] @@ -462,7 +462,7 @@ class KINDLE_FIRE(KINDLE2): name = 'Kindle Fire Device Interface' description = _('Communicate with the Kindle Fire') gui_name = 'Fire' - FORMATS = list(KINDLE2.FORMATS) + FORMATS = ['azw3', 'azw', 'mobi', 'prc', 'azw1', 'tpz', 'azw4', 'pobi', 'pdf', 'txt'] PRODUCT_ID = [0x0006] BCD = [0x216, 0x100] From a82ffd92b3c6a0e0251fa69ef5b09fc6346debeb Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 23 Sep 2012 11:51:36 +0530 Subject: [PATCH 09/26] MTP driver: Do not try to connect to unsuitable devices such as the iPhone. Fixes #1054562 (Error when connecting iPhone 4) --- .../mtp/windows/device_enumeration.cpp | 25 +++++++++++++++++-- src/calibre/devices/mtp/windows/driver.py | 6 +++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/src/calibre/devices/mtp/windows/device_enumeration.cpp b/src/calibre/devices/mtp/windows/device_enumeration.cpp index 2c9b48d506..9fddd6bb4d 100644 --- a/src/calibre/devices/mtp/windows/device_enumeration.cpp +++ b/src/calibre/devices/mtp/windows/device_enumeration.cpp @@ -84,8 +84,8 @@ PyObject* get_storage_info(IPortableDevice *device) { // {{{ PWSTR object_ids[10]; GUID guid; ULONGLONG capacity, free_space, capacity_objects, free_objects; - ULONG access; - LPWSTR storage_desc = NULL; + ULONG access, storage_type = WPD_STORAGE_TYPE_UNDEFINED; + LPWSTR storage_desc = NULL, st = NULL; storage = PyList_New(0); if (storage == NULL) { PyErr_NoMemory(); goto end; } @@ -116,6 +116,7 @@ PyObject* get_storage_info(IPortableDevice *device) { // {{{ hr = storage_properties->Add(WPD_STORAGE_FREE_SPACE_IN_OBJECTS); hr = storage_properties->Add(WPD_STORAGE_ACCESS_CAPABILITY); hr = storage_properties->Add(WPD_STORAGE_FILE_SYSTEM_TYPE); + hr = storage_properties->Add(WPD_STORAGE_TYPE); hr = storage_properties->Add(WPD_OBJECT_NAME); Py_END_ALLOW_THREADS; if (FAILED(hr)) {hresult_set_exc("Failed to create collection of properties for storage query", hr); goto end; } @@ -145,6 +146,7 @@ PyObject* get_storage_info(IPortableDevice *device) { // {{{ values->GetUnsignedLargeIntegerValue(WPD_STORAGE_CAPACITY_IN_OBJECTS, &capacity_objects); values->GetUnsignedLargeIntegerValue(WPD_STORAGE_FREE_SPACE_IN_BYTES, &free_space); values->GetUnsignedLargeIntegerValue(WPD_STORAGE_FREE_SPACE_IN_OBJECTS, &free_objects); + values->GetUnsignedIntegerValue(WPD_STORAGE_TYPE, &storage_type); desc = Py_False; if (SUCCEEDED(values->GetUnsignedIntegerValue(WPD_STORAGE_ACCESS_CAPABILITY, &access)) && access == WPD_STORAGE_ACCESS_CAPABILITY_READWRITE) desc = Py_True; soid = PyUnicode_FromWideChar(object_ids[i], wcslen(object_ids[i])); @@ -167,6 +169,25 @@ PyObject* get_storage_info(IPortableDevice *device) { // {{{ if (desc != NULL) { PyDict_SetItemString(so, "filesystem", desc); Py_DECREF(desc);} CoTaskMemFree(storage_desc); storage_desc = NULL; } + switch(storage_type) { + case WPD_STORAGE_TYPE_REMOVABLE_RAM: + st = L"removable_ram"; + break; + case WPD_STORAGE_TYPE_REMOVABLE_ROM: + st = L"removable_rom"; + break; + case WPD_STORAGE_TYPE_FIXED_RAM: + st = L"fixed_ram"; + break; + case WPD_STORAGE_TYPE_FIXED_ROM: + st = L"fixed_rom"; + break; + default: + st = L"unknown_unknown"; + } + desc = PyUnicode_FromWideChar(st, wcslen(st)); + if (desc != NULL) {PyDict_SetItemString(so, "type", desc); Py_DECREF(desc);} + desc = NULL; PyList_Append(storage, so); Py_DECREF(so); } diff --git a/src/calibre/devices/mtp/windows/driver.py b/src/calibre/devices/mtp/windows/driver.py index 0bc8651192..17f8a40b5b 100644 --- a/src/calibre/devices/mtp/windows/driver.py +++ b/src/calibre/devices/mtp/windows/driver.py @@ -200,6 +200,12 @@ class MTP_DEVICE(MTPDeviceBase): if not devdata.get('has_storage', False): return False has_rw_storage = False for s in devdata.get('storage', []): + if s.get('filesystem', None) == 'DCF': + # DCF filesystem indicates a camera or an iPhone + # See https://bugs.launchpad.net/calibre/+bug/1054562 + continue + if s.get('type', 'unknown_unknown').split('_')[-1] == 'rom': + continue # Read only storage if s.get('rw', False): has_rw_storage = True break From b57eb672ec03ceea4a23c651c863913e1f68d6fe Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 23 Sep 2012 12:19:53 +0530 Subject: [PATCH 10/26] ... --- resources/content_server/button-donate.png | Bin 1657 -> 5887 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/resources/content_server/button-donate.png b/resources/content_server/button-donate.png index 25ccf3f514d627e3db478ce9acaa9481fb40754e..1c15b3d13ce7440aa471f753c9c88acb20670e31 100644 GIT binary patch literal 5887 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 K)dt{O$o~P77H(ev literal 1657 zcmV-<28Q{GP)wSDT%8hZH85b zm#)|4Cy}&qoT@LIz?rNk(ADTbytJv?BNqw+(7J^%m!4RlgYQ~&?}|NsC0|NsC0{{TJcB8C6}1vW`U zK~#9!>{VNPqDT;x;53G!XGBCmGz4#>Aw(tUCS=wB|F5R1FL=r9ZuVur&uoqlR9APM zs?*)oHtudd1@0POLcWB23HiSWX?zNN3HcK89|$p938LN7qjnf>I%KvJ@!5zEu4>aL z(de|4u_1SP^y6TYyd|w;HnZG>wF6g{M_NP+XJojnL9v!6$x20Z^IIXN{v{yru%S^X z>{SbGfG36?3kC)j^9JzTAg98;3WFyf7qyOlqw^q)w^C=&4l!y2*yfRz(hnjXzQh=1 zt~zF&>^_rB;l^y7|4@ioBY~}Lds~2(&@njlM;I$;);Mi}%}uj|qXEOkwmqWt_OuNn zn{X&_QQ5BjixpVwz|P?*ll&NJtq&f?V`_5=u$|YmBE8K~2!;+YVP;A*r)h6^F1iu0cySUb$wA9H^A_5@;`OP1T4rYGv@=bo?`($PVk8&tNkHZcfEPD z3viJ9A^LlBe<0#Ju+8DM@m|OnLp|_zrZm^ITAGOXxFwyx71A@s?RZ3UyrT_98)-)1 zF_rNFf-Uq<){6qpNR=NT=rK{h*A#wo;Njd1>>eHYU;v)`8zJ(P3R9Xb*p8KXf*h}0 z`$9;3SpX$`!(A4>B?Jb*6c0#dMBWtd((X*i_Q^t&=RCM;DGG?q4U7u4jj1>^#(9SK z?zIc~&-X$`mbHR>O2-hiHpv5cRV=)gmDHua$>xhsguKKxr~vvRg9U3Yd}#PsK!TS` zWWs@}B(SzRDFQsE|MLekLJG@}*uZ1kS*ig6W zxWEmJ*(6x^r)d>2qZJUNaUa}1cc)aMfi?vA+PC|+ zDR11rj)kv-oCg_RAvtIF^4&;#u1d=~K{ap5CHS5wrY3tY#5lsGhB6GQl;HD8WMG&< zPQ*9gjdqco6fV~n^N}w>1I44wXwG*h37FBG@QG_Mcjs#!SBD$!4#SkqbPn>khOgJu zM9etDxsZ>5eaQ81!vA^h0|{o0Au~P$hK>EF&|&Nwj_-Z~%$ Date: Sun, 23 Sep 2012 15:01:01 +0530 Subject: [PATCH 11/26] PDF Output: On windows, remove any embedded fonts before generating the PDF as on windows, Qt generates image based PDFs when embedded fonts are present. Fixes #1053906 (The EPUB to PDF conversion generates the text of the EPUB file to a PDF file in graphic mode.) --- .../ebooks/conversion/plugins/pdf_output.py | 10 +++++ src/calibre/utils/fonts/embedflag.py | 45 +++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 src/calibre/utils/fonts/embedflag.py diff --git a/src/calibre/ebooks/conversion/plugins/pdf_output.py b/src/calibre/ebooks/conversion/plugins/pdf_output.py index 35504b31fb..e12f094e18 100644 --- a/src/calibre/ebooks/conversion/plugins/pdf_output.py +++ b/src/calibre/ebooks/conversion/plugins/pdf_output.py @@ -14,6 +14,8 @@ import os from calibre.customize.conversion import OutputFormatPlugin, \ OptionRecommendation from calibre.ptempfile import TemporaryDirectory +from calibre.constants import iswindows +from calibre import walk UNITS = [ 'millimeter', @@ -148,6 +150,14 @@ class PDFOutput(OutputFormatPlugin): oeb_output = plugin_for_output_format('oeb') oeb_output.convert(oeb_book, oeb_dir, self.input_plugin, self.opts, self.log) + if iswindows: + for f in walk(oeb_dir): + if f.rpartition('.')[-1].lower() in {'ttf', 'otf'}: + self.log.warn('Found embedded font %s, removing it, as ' + 'embedded fonts on windows are not supported by ' + 'the PDF Output plugin'%os.path.basename(f)) + os.remove(f) + opfpath = glob.glob(os.path.join(oeb_dir, '*.opf'))[0] opf = OPF(opfpath, os.path.dirname(opfpath)) diff --git a/src/calibre/utils/fonts/embedflag.py b/src/calibre/utils/fonts/embedflag.py new file mode 100644 index 0000000000..0c4e94bae6 --- /dev/null +++ b/src/calibre/utils/fonts/embedflag.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:fdm=marker:ai +from __future__ import (unicode_literals, division, absolute_import, + print_function) + +__license__ = 'GPL v3' +__copyright__ = '2012, Kovid Goyal ' +__docformat__ = 'restructuredtext en' + +import sys, struct + +class UnsupportedFont(ValueError): + pass + +def remove_embed_restriction(raw): + sfnt_version = raw[:4] + if sfnt_version not in {b'\x00\x01\x00\x00', b'OTTO'}: + raise UnsupportedFont('Not a supported font, sfnt_version: %r'%sfnt_version) + + num_tables = struct.unpack_from(b'>H', raw, 4)[0] + + # Find OS/2 table + offset = 4 + 4*2 # Start of the Table record entries + os2_table_offset = None + for i in xrange(num_tables): + table_tag = raw[offset:offset+4] + offset += 16 # Size of a table record + if table_tag == b'OS/2': + os2_table_offset = struct.unpack_from(b'>I', raw, offset+8)[0] + break + if os2_table_offset is None: + raise UnsupportedFont('Not a supported font, has no OS/2 table') + + version, = struct.unpack_from(b'>H', raw, os2_table_offset) + + fs_type_offset = os2_table_offset + struct.calcsize(b'>HhHH') + fs_type = struct.unpack_from(b'>H', raw, fs_type_offset)[0] + if fs_type == 0: + return raw + + return raw[:fs_type_offset] + struct.pack(b'>H', 0) + raw[fs_type_offset+2:] + +if __name__ == '__main__': + remove_embed_restriction(open(sys.argv[-1], 'rb').read()) + From 984374825fd15ab866defb812c2a87ddc4b1e36d Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 23 Sep 2012 17:10:08 +0530 Subject: [PATCH 12/26] Update Mac World and Maximum PC --- recipes/mac_world.recipe | 24 ++++++++++++----------- recipes/maximum_pc.recipe | 41 +++++++++++++++++++-------------------- 2 files changed, 33 insertions(+), 32 deletions(-) diff --git a/recipes/mac_world.recipe b/recipes/mac_world.recipe index 486aa9cb87..5abbffb6bb 100644 --- a/recipes/mac_world.recipe +++ b/recipes/mac_world.recipe @@ -34,20 +34,21 @@ class macWorld(BasicNewsRecipe): remove_javascript = True no_stylesheets = True + auto_cleanup = True - keep_only_tags = [ - dict(name='div', attrs={'id':'content'}) - ] + #keep_only_tags = [ + #dict(name='div', attrs={'id':'content'}) + #] - remove_tags = [ - {'class':['toolBar','mac_tags','toolBar btmTools','textAds']}, - dict(name='p', attrs={'class':'breadcrumbs'}), - dict(id=['breadcrumb','sidebar','comments','topContentWrapper', - 'rightColumn', 'aboveFootPromo', 'storyCarousel']), - {'class':lambda x: x and ('tools' in x or 'toolBar' - in x)} + #remove_tags = [ + #{'class':['toolBar','mac_tags','toolBar btmTools','textAds']}, + #dict(name='p', attrs={'class':'breadcrumbs'}), + #dict(id=['breadcrumb','sidebar','comments','topContentWrapper', + #'rightColumn', 'aboveFootPromo', 'storyCarousel']), + #{'class':lambda x: x and ('tools' in x or 'toolBar' + #in x)} - ] + #] feeds = [ (u'MacWorld Headlines', u'http://rss.macworld.com/macworld/news'), @@ -82,3 +83,4 @@ class macWorld(BasicNewsRecipe): .articleInfo {color:#4D4D4D;font-family:Arial,Helvetica,sans-serif;font-size:10px; font-size-adjust:none; font-stretch:normal; font-style:bold; font-variant:normal; font-weight:bold; line-height:10px; text-decoration:none;} img {align:left;} ''' + diff --git a/recipes/maximum_pc.recipe b/recipes/maximum_pc.recipe index 3e4d8a58d9..c6e8099fcf 100644 --- a/recipes/maximum_pc.recipe +++ b/recipes/maximum_pc.recipe @@ -1,4 +1,3 @@ -from calibre.ptempfile import PersistentTemporaryFile from calibre.web.feeds.news import BasicNewsRecipe class AdvancedUserRecipe1276930924(BasicNewsRecipe): @@ -14,30 +13,30 @@ class AdvancedUserRecipe1276930924(BasicNewsRecipe): use_embedded_content = False no_stylesheets = True language = 'en' - temp_files = [] - articles_are_obfuscated = True - feeds = [(u'News', u'http://www.maximumpc.com/articles/4/feed'), + auto_cleanup = True + feeds = [#(u'News', u'http://www.maximumpc.com/articles/all/feed'), + (u'News', u'http://www.maximumpc.com/articles/4/feed'), (u'Reviews', u'http://www.maximumpc.com/articles/40/feed'), (u'Editors Blog', u'http://www.maximumpc.com/articles/6/feed'), (u'How-to', u'http://www.maximumpc.com/articles/32/feed'), (u'Features', u'http://www.maximumpc.com/articles/31/feed'), (u'From the Magazine', u'http://www.maximumpc.com/articles/72/feed') ] - keep_only_tags = [ - dict(name='div', attrs={'class':['print-title','article_body']}), - ] - remove_tags = [ - dict(name='div', attrs={'class':'comments-tags-actions'}), - ] - remove_tags_before = dict(name='div', attrs={'class':'print-title'}) - remove_tags_after = dict(name='div', attrs={'class':'meta-content'}) + #keep_only_tags = [ + #dict(name='div', attrs={'class':['print-title','article_body']}), + #] + #remove_tags = [ + #dict(name='div', attrs={'class':'comments-tags-actions'}), + #] + #remove_tags_before = dict(name='div', attrs={'class':'print-title'}) + #remove_tags_after = dict(name='div', attrs={'class':'meta-content'}) - def get_obfuscated_article(self, url): - br = self.get_browser() - br.open(url) - response = br.follow_link(url_regex = r'/print/[0-9]+', nr = 0) - html = response.read() - self.temp_files.append(PersistentTemporaryFile('_fa.html')) - self.temp_files[-1].write(html) - self.temp_files[-1].close() - return self.temp_files[-1].name + #def get_obfuscated_article(self, url): + #br = self.get_browser() + #br.open(url) + #response = br.follow_link(url_regex = r'/print/[0-9]+', nr = 0) + #html = response.read() + #self.temp_files.append(PersistentTemporaryFile('_fa.html')) + #self.temp_files[-1].write(html) + #self.temp_files[-1].close() + #return self.temp_files[-1].name From 233d9021075f190f280a4fe39b07bb5683b1c555 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 23 Sep 2012 19:06:35 +0530 Subject: [PATCH 13/26] ... --- src/calibre/devices/prst1/driver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/devices/prst1/driver.py b/src/calibre/devices/prst1/driver.py index 8b76255532..4cbe9b4994 100644 --- a/src/calibre/devices/prst1/driver.py +++ b/src/calibre/devices/prst1/driver.py @@ -687,7 +687,7 @@ class PRST1(USBMS): 'WHERE _id = ?') t = (collectionId,) cursor.execute(query, t) - debug_print('Deleted Collection: ' + collection) + debug_print('Deleted Collection: ' + repr(collection)) connection.commit() cursor.close() From e5d1e81e17df63676fda8756765e6e8d4c6902a9 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 23 Sep 2012 21:10:23 +0530 Subject: [PATCH 14/26] ... --- src/calibre/ebooks/conversion/plugins/pdf_output.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/calibre/ebooks/conversion/plugins/pdf_output.py b/src/calibre/ebooks/conversion/plugins/pdf_output.py index e12f094e18..b3eed763ac 100644 --- a/src/calibre/ebooks/conversion/plugins/pdf_output.py +++ b/src/calibre/ebooks/conversion/plugins/pdf_output.py @@ -151,6 +151,8 @@ class PDFOutput(OutputFormatPlugin): oeb_output.convert(oeb_book, oeb_dir, self.input_plugin, self.opts, self.log) if iswindows: + # On windows Qt generates an image based PDF if the html uses + # embedded fonts. See https://launchpad.net/bugs/1053906 for f in walk(oeb_dir): if f.rpartition('.')[-1].lower() in {'ttf', 'otf'}: self.log.warn('Found embedded font %s, removing it, as ' From 761cb5509c20fae8b86f429d5fb42f632a4a6d4f Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 23 Sep 2012 21:52:36 +0530 Subject: [PATCH 15/26] AZW3 Output: Fix handling of & < and > entities in the text. They were being incorrectly unescaped. --- src/calibre/ebooks/mobi/writer8/skeleton.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/calibre/ebooks/mobi/writer8/skeleton.py b/src/calibre/ebooks/mobi/writer8/skeleton.py index ae8fdf364c..2c3562e87b 100644 --- a/src/calibre/ebooks/mobi/writer8/skeleton.py +++ b/src/calibre/ebooks/mobi/writer8/skeleton.py @@ -10,6 +10,7 @@ __docformat__ = 'restructuredtext en' import re from collections import namedtuple from functools import partial +from xml.sax.saxutils import escape from lxml import etree @@ -289,6 +290,7 @@ class Chunker(object): self.chunk_selector = ('S', aid) def chunk_up_text(self, text): + text = escape(text) text = text.encode('utf-8') ans = [] From 50e9e1f768826d6b0c9532048ad4ce9c966d258f Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 24 Sep 2012 08:09:24 +0530 Subject: [PATCH 16/26] Fix #1055129 (Xoom MTP 'Send to Main' send to SDCARD) --- src/calibre/devices/mtp/windows/driver.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/calibre/devices/mtp/windows/driver.py b/src/calibre/devices/mtp/windows/driver.py index 17f8a40b5b..e349ddf5e0 100644 --- a/src/calibre/devices/mtp/windows/driver.py +++ b/src/calibre/devices/mtp/windows/driver.py @@ -290,6 +290,8 @@ class MTP_DEVICE(MTPDeviceBase): raise BlacklistedDevice( 'The %s device has been blacklisted by the user'%(connected_device,)) + storage.sort(key=lambda x:x.get('id', 'zzzzz')) + self._main_id = storage[0]['id'] if len(storage) > 1: self._carda_id = storage[1]['id'] From c87508d20e22bc37a74f2b248eaf59d4f7add49c Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 24 Sep 2012 10:25:55 +0530 Subject: [PATCH 17/26] MTP: Add a way to get device info in the GUI easily --- src/calibre/devices/mtp/unix/driver.py | 13 +++++++ src/calibre/devices/mtp/windows/driver.py | 6 ++++ src/calibre/gui2/device_drivers/mtp_config.py | 35 ++++++++++++++++--- 3 files changed, 49 insertions(+), 5 deletions(-) diff --git a/src/calibre/devices/mtp/unix/driver.py b/src/calibre/devices/mtp/unix/driver.py index 599304e851..760113c366 100644 --- a/src/calibre/devices/mtp/unix/driver.py +++ b/src/calibre/devices/mtp/unix/driver.py @@ -196,6 +196,19 @@ class MTP_DEVICE(MTPDeviceBase): self.current_serial_num = snum self.currently_connected_dev = connected_device + @synchronous + def device_debug_info(self): + ans = self.get_gui_name() + ans += '\nSerial number: %s'%self.current_serial_num + ans += '\nManufacturer: %s'%self.dev.manufacturer_name + ans += '\nModel: %s'%self.dev.model_name + ans += '\nids: %s'%(self.dev.ids,) + ans += '\nDevice version: %s'%self.dev.device_version + ans += '\nStorage:\n' + storage = sorted(self.dev.storage_info, key=operator.itemgetter('id')) + ans += pprint.pformat(storage) + return ans + @property def filesystem_cache(self): if self._filesystem_cache is None: diff --git a/src/calibre/devices/mtp/windows/driver.py b/src/calibre/devices/mtp/windows/driver.py index e349ddf5e0..202c8dfd6e 100644 --- a/src/calibre/devices/mtp/windows/driver.py +++ b/src/calibre/devices/mtp/windows/driver.py @@ -52,6 +52,7 @@ class MTP_DEVICE(MTPDeviceBase): self.start_thread = None self._filesystem_cache = None self.eject_dev_on_next_scan = False + self.current_device_data = {} def startup(self): self.start_thread = threading.current_thread() @@ -303,6 +304,11 @@ class MTP_DEVICE(MTPDeviceBase): _('Unknown MTP device')) self.currently_connected_pnp_id = connected_device self.current_serial_num = snum + self.current_device_data = devdata.copy() + + def device_debug_info(self): + import pprint + return pprint.pformat(self.current_device_data) @same_thread def get_basic_device_information(self): diff --git a/src/calibre/gui2/device_drivers/mtp_config.py b/src/calibre/gui2/device_drivers/mtp_config.py index 9fd59ab124..dbb31a3e3d 100644 --- a/src/calibre/gui2/device_drivers/mtp_config.py +++ b/src/calibre/gui2/device_drivers/mtp_config.py @@ -12,7 +12,8 @@ import weakref from PyQt4.Qt import (QWidget, QListWidgetItem, Qt, QToolButton, QLabel, QTabWidget, QGridLayout, QListWidget, QIcon, QLineEdit, QVBoxLayout, QPushButton, QGroupBox, QScrollArea, QHBoxLayout, QComboBox, - pyqtSignal, QSizePolicy, QDialog, QDialogButtonBox) + pyqtSignal, QSizePolicy, QDialog, QDialogButtonBox, QPlainTextEdit, + QApplication) from calibre.ebooks import BOOK_EXTENSIONS from calibre.gui2 import error_dialog @@ -372,15 +373,19 @@ class MTPConfig(QTabWidget): _('&Ignore the %s in calibre')%device.current_friendly_name, self.base) b.clicked.connect(self.ignore_device) + self.show_debug_button = bd = QPushButton(QIcon(I('debug.png')), + _('Show device information')) + bd.clicked.connect(self.show_debug_info) l.addWidget(b, 0, 0, 1, 2) l.addWidget(la, 1, 0, 1, 1) - l.addWidget(self.formats, 2, 0, 3, 1) + l.addWidget(self.formats, 2, 0, 4, 1) l.addWidget(self.send_to, 2, 1, 1, 1) l.addWidget(self.template, 3, 1, 1, 1) - l.setRowStretch(4, 10) - l.addWidget(r, 5, 0, 1, 2) - l.setRowStretch(5, 100) + l.addWidget(self.show_debug_button, 4, 1, 1, 1) + l.setRowStretch(5, 10) + l.addWidget(r, 6, 0, 1, 2) + l.setRowStretch(6, 100) self.igntab = IgnoredDevices(self.device.prefs['history'], self.device.prefs['blacklist']) @@ -388,6 +393,26 @@ class MTPConfig(QTabWidget): self.setCurrentIndex(1 if msg else 0) + def show_debug_info(self): + info = self.device.device_debug_info() + d = QDialog(self) + d.l = l = QVBoxLayout() + d.setLayout(l) + d.v = v = QPlainTextEdit() + d.setWindowTitle(self.device.get_gui_name()) + v.setPlainText(info) + v.setMinimumWidth(400) + v.setMinimumHeight(350) + l.addWidget(v) + bb = d.bb = QDialogButtonBox(QDialogButtonBox.Close) + bb.accepted.connect(d.accept) + bb.rejected.connect(d.reject) + l.addWidget(bb) + bb.addButton(_('Copy to clipboard'), bb.ActionRole) + bb.clicked.connect(lambda : + QApplication.clipboard().setText(v.toPlainText())) + d.exec_() + def ignore_device(self): self.igntab.ignore_device(self.device.current_serial_num) self.base.b.setEnabled(False) From 952f5709b031b1d38fe55c7d09b46c4135dbd98a Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 25 Sep 2012 01:15:39 +0530 Subject: [PATCH 18/26] Update Baltimore Sun --- recipes/baltimore_sun.recipe | 147 +++++++++++++++++++---------------- 1 file changed, 82 insertions(+), 65 deletions(-) diff --git a/recipes/baltimore_sun.recipe b/recipes/baltimore_sun.recipe index ac6906a5e6..7c55bfd5fe 100644 --- a/recipes/baltimore_sun.recipe +++ b/recipes/baltimore_sun.recipe @@ -1,45 +1,37 @@ from __future__ import with_statement __license__ = 'GPL 3' -__copyright__ = 'Original 2009, Kovid Goyal ' -__copyright__= 'Modified 2011, Josh Hall ' +__copyright__ = '2009, Kovid Goyal ' +__copyright__ = '2012 Josh Hall' __docformat__ = 'restructuredtext en' -''' -www.baltimoresun.com -''' - +import urllib, re from calibre.web.feeds.news import BasicNewsRecipe class BaltimoreSun(BasicNewsRecipe): title = 'The Baltimore Sun' __author__ = 'Josh Hall' - description = 'Politics, local and business news from Baltimore' - language = 'en' + + description = 'Complete local news and blogs from Baltimore' + language = 'en' + version = 2 oldest_article = 1 max_articles_per_feed = 100 - remove_empty_feeds = True - use_embedded_content = False - no_stylesheets = True - remove_javascript = True - #masthead_url = 'http://www.baltimoresun.com/images/thirdpartylogo.gif' - - remove_tags_before = dict(name='div', attrs={'class':['story', 'entry']}) - remove_tags_after = [ - {'class':['photo_article',]}, - dict(name='div', attrs={'class':'shirttail-promo right clearfix'}), - ] + use_embedded_content = False + no_stylesheets = True + remove_javascript = True + recursions = 1 keep_only_tags = [dict(name='div', attrs={'class':["story","entry-asset asset hentry"]}), dict(name='div', attrs={'id':["pagebody","story","maincontentcontainer"]}), ] + remove_tags_after = [{'class':['photo_article',]}] + match_regexps = [r'page=[0-9]+'] - remove_tags = [{'id':["moduleArticleTools","content-bottom","rail","articleRelates module","toolSet","relatedrailcontent","div-wrapper","beta","atp-comments","footer","article-promo"]}, - {'class':["entry-footer-left","entry-footer-right","shirttail-promo right clearfix","clearfix","relatedTitle","articleRelates module","asset-footer","tools","comments","featurePromo","featurePromo fp-topjobs brownBackground","clearfix fullSpan brownBackground","curvedContent","toppaginate","module","module-header","module-content"]}, - dict(name='font',attrs={'id':["cr-other-headlines"]}), - dict(name=['iframe']), - ] + remove_tags = [{'id':["moduleArticleTools","content-bottom","rail","articleRelates module","toolSet","relatedrailcontent","div-wrapper","beta","atp-comments","footer",'gallery-subcontent','subFooter']}, + {'class':["clearfix","relatedTitle","articleRelates module","asset-footer","tools","comments","featurePromo","featurePromo fp-topjobs brownBackground","clearfix fullSpan brownBackground","curvedContent",'nextgen-share-tools','outbrainTools', 'google-ad-story-bottom']}, + dict(name='font',attrs={'id':["cr-other-headlines"]})] extra_css = ''' h1{font-family:Arial,Helvetica,sans-serif; font-weight:bold;font-size:large;} h2{font-family:Arial,Helvetica,sans-serif; font-weight:normal;font-size:small;} @@ -53,8 +45,9 @@ class BaltimoreSun(BasicNewsRecipe): .maincontentcontainer{font-family:Arial,Helvetica,sans-serif;font-size:small;} .story-body{font-family:Arial,Helvetica,sans-serif;font-size:small;} body{font-family:Helvetica,Arial,sans-serif;font-size:small;} - ''' + ''' feeds = [ +## News ## (u'Top Headlines', u'http://www.baltimoresun.com/rss2.0.xml'), (u'Breaking News', u'http://www.baltimoresun.com/news/breaking/rss2.0.xml'), (u'Top Maryland', u'http://www.baltimoresun.com/news/maryland/rss2.0.xml'), @@ -69,10 +62,10 @@ class BaltimoreSun(BasicNewsRecipe): (u'Local Politics', u'http://www.baltimoresun.com/news/maryland/politics/rss2.0.xml'), (u'Weather', u'http://www.baltimoresun.com/news/weather/rss2.0.xml'), #(u'Traffic', u'http://www.baltimoresun.com/features/commuting/rss2.0.xml'), - (u'Nation/world', u'http://feeds.chicagotribune.com/chicagotribune/news/nationworld/'), + (u'Nation/world', u'http://feeds.feedburner.com/baltimoresun/news/nationworld/rss2'), (u'Weird News', u'http://www.baltimoresun.com/news/offbeat/rss2.0.xml'), - +##Sports## (u'Top Sports', u'http://www.baltimoresun.com/sports/rss2.0.xml'), (u'Orioles/Baseball', u'http://www.baltimoresun.com/sports/orioles/rss2.0.xml'), (u'Ravens/Football', u'http://www.baltimoresun.com/sports/ravens/rss2.0.xml'), @@ -85,6 +78,7 @@ class BaltimoreSun(BasicNewsRecipe): #(u'High School', u'http://www.baltimoresun.com/sports/high-school/rss2.0.xml'), #(u'Outdoors', u'http://www.baltimoresun.com/sports/outdoors/rss2.0.xml'), +## Entertainment ## (u'Celebrity News', u'http://www.baltimoresun.com/entertainment/celebrities/rss2.0.xml'), (u'Arts & Theater', u'http://www.baltimoresun.com/entertainment/arts/rss2.0.xml'), (u'Movies', u'http://www.baltimoresun.com/entertainment/movies/rss2.0.xml'), @@ -92,14 +86,16 @@ class BaltimoreSun(BasicNewsRecipe): (u'Restaurants & Food', u'http://www.baltimoresun.com/entertainment/dining/rss2.0.xml'), (u'TV/Media', u'http://www.baltimoresun.com/entertainment/tv/rss2.0.xml'), +## Life ## (u'Health&Wellness', u'http://www.baltimoresun.com/health/rss2.0.xml'), (u'Home & Garden', u'http://www.baltimoresun.com/features/home-garden/rss2.0.xml'), (u'Living Green', u'http://www.baltimoresun.com/features/green/rss2.0.xml'), (u'Parenting', u'http://www.baltimoresun.com/features/parenting/rss2.0.xml'), (u'Fashion', u'http://www.baltimoresun.com/features/fashion/rss2.0.xml'), (u'Travel', u'http://www.baltimoresun.com/travel/rss2.0.xml'), - (u'Faith', u'http://www.baltimoresun.com/features/faith/rss2.0.xml'), + #(u'Faith', u'http://www.baltimoresun.com/features/faith/rss2.0.xml'), +## Business ## (u'Top Business', u'http://www.baltimoresun.com/business/rss2.0.xml'), (u'Technology', u'http://www.baltimoresun.com/business/technology/rss2.0.xml'), (u'Personal finance', u'http://www.baltimoresun.com/business/money/rss2.0.xml'), @@ -109,12 +105,14 @@ class BaltimoreSun(BasicNewsRecipe): (u'Consumer Safety', u'http://www.baltimoresun.com/business/consumer-safety/rss2.0.xml'), (u'Investing', u'http://www.baltimoresun.com/business/money/rss2.0.xml'), +## Opinion## (u'Sun Editorials', u'http://www.baltimoresun.com/news/opinion/editorial/rss2.0.xml'), (u'Op/Ed', u'http://www.baltimoresun.com/news/opinion/oped/rss2.0.xml'), (u'Readers Respond', u'http://www.baltimoresun.com/news/opinion/readersrespond/'), - (u'Kevin Cowherd', 'http://www.baltimoresun.com/sports/bal-columnist-cowherd,0,6829726.columnist-rss2.0.xml'), - (u'Jay Hancock', u'http://www.baltimoresun.com/business/money/bal-columnist-hancock,0,6673611.columnist-rss2.0.xml'), +## Columnists ## + (u'Kevin Cowherd', u'http://www.baltimoresun.com/sports/bal-columnist-cowherd,0,6829726.columnist-rss2.0.xml'), + (u'Robert Ehrlich', u'http://www.baltimoresun.com/news/opinion/columnists/bal-columnist-ehrlich,0,1825227.columnist-rss2.0.xml'), (u'Jacques Kelly', u'http://www.baltimoresun.com/news/maryland/bal-columnist-kelly,0,1154701.columnist-rss2.0.xml'), (u'Marta H. Mossburg', u'http://www.baltimoresun.com/news/opinion/oped/bal-columnist-mossburg,0,7982155.columnist-rss2.0.xml'), (u'Mike Preston', u'http://www.baltimoresun.com/sports/bal-columnist-preston,0,6169796.columnist-rss2.0.xml'), @@ -122,59 +120,80 @@ class BaltimoreSun(BasicNewsRecipe): (u'Dan Rodricks', u'http://www.baltimoresun.com/news/maryland/bal-columnist-rodricks,0,7089843.columnist-rss2.0.xml'), (u'Thomas F. Schaller', u'http://www.baltimoresun.com/news/opinion/columnists/bal-columnist-schaller,0,897397.columnist-rss2.0.xml'), (u'Peter Schmuck', u'http://www.baltimoresun.com/sports/bal-columnist-schmuck,0,7485088.columnist-rss2.0.xml'), - (u'Ron Smith', u'http://www.baltimoresun.com/news/opinion/bal-columnist-ronsmith,0,3964803.columnist-rss2.0.xml'), - (u'Baltimore Crime Beat', u'http://weblogs.baltimoresun.com/news/crime/blog/index.xml'), - (u'Getting There', u'http://weblogs.baltimoresun.com/news/traffic/index.xml'), - (u'InsideEd', u'http://weblogs.baltimoresun.com/news/education/blog/index.xml'), - (u'Maryland Politics', u'http://weblogs.baltimoresun.com/news/local/politics/index.xml'), - (u'Maryland Weather', u'http://weblogs.marylandweather.com/index.xml'), - (u'Second Opinion', u'http://weblogs.baltimoresun.com/news/opinion/index.xml'), - (u'You Dont Say', u'http://weblogs.baltimoresun.com/news/mcintyre/blog/index.xml'), +## News Blogs ## + (u'Baltimore Crime Beat', u'http://baltimore.feedsportal.com/c/34255/f/623075/index.rss'), + (u'InsideEd', u'http://www.baltimoresun.com/news/maryland/education/blog/rss2.0.xml'), + (u'Maryland Politics', u'http://www.baltimoresun.com/news/maryland/politics/blog/rss2.0.xml'), + (u'Maryland Weather', u'http://www.baltimoresun.com/news/weather/weather-blog/rss2.0.xml'), + (u'Second Opinion', u'http://www.baltimoresun.com/news/opinion/second-opinion-blog/rss2.0.xml'), + (u'Sun Investigates', u'http://www.baltimoresun.com/news/maryland/sun-investigates/rss2.0.xml'), + (u'You Dont Say', u'http://www.baltimoresun.com/news/language-blog/rss2.0.xml'), - (u'BaltTech', u'http://weblogs.baltimoresun.com/news/technology/index.xml'), - (u'Consuming Interests', u'http://weblogs.baltimoresun.com/business/consuminginterests/blog/index.xml'), - (u'Jay Hancocks Blog', u'http://weblogs.baltimoresun.com/business/hancock/blog/index.xml'), - (u'The Real Estate Wonk', u'http://weblogs.baltimoresun.com/business/realestate/blog/index.xml'), +## Business Blogs ## + (u'BaltTech', u'http://www.baltimoresun.com/business/technology/blog/rss2.0.xml'), + (u'Consuming Interests', u'http://www.baltimoresun.com/business/consuming-interests-blog/rss2.0.xml'), + (u'The Real Estate Wonk', u'http://www.baltimoresun.com/business/real-estate/wonk/rss2.0.xml'), - (u'Clef Notes', 'http://weblogs.baltimoresun.com/entertainment/classicalmusic/index.xml'), - (u'Dining at Large', u'http://weblogs.baltimoresun.com/entertainment/dining/reviews/blog/index.xml'), - (u'Midnight Sun', u'http://weblogs.baltimoresun.com/entertainment/midnight_sun/blog/index.xml'), - (u'Mike Sragow Gets Reel', u'http://weblogs.baltimoresun.com/entertainment/movies/blog/index.xml'), - (u'Read Street', u'http://weblogs.baltimoresun.com/entertainment/books/blog/index.xml'), - (u'Reality Check', u'http://weblogs.baltimoresun.com/entertainment/realitycheck/blog/index.xml'), - (u'Z on TV', u'http://weblogs.baltimoresun.com/entertainment/zontv/index.xml'), +## Entertainment Blogs ## + (u'Clef Notes & Drama Queens', 'http://weblogs.baltimoresun.com/entertainment/classicalmusic/index.xml'), + (u'Baltimore Diner', u'http://baltimore.feedsportal.com/c/34255/f/623088/index.rss'), + (u'Midnight Sun', u'http://www.baltimoresun.com/entertainment/music/midnight-sun-blog/rss2.0.xml'), + (u'Read Street', u'http://www.baltimoresun.com/features/books/read-street/rss2.0.xml'), + (u'Z on TV', u'http://www.baltimoresun.com/entertainment/tv/z-on-tv-blog/rss2.0.xml'), +## Life Blogs ## (u'BMore Green', u'http://weblogs.baltimoresun.com/features/green/index.xml'), - (u'Charm City Moms', u'http://weblogs.baltimoresun.com/features/baltimoremomblog/index.xml'), - (u'Exercists', u'http://weblogs.baltimoresun.com/health/fitness/index.xml'), - (u'Garden Variety', 'http://weblogs.baltimoresun.com/features/gardening/index.xml'), - #(u'In Good Faith', u'http://weblogs.baltimoresun.com/news/faith/index.xml'), - (u'Picture of Health', u'http://weblogs.baltimoresun.com/health/index.xml'), + (u'Baltimore Insider',u'http://www.baltimoresun.com/features/baltimore-insider-blog/rss2.0.xml'), + (u'Homefront', u'http://www.baltimoresun.com/features/parenting/homefront/rss2.0.xml'), + (u'Picture of Health', u'http://www.baltimoresun.com/health/blog/rss2.0.xml'), (u'Unleashed', u'http://weblogs.baltimoresun.com/features/mutts/blog/index.xml'), +## b the site blogs ## + (u'Game Cache', u'http://www.baltimoresun.com/entertainment/bthesite/game-cache/rss2.0.xml'), + (u'TV Lust', u'http://www.baltimoresun.com/entertainment/bthesite/tv-lust/rss2.0.xml'), + +## Sports Blogs ## + (u'Baltimore Sports Blitz', u'http://baltimore.feedsportal.com/c/34255/f/623097/index.rss'), #(u'Faceoff', u'http://weblogs.baltimoresun.com/sports/lacrosse/blog/index.xml'), #(u'MMA Stomping Grounds', u'http://weblogs.baltimoresun.com/sports/mma/blog/index.xml'), - (u'Orioles Insider', u'http://weblogs.baltimoresun.com/sports/orioles/blog/index.xml'), - #(u'Outdoors Girl', u'http://weblogs.baltimoresun.com/sports/outdoors/blog/index.xml'), - (u'Ravens Insider', u'http://weblogs.baltimoresun.com/sports/ravens/blog/index.xml'), + (u'Orioles Insider', u'http://baltimore.feedsportal.com/c/34255/f/623100/index.rss'), + (u'Ravens Insider', u'http://www.baltimoresun.com/sports/ravens/ravens-insider/rss2.0.xml'), #(u'Recruiting Report', u'http://weblogs.baltimoresun.com/sports/college/recruiting/index.xml'), #(u'Ring Posts', u'http://weblogs.baltimoresun.com/sports/wrestling/blog/index.xml'), - (u'The Schmuck Stops Here', u'http://weblogs.baltimoresun.com/sports/schmuck/index.xml'), - (u'Toy Department', u'http://weblogs.baltimoresun.com/sports/thetoydepartment/index.xml'), + (u'The Schmuck Stops Here', u'http://www.baltimoresun.com/sports/schmuck-blog/rss2.0.xml'), #(u'Tracking the Terps', u'http://weblogs.baltimoresun.com/sports/college/maryland_terps/blog/index.xml'), #(u'Varsity Letters', u'http://weblogs.baltimoresun.com/sports/highschool/varsityletters/index.xml'), - (u'Virtual Vensanity', u'http://weblogs.baltimoresun.com/entertainment/bthesite/vensel/index.xml'), - ] def get_article_url(self, article): - print article.get('feedburner_origlink', article.get('guid', article.get('link'))) - return article.get('feedburner_origlink', article.get('guid', article.get('link'))) + ans = None + try: + s = article.summary + ans = urllib.unquote( + re.search(r'href=".+?bookmark.cfm.+?link=(.+?)"', s).group(1)) + except: + pass + if ans is None: + ans = article.get('feedburner_origlink', article.get('guid', article.get('link'))) + if ans is not None: + return ans.replace('?track=rss', '') + def skip_ad_pages(self, soup): + text = soup.find(text='click here to continue to article') + if text: + a = text.parent + url = a.get('href') + if url: + return self.index_to_soup(url, raw=True) def postprocess_html(self, soup, first_fetch): + # Remove the navigation bar. It was kept until now to be able to follow + # the links to further pages. But now we don't need them anymore. + for nav in soup.findAll(attrs={'class':['toppaginate','article-nav clearfix']}): + nav.extract() + for t in soup.findAll(['table', 'tr', 'td']): t.name = 'div' @@ -182,5 +201,3 @@ class BaltimoreSun(BasicNewsRecipe): tag.extract() for tag in soup.findAll('font', dict(attrs={'id':["cr-other-headlines"]})): tag.extract() - - return soup From 984d2b8b766dfaa3b9b8736baa692e2df634e466 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 25 Sep 2012 11:08:02 +0530 Subject: [PATCH 19/26] Update chronicle of higher education --- recipes/chronicle_higher_ed.recipe | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/recipes/chronicle_higher_ed.recipe b/recipes/chronicle_higher_ed.recipe index f0188d4d77..15b284cd7a 100644 --- a/recipes/chronicle_higher_ed.recipe +++ b/recipes/chronicle_higher_ed.recipe @@ -1,3 +1,4 @@ +import re from calibre.web.feeds.recipes import BasicNewsRecipe from collections import OrderedDict @@ -14,7 +15,8 @@ class Chronicle(BasicNewsRecipe): dict(name='div', attrs={'class':'article'}), ] remove_tags = [dict(name='div',attrs={'class':['related module1','maintitle']}), - dict(name='div', attrs={'id':['section-nav','icon-row']})] + dict(name='div', attrs={'id':['section-nav','icon-row', 'enlarge-popup']}), + dict(name='a', attrs={'class':'show-enlarge enlarge'})] no_javascript = True no_stylesheets = True @@ -31,7 +33,6 @@ class Chronicle(BasicNewsRecipe): return br def parse_index(self): - #Go to the issue soup0 = self.index_to_soup('http://chronicle.com/section/Archives/39/') issue = soup0.find('ul',attrs={'class':'feature-promo-list'}).li @@ -42,9 +43,12 @@ class Chronicle(BasicNewsRecipe): self.timefmt = u' [%s]'%dates #Find cover - cover=soup0.find('div',attrs={'class':'promo'}).findNext('div') - self.cover_url="http://chronicle.com"+cover.find('img')['src'] - + cover=soup0.find('div',attrs={'class':'side-content'}).find(attrs={'src':re.compile("photos/biz/Current")}) + if cover is not None: + if "chronicle.com" in cover['src']: + self.cover_url=cover['src'] + else: + self.cover_url="http://chronicle.com" + cover['src'] #Go to the main body soup = self.index_to_soup(issueurl) div = soup.find ('div', attrs={'id':'article-body'}) @@ -74,8 +78,10 @@ class Chronicle(BasicNewsRecipe): def preprocess_html(self,soup): #process all the images for div in soup.findAll('div', attrs={'class':'tableauPlaceholder'}): + noscripts=div.find('noscript').a div.replaceWith(noscripts) for div0 in soup.findAll('div',text='Powered by Tableau'): div0.extract() return soup + From 05b0a097acbc7304c5a63f71726f11eb3a035c9d Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 25 Sep 2012 11:43:42 +0530 Subject: [PATCH 20/26] Revert the change to ignore the A: and B: drives on windows --- src/calibre/utils/windows/winutil.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/calibre/utils/windows/winutil.c b/src/calibre/utils/windows/winutil.c index 6b23f47c6d..53ebfcca89 100644 --- a/src/calibre/utils/windows/winutil.c +++ b/src/calibre/utils/windows/winutil.c @@ -295,10 +295,10 @@ get_all_removable_disks(struct tagDrives *g_drives) for(nLoopIndex = 0; nLoopIndex < MAX_DRIVES; nLoopIndex++) { - // if a drive is present (we ignore the A and B drives as they are - // always present (even if no actual floppy is present) and we dont - // care about floppies) - if(nLoopIndex > 1 && dwDriveMask & 1) + // if a drive is present (we cannot ignore the A and B drives as there + // are people out there that think mapping devices to use those letters + // is a good idea, sigh) + if(dwDriveMask & 1) { caDrive[0] = 'A' + nLoopIndex; From 0ec5e231dc63be4f559470cf9a250b2915be7b27 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 25 Sep 2012 19:31:36 +0530 Subject: [PATCH 21/26] Fix #1056178 (Updated recipe for El Pais) --- recipes/elpais_impreso.recipe | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/recipes/elpais_impreso.recipe b/recipes/elpais_impreso.recipe index ffa1033477..2dbd79d094 100644 --- a/recipes/elpais_impreso.recipe +++ b/recipes/elpais_impreso.recipe @@ -41,7 +41,7 @@ class ElPais_RSS(BasicNewsRecipe): ,dict(attrs={'class':['firma','columna_texto','entrevista_p_r']}) ] remove_tags = [ - dict(name=['meta','link','base','iframe','embed','object']) + dict(name=['iframe','embed','object']) ,dict(attrs={'class':'disposicion_vertical'}) ] @@ -74,13 +74,14 @@ class ElPais_RSS(BasicNewsRecipe): ,(u'Justicia y Leyes' , u'http://elpais.com/tag/rss/justicia/a/' ) ,(u'Guerras y conflictos' , u'http://elpais.com/tag/rss/conflictos/a/' ) ,(u'Politica' , u'http://ep00.epimg.net/rss/politica/portada.xml' ) - ,(u'Opinion' , u'http://ep01.epimg.net/rss/politica/opinion.xml' ) + ,(u'Opinion' , u'http://ep01.epimg.net/rss/elpais/opinion.xml' ) ] def get_article_url(self, article): url = BasicNewsRecipe.get_article_url(self, article) if url and (not('/album/' in url) and not('/futbol/partido/' in url)): - return url + urlverified = self.browser.open_novisit(url).geturl() + return urlverified self.log('Skipping non-article', url) return None @@ -107,3 +108,7 @@ class ElPais_RSS(BasicNewsRecipe): for item in soup.findAll('img',alt=False): item['alt'] = 'image' return soup + + def preprocess_raw_html(self, raw, url): + return 'Untitled'+raw[raw.find(''):] + \ No newline at end of file From 075fbec828365e15bb86a6cdbaf7b0123af6517b Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 26 Sep 2012 10:24:13 +0530 Subject: [PATCH 22/26] Fix #1056270 (Updated recipe for Monitor online) --- recipes/icons/monitor.png | Bin 0 -> 1094 bytes recipes/monitor.recipe | 117 ++++++++++++++------------------------ 2 files changed, 42 insertions(+), 75 deletions(-) create mode 100644 recipes/icons/monitor.png diff --git a/recipes/icons/monitor.png b/recipes/icons/monitor.png new file mode 100644 index 0000000000000000000000000000000000000000..89aa1fd3991bccbfe9adfcec409618a068b18791 GIT binary patch literal 1094 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GXl4m>B|mLR`=H_0JI!+Mk#IaOch& zOP6hMb)6$B`s4NMoiQ=LK7YP4drlt%!+a^JRc7WFCQh8f$ha>v>)V?*YaE@<_4Ljb z5cqWe{+H*^`xzL1fBQCDSa`m&^3{3s?rqw9FfZ@t$B#4l_%6?!HIj*V@`nVr1MF z60$!d^ZV=9vn3=>)YpG~_OzFQ;qktGz*rasgCPV~{D@r3z`)4p>EaktF-LSlrZQ8Y zMC*OWP3sG1>uBD#p0=@PEziOeVK>_(rW;(EohuOe>*G6H`E%{hFIVyj+;qEHka8+< zHiw3EMfCNQDc+_NR6mGIuDpDrI6<@^Yh|&u{O_H)r)E+j_eJ$a=JFXMuIJ0Z(pzC8&&>U$zQgz?sqmh zt@EcwCR6vwx_i4HB>dTO?a9i&yc_SmS+R Date: Wed, 26 Sep 2012 13:04:09 +0530 Subject: [PATCH 23/26] Get Books: Fix incorrect price retrieval from ebooks.com. Fixes #1055785 (Price of book search always incorrect) --- src/calibre/gui2/store/stores/ebooks_com_plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/gui2/store/stores/ebooks_com_plugin.py b/src/calibre/gui2/store/stores/ebooks_com_plugin.py index 7bf6704d9f..826b59d41d 100644 --- a/src/calibre/gui2/store/stores/ebooks_com_plugin.py +++ b/src/calibre/gui2/store/stores/ebooks_com_plugin.py @@ -97,7 +97,7 @@ class EbookscomStore(BasicStoreConfig, StorePlugin): with closing(br.open(url + id, timeout=timeout)) as nf: pdoc = html.fromstring(nf.read()) - price_l = pdoc.xpath('//span[@class="price"]/text()') + price_l = pdoc.xpath('//div[@class="book-info"]/div[@class="price"]/text()') if price_l: price = price_l[0] search_result.price = price.strip() From a83f930b92ec4b9d858cf7079ba7241b11b73c21 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 26 Sep 2012 16:36:23 +0530 Subject: [PATCH 24/26] Pubblico Giornale by iusvar --- recipes/pubblico_giornale.recipe | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 recipes/pubblico_giornale.recipe diff --git a/recipes/pubblico_giornale.recipe b/recipes/pubblico_giornale.recipe new file mode 100644 index 0000000000..b11b5fb8aa --- /dev/null +++ b/recipes/pubblico_giornale.recipe @@ -0,0 +1,21 @@ +#!/usr/bin/env python +__license__ = 'GPL v3' +__author__ = 'iusvar' +__description__ = 'Pubblico giornale' + +''' +http://pubblicogiornale.it/ +''' + +from calibre.web.feeds.news import BasicNewsRecipe + +class Pubblicogiornale(BasicNewsRecipe): + description = 'Italian newspaper directed by Luca Telese' + cover_url = 'http://pubblicogiornale.it/wp-content/uploads/logo_n.png?84cd58' + title = u'Pubblico giornale' + publisher = 'PUBBLICO EDIZIONI Srl' + category = 'News' + language = 'it' + __author__ = 'iusvar' + + feeds = [(u'Pubblico giornale', u'http://pubblicogiornale.it/feed/')] From 5399e417da2fde4ab26e33b2042ffff1d18d747e Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 26 Sep 2012 18:17:54 +0530 Subject: [PATCH 25/26] EPUB metadata: When setting metadata in an EPUB, explicitly specify the namespace of the role attribute on the tag --- src/calibre/ebooks/metadata/opf2.py | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/calibre/ebooks/metadata/opf2.py b/src/calibre/ebooks/metadata/opf2.py index 966e5caa30..cdd05ee430 100644 --- a/src/calibre/ebooks/metadata/opf2.py +++ b/src/calibre/ebooks/metadata/opf2.py @@ -792,19 +792,16 @@ class OPF(object): # {{{ remove = list(self.authors_path(self.metadata)) for elem in remove: elem.getparent().remove(elem) - elems = [] - for author in val: - attrib = {'{%s}role'%self.NAMESPACES['opf']: 'aut'} - elem = self.create_metadata_element('creator', attrib=attrib) + # Ensure new author element is at the top of the list + # for broken implementations that always use the first + # element with no attention to the role + for author in reversed(val): + elem = self.metadata.makeelement('{%s}creator'% + self.NAMESPACES['dc'], nsmap=self.NAMESPACES) + elem.tail = '\n' + self.metadata.insert(0, elem) + elem.set('{%s}role'%self.NAMESPACES['opf'], 'aut') self.set_text(elem, author.strip()) - # Ensure new author element is at the top of the list - # for broken implementations that always use the first - # element with no attention to the role - elems.append(elem) - for elem in reversed(elems): - parent = elem.getparent() - parent.remove(elem) - parent.insert(0, elem) return property(fget=fget, fset=fset) From 13e29a05a3143c08778792a0645f2448ae1bb659 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 26 Sep 2012 18:28:26 +0530 Subject: [PATCH 26/26] EPUB metadata: Fix book producer not being set when updating EPUB metadata --- src/calibre/ebooks/metadata/opf2.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/calibre/ebooks/metadata/opf2.py b/src/calibre/ebooks/metadata/opf2.py index cdd05ee430..3e5d95f1ce 100644 --- a/src/calibre/ebooks/metadata/opf2.py +++ b/src/calibre/ebooks/metadata/opf2.py @@ -1017,9 +1017,8 @@ class OPF(object): # {{{ def fset(self, val): matches = self.bkp_path(self.metadata) if not matches: - attrib = {'{%s}role'%self.NAMESPACES['opf']: 'bkp'} - matches = [self.create_metadata_element('contributor', - attrib=attrib)] + matches = [self.create_metadata_element('contributor')] + matches[0].set('{%s}role'%self.NAMESPACES['opf'], 'bkp') self.set_text(matches[0], unicode(val)) return property(fget=fget, fset=fset) @@ -1152,7 +1151,7 @@ class OPF(object): # {{{ def smart_update(self, mi, replace_metadata=False): for attr in ('title', 'authors', 'author_sort', 'title_sort', 'publisher', 'series', 'series_index', 'rating', - 'isbn', 'tags', 'category', 'comments', + 'isbn', 'tags', 'category', 'comments', 'book_producer', 'pubdate', 'user_categories', 'author_link_map'): val = getattr(mi, attr, None) if val is not None and val != [] and val != (None, None):