mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Merge from trunk
This commit is contained in:
commit
dbaac02e3c
@ -30,6 +30,7 @@ Environment variables
|
|||||||
* ``CALIBRE_OVERRIDE_DATABASE_PATH`` - allows you to specify the full path to metadata.db. Using this variable you can have metadata.db be in a location other than the library folder. Useful if your library folder is on a networked drive that does not support file locking.
|
* ``CALIBRE_OVERRIDE_DATABASE_PATH`` - allows you to specify the full path to metadata.db. Using this variable you can have metadata.db be in a location other than the library folder. Useful if your library folder is on a networked drive that does not support file locking.
|
||||||
* ``CALIBRE_DEVELOP_FROM`` - Used to run from a calibre development environment. See :ref:`develop`.
|
* ``CALIBRE_DEVELOP_FROM`` - Used to run from a calibre development environment. See :ref:`develop`.
|
||||||
* ``CALIBRE_OVERRIDE_LANG`` - Used to force the language used by the interface (ISO 639 language code)
|
* ``CALIBRE_OVERRIDE_LANG`` - Used to force the language used by the interface (ISO 639 language code)
|
||||||
|
* ``CALIBRE_NO_NATIVE_FILEDIALOGS`` - Causes calibre to not use native file dialogs for selecting files/directories.
|
||||||
* ``SYSFS_PATH`` - Use if sysfs is mounted somewhere other than /sys
|
* ``SYSFS_PATH`` - Use if sysfs is mounted somewhere other than /sys
|
||||||
* ``http_proxy`` - Used on linux to specify an HTTP proxy
|
* ``http_proxy`` - Used on linux to specify an HTTP proxy
|
||||||
|
|
||||||
|
@ -12,7 +12,11 @@ class AmericanProspect(BasicNewsRecipe):
|
|||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
remove_javascript = True
|
remove_javascript = True
|
||||||
|
|
||||||
keep_only_tags = [dict(name='div', attrs={'class':'pad_10L10R'})]
|
#keep_only_tags = [dict(name='div', attrs={'class':'pad_10L10R'})]
|
||||||
remove_tags = [dict(name='form'), dict(name='div', attrs={'class':['bkt_caption','sharebox noprint','badgebox']})]
|
#remove_tags = [dict(name='form'), dict(name='div', attrs={'class':['bkt_caption','sharebox noprint','badgebox']})]
|
||||||
|
use_embedded_content = False
|
||||||
|
|
||||||
|
no_stylesheets = True
|
||||||
|
auto_cleanup = True
|
||||||
feeds = [(u'Articles', u'feed://www.prospect.org/articles_rss.jsp')]
|
feeds = [(u'Articles', u'feed://www.prospect.org/articles_rss.jsp')]
|
||||||
|
|
||||||
|
@ -6,16 +6,15 @@ from calibre.web.feeds.recipes import BasicNewsRecipe
|
|||||||
class AdvancedUserRecipe1325006965(BasicNewsRecipe):
|
class AdvancedUserRecipe1325006965(BasicNewsRecipe):
|
||||||
|
|
||||||
title = u'The Sun UK'
|
title = u'The Sun UK'
|
||||||
description = 'A Recipe for The Sun tabloid UK'
|
description = 'Articles from The Sun tabloid UK'
|
||||||
__author__ = 'Dave Asbury'
|
__author__ = 'Dave Asbury'
|
||||||
# last updated 29/4/12
|
# last updated 15/7/12
|
||||||
language = 'en_GB'
|
language = 'en_GB'
|
||||||
oldest_article = 1
|
oldest_article = 1
|
||||||
max_articles_per_feed = 15
|
max_articles_per_feed = 15
|
||||||
remove_empty_feeds = True
|
remove_empty_feeds = True
|
||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
#auto_cleanup = True
|
|
||||||
#articles_are_obfuscated = True
|
|
||||||
|
|
||||||
masthead_url = 'http://www.thesun.co.uk/sol/img/global/Sun-logo.gif'
|
masthead_url = 'http://www.thesun.co.uk/sol/img/global/Sun-logo.gif'
|
||||||
encoding = 'UTF-8'
|
encoding = 'UTF-8'
|
||||||
@ -34,7 +33,7 @@ class AdvancedUserRecipe1325006965(BasicNewsRecipe):
|
|||||||
|
|
||||||
|
|
||||||
keep_only_tags = [
|
keep_only_tags = [
|
||||||
dict(name='h1'),dict(name='h2',attrs={'class' : 'medium centered'}),
|
dict(name='h1'),dict(name='h2',attrs={'class' : ['large','large centered','medium centered','medium']}),dict(name='h3'),
|
||||||
dict(name='div',attrs={'class' : 'text-center'}),
|
dict(name='div',attrs={'class' : 'text-center'}),
|
||||||
dict(name='div',attrs={'id' : 'bodyText'})
|
dict(name='div',attrs={'id' : 'bodyText'})
|
||||||
# dict(name='p')
|
# dict(name='p')
|
||||||
@ -72,22 +71,18 @@ class AdvancedUserRecipe1325006965(BasicNewsRecipe):
|
|||||||
cov2 = str(cov)
|
cov2 = str(cov)
|
||||||
cov2=cov2[27:-18]
|
cov2=cov2[27:-18]
|
||||||
#cov2 now is pic url, now go back to original function
|
#cov2 now is pic url, now go back to original function
|
||||||
|
|
||||||
br = browser()
|
br = browser()
|
||||||
br.set_handle_redirect(False)
|
br.set_handle_redirect(False)
|
||||||
try:
|
try:
|
||||||
br.open_novisit(cov2)
|
br.open_novisit(cov2)
|
||||||
cover_url = cov2
|
cover_url = cov2
|
||||||
except:
|
except:
|
||||||
cover_url = random.choice((
|
cover_url = random.choice([
|
||||||
'http://img.thesun.co.uk/multimedia/archive/00905/errorpage6_677961a_905507a.jpg'
|
'http://img.thesun.co.uk/multimedia/archive/00905/errorpage6_677961a_905507a.jpg'
|
||||||
,'http://img.thesun.co.uk/multimedia/archive/00905/errorpage7_677962a_905505a.jpg'
|
,'http://img.thesun.co.uk/multimedia/archive/00905/errorpage7_677962a_905505a.jpg'
|
||||||
,'http://img.thesun.co.uk/multimedia/archive/00905/errorpage5_677960a_905512a.jpg'
|
,'http://img.thesun.co.uk/multimedia/archive/00905/errorpage5_677960a_905512a.jpg'
|
||||||
,'http://img.thesun.co.uk/multimedia/archive/00905/errorpage2_677957a_905502a.jpg'
|
,'http://img.thesun.co.uk/multimedia/archive/00905/errorpage2_677957a_905502a.jpg'
|
||||||
,'http://img.thesun.co.uk/multimedia/archive/00905/errorpage3_677958a_905503a.jpg'
|
,'http://img.thesun.co.uk/multimedia/archive/00905/errorpage3_677958a_905503a.jpg'
|
||||||
))
|
])
|
||||||
|
|
||||||
return cover_url
|
return cover_url
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -98,15 +98,6 @@ authors_split_regex = r'(?i),?\s+(and|with)\s+'
|
|||||||
# categories_use_field_for_author_name = 'author_sort'
|
# categories_use_field_for_author_name = 'author_sort'
|
||||||
categories_use_field_for_author_name = 'author'
|
categories_use_field_for_author_name = 'author'
|
||||||
|
|
||||||
#: Completion sort order: choose when to change from lexicographic to ASCII-like
|
|
||||||
# Calibre normally uses locale-dependent lexicographic ordering when showing
|
|
||||||
# completion values. This means that the sort order is correct for the user's
|
|
||||||
# language. However, this can be slow. Performance is improved by switching to
|
|
||||||
# ascii ordering. This tweak controls when that switch happens. Set it to zero
|
|
||||||
# to always use ascii ordering. Set it to something larger than zero to switch
|
|
||||||
# to ascii ordering for performance reasons.
|
|
||||||
completion_change_to_ascii_sorting = 2500
|
|
||||||
|
|
||||||
#: Control partitioning of Tag Browser
|
#: Control partitioning of Tag Browser
|
||||||
# When partitioning the tags browser, the format of the subcategory label is
|
# When partitioning the tags browser, the format of the subcategory label is
|
||||||
# controlled by a template: categories_collapsed_name_template if sorting by
|
# controlled by a template: categories_collapsed_name_template if sorting by
|
||||||
@ -525,3 +516,11 @@ default_tweak_format = None
|
|||||||
# enable_multicharacters_in_tag_browser = False
|
# enable_multicharacters_in_tag_browser = False
|
||||||
enable_multicharacters_in_tag_browser = True
|
enable_multicharacters_in_tag_browser = True
|
||||||
|
|
||||||
|
#: Do not preselect a completion when editing authors/tags/series/etc.
|
||||||
|
# This means that you can make changes and press Enter and your changes will
|
||||||
|
# not be overwritten by a matching completion. However, if you wish to use the
|
||||||
|
# completions you will now have to press Tab to select one before pressing
|
||||||
|
# Enter. Which technique you prefer will depend on the state of metadata in
|
||||||
|
# your library and your personal editing style.
|
||||||
|
preselect_first_completion = False
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@ MAGICK_PREFIX = '/usr'
|
|||||||
binary_includes = [
|
binary_includes = [
|
||||||
'/usr/bin/pdftohtml',
|
'/usr/bin/pdftohtml',
|
||||||
'/usr/bin/pdfinfo',
|
'/usr/bin/pdfinfo',
|
||||||
|
'/usr/lib/libglib-2.0.so.0',
|
||||||
'/usr/bin/pdftoppm',
|
'/usr/bin/pdftoppm',
|
||||||
'/usr/lib/libwmflite-0.2.so.7',
|
'/usr/lib/libwmflite-0.2.so.7',
|
||||||
'/usr/lib/liblcms.so.1',
|
'/usr/lib/liblcms.so.1',
|
||||||
|
@ -1483,6 +1483,16 @@ class StoreManyBooksStore(StoreBase):
|
|||||||
headquarters = 'US'
|
headquarters = 'US'
|
||||||
formats = ['EPUB', 'FB2', 'JAR', 'LIT', 'LRF', 'MOBI', 'PDB', 'PDF', 'RB', 'RTF', 'TCR', 'TXT', 'ZIP']
|
formats = ['EPUB', 'FB2', 'JAR', 'LIT', 'LRF', 'MOBI', 'PDB', 'PDF', 'RB', 'RTF', 'TCR', 'TXT', 'ZIP']
|
||||||
|
|
||||||
|
class StoreMillsBoonUKStore(StoreBase):
|
||||||
|
name = 'Mills and Boon UK'
|
||||||
|
author = 'Charles Haley'
|
||||||
|
description = u'"Bring Romance to Life" "[A] hallmark for romantic fiction, recognised around the world."'
|
||||||
|
actual_plugin = 'calibre.gui2.store.stores.mills_boon_uk_plugin:MillsBoonUKStore'
|
||||||
|
|
||||||
|
headquarters = 'UK'
|
||||||
|
formats = ['EPUB']
|
||||||
|
affiliate = True
|
||||||
|
|
||||||
class StoreMobileReadStore(StoreBase):
|
class StoreMobileReadStore(StoreBase):
|
||||||
name = 'MobileRead'
|
name = 'MobileRead'
|
||||||
description = u'Ebooks handcrafted with the utmost care.'
|
description = u'Ebooks handcrafted with the utmost care.'
|
||||||
@ -1646,6 +1656,7 @@ plugins += [
|
|||||||
StoreLibreDEStore,
|
StoreLibreDEStore,
|
||||||
StoreLitResStore,
|
StoreLitResStore,
|
||||||
StoreManyBooksStore,
|
StoreManyBooksStore,
|
||||||
|
StoreMillsBoonUKStore,
|
||||||
StoreMobileReadStore,
|
StoreMobileReadStore,
|
||||||
StoreNextoStore,
|
StoreNextoStore,
|
||||||
StoreOpenBooksStore,
|
StoreOpenBooksStore,
|
||||||
|
@ -194,7 +194,7 @@ class ANDROID(USBMS):
|
|||||||
'GENERIC-', 'ZTE', 'MID', 'QUALCOMM', 'PANDIGIT', 'HYSTON',
|
'GENERIC-', 'ZTE', 'MID', 'QUALCOMM', 'PANDIGIT', 'HYSTON',
|
||||||
'VIZIO', 'GOOGLE', 'FREESCAL', 'KOBO_INC', 'LENOVO', 'ROCKCHIP',
|
'VIZIO', 'GOOGLE', 'FREESCAL', 'KOBO_INC', 'LENOVO', 'ROCKCHIP',
|
||||||
'POCKET', 'ONDA_MID', 'ZENITHIN', 'INGENIC', 'PMID701C', 'PD',
|
'POCKET', 'ONDA_MID', 'ZENITHIN', 'INGENIC', 'PMID701C', 'PD',
|
||||||
'PMP5097C']
|
'PMP5097C', 'MASS', 'NOVO7']
|
||||||
WINDOWS_MAIN_MEM = ['ANDROID_PHONE', 'A855', 'A853', 'INC.NEXUS_ONE',
|
WINDOWS_MAIN_MEM = ['ANDROID_PHONE', 'A855', 'A853', 'INC.NEXUS_ONE',
|
||||||
'__UMS_COMPOSITE', '_MB200', 'MASS_STORAGE', '_-_CARD', 'SGH-I897',
|
'__UMS_COMPOSITE', '_MB200', 'MASS_STORAGE', '_-_CARD', 'SGH-I897',
|
||||||
'GT-I9000', 'FILE-STOR_GADGET', 'SGH-T959_CARD', 'SGH-T959', 'SAMSUNG_ANDROID',
|
'GT-I9000', 'FILE-STOR_GADGET', 'SGH-T959_CARD', 'SGH-T959', 'SAMSUNG_ANDROID',
|
||||||
@ -211,7 +211,8 @@ class ANDROID(USBMS):
|
|||||||
'XT910', 'BOOK_A10', 'USB_2.0_DRIVER', 'I9100T', 'P999DW',
|
'XT910', 'BOOK_A10', 'USB_2.0_DRIVER', 'I9100T', 'P999DW',
|
||||||
'KTABLET_PC', 'INGENIC', 'GT-I9001_CARD', 'USB_2.0_DRIVER',
|
'KTABLET_PC', 'INGENIC', 'GT-I9001_CARD', 'USB_2.0_DRIVER',
|
||||||
'GT-S5830L_CARD', 'UNIVERSE', 'XT875', 'PRO', '.KOBO_VOX',
|
'GT-S5830L_CARD', 'UNIVERSE', 'XT875', 'PRO', '.KOBO_VOX',
|
||||||
'THINKPAD_TABLET', 'SGH-T989', 'YP-G70']
|
'THINKPAD_TABLET', 'SGH-T989', 'YP-G70', 'STORAGE_DEVICE',
|
||||||
|
'ADVANCED']
|
||||||
WINDOWS_CARD_A_MEM = ['ANDROID_PHONE', 'GT-I9000_CARD', 'SGH-I897',
|
WINDOWS_CARD_A_MEM = ['ANDROID_PHONE', 'GT-I9000_CARD', 'SGH-I897',
|
||||||
'FILE-STOR_GADGET', 'SGH-T959_CARD', 'SGH-T959', 'SAMSUNG_ANDROID', 'GT-P1000_CARD',
|
'FILE-STOR_GADGET', 'SGH-T959_CARD', 'SGH-T959', 'SAMSUNG_ANDROID', 'GT-P1000_CARD',
|
||||||
'A70S', 'A101IT', '7', 'INCREDIBLE', 'A7EB', 'SGH-T849_CARD',
|
'A70S', 'A101IT', '7', 'INCREDIBLE', 'A7EB', 'SGH-T849_CARD',
|
||||||
|
@ -163,6 +163,7 @@ class EXTHRecord(object):
|
|||||||
501 : 'cdetype', # 4 chars (PDOC or EBOK)
|
501 : 'cdetype', # 4 chars (PDOC or EBOK)
|
||||||
502 : 'lastupdatetime',
|
502 : 'lastupdatetime',
|
||||||
503 : 'updatedtitle',
|
503 : 'updatedtitle',
|
||||||
|
524 : 'language',
|
||||||
}.get(self.type, repr(self.type))
|
}.get(self.type, repr(self.type))
|
||||||
|
|
||||||
if (self.name in {'coveroffset', 'thumboffset', 'hasfakecover',
|
if (self.name in {'coveroffset', 'thumboffset', 'hasfakecover',
|
||||||
|
@ -13,6 +13,7 @@ from calibre.utils.date import parse_date
|
|||||||
from calibre.ebooks.mobi import MobiError
|
from calibre.ebooks.mobi import MobiError
|
||||||
from calibre.ebooks.metadata import MetaInformation, check_isbn
|
from calibre.ebooks.metadata import MetaInformation, check_isbn
|
||||||
from calibre.ebooks.mobi.langcodes import main_language, sub_language, mobi2iana
|
from calibre.ebooks.mobi.langcodes import main_language, sub_language, mobi2iana
|
||||||
|
from calibre.utils.localization import canonicalize_lang
|
||||||
|
|
||||||
NULL_INDEX = 0xffffffff
|
NULL_INDEX = 0xffffffff
|
||||||
|
|
||||||
@ -68,6 +69,14 @@ class EXTHHeader(object): # {{{
|
|||||||
title = content.decode(codec)
|
title = content.decode(codec)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
elif idx == 524: # Lang code
|
||||||
|
try:
|
||||||
|
lang = content.decode(codec)
|
||||||
|
lang = canonicalize_lang(lang)
|
||||||
|
if lang:
|
||||||
|
self.mi.language = lang
|
||||||
|
except:
|
||||||
|
pass
|
||||||
#else:
|
#else:
|
||||||
# print 'unknown record', idx, repr(content)
|
# print 'unknown record', idx, repr(content)
|
||||||
if title:
|
if title:
|
||||||
@ -201,6 +210,7 @@ class BookHeader(object):
|
|||||||
self.exth = EXTHHeader(raw[16 + self.length:], self.codec,
|
self.exth = EXTHHeader(raw[16 + self.length:], self.codec,
|
||||||
self.title)
|
self.title)
|
||||||
self.exth.mi.uid = self.unique_id
|
self.exth.mi.uid = self.unique_id
|
||||||
|
if self.exth.mi.is_null('language'):
|
||||||
try:
|
try:
|
||||||
self.exth.mi.language = mobi2iana(langid, sublangid)
|
self.exth.mi.language = mobi2iana(langid, sublangid)
|
||||||
except:
|
except:
|
||||||
|
@ -297,10 +297,13 @@ class MobiWriter(object):
|
|||||||
|
|
||||||
# 0x70 - 0x73 : EXTH flags
|
# 0x70 - 0x73 : EXTH flags
|
||||||
# Bit 6 (0b1000000) being set indicates the presence of an EXTH header
|
# Bit 6 (0b1000000) being set indicates the presence of an EXTH header
|
||||||
|
# Bit 12 being set indicates the presence of embedded fonts
|
||||||
# The purpose of the other bits is unknown
|
# The purpose of the other bits is unknown
|
||||||
exth_flags = 0b1010000
|
exth_flags = 0b1010000
|
||||||
if self.is_periodical:
|
if self.is_periodical:
|
||||||
exth_flags |= 0b1000
|
exth_flags |= 0b1000
|
||||||
|
if self.resources.has_fonts:
|
||||||
|
exth_flags |= 0b1000000000000
|
||||||
record0.write(pack(b'>I', exth_flags))
|
record0.write(pack(b'>I', exth_flags))
|
||||||
|
|
||||||
# 0x74 - 0x93 : Unknown
|
# 0x74 - 0x93 : Unknown
|
||||||
@ -406,7 +409,10 @@ class MobiWriter(object):
|
|||||||
# Now change the header fields that need to be different in the MOBI 6
|
# Now change the header fields that need to be different in the MOBI 6
|
||||||
# header
|
# header
|
||||||
header_fields['first_resource_record'] = first_image_record
|
header_fields['first_resource_record'] = first_image_record
|
||||||
header_fields['exth_flags'] = 0b100001010000 # Kinglegen uses this
|
ef = 0b100001010000 # Kinglegen uses this
|
||||||
|
if self.resources.has_fonts:
|
||||||
|
ef |= 0b1000000000000
|
||||||
|
header_fields['exth_flags'] = ef
|
||||||
header_fields['fdst_record'] = pack(b'>HH', 1, last_content_record)
|
header_fields['fdst_record'] = pack(b'>HH', 1, last_content_record)
|
||||||
header_fields['fdst_count'] = 1 # Why not 0? Kindlegen uses 1
|
header_fields['fdst_count'] = 1 # Why not 0? Kindlegen uses 1
|
||||||
header_fields['flis_record'] = flis_number
|
header_fields['flis_record'] = flis_number
|
||||||
|
@ -32,6 +32,7 @@ class Resources(object):
|
|||||||
self.used_image_indices = set()
|
self.used_image_indices = set()
|
||||||
self.image_indices = set()
|
self.image_indices = set()
|
||||||
self.cover_offset = self.thumbnail_offset = None
|
self.cover_offset = self.thumbnail_offset = None
|
||||||
|
self.has_fonts = False
|
||||||
|
|
||||||
self.add_resources(add_fonts)
|
self.add_resources(add_fonts)
|
||||||
|
|
||||||
@ -109,6 +110,7 @@ class Resources(object):
|
|||||||
'ttf', 'otf'} and isinstance(item.data, bytes):
|
'ttf', 'otf'} and isinstance(item.data, bytes):
|
||||||
self.records.append(write_font_record(item.data))
|
self.records.append(write_font_record(item.data))
|
||||||
self.item_map[item.href] = len(self.records)
|
self.item_map[item.href] = len(self.records)
|
||||||
|
self.has_fonts = True
|
||||||
|
|
||||||
def add_extra_images(self):
|
def add_extra_images(self):
|
||||||
'''
|
'''
|
||||||
|
@ -12,6 +12,7 @@ from struct import pack
|
|||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
from calibre.ebooks.mobi.utils import utf8_text
|
from calibre.ebooks.mobi.utils import utf8_text
|
||||||
|
from calibre.utils.localization import lang_as_iso639_1
|
||||||
|
|
||||||
EXTH_CODES = {
|
EXTH_CODES = {
|
||||||
'creator': 100,
|
'creator': 100,
|
||||||
@ -35,6 +36,7 @@ EXTH_CODES = {
|
|||||||
'hasfakecover': 203,
|
'hasfakecover': 203,
|
||||||
'lastupdatetime': 502,
|
'lastupdatetime': 502,
|
||||||
'title': 503,
|
'title': 503,
|
||||||
|
'language': 524,
|
||||||
}
|
}
|
||||||
|
|
||||||
COLLAPSE_RE = re.compile(r'[ \t\r\n\v]+')
|
COLLAPSE_RE = re.compile(r'[ \t\r\n\v]+')
|
||||||
@ -57,6 +59,16 @@ def build_exth(metadata, prefer_author_sort=False, is_periodical=False,
|
|||||||
else:
|
else:
|
||||||
creators = [unicode(c) for c in items]
|
creators = [unicode(c) for c in items]
|
||||||
items = creators
|
items = creators
|
||||||
|
elif term == 'rights':
|
||||||
|
try:
|
||||||
|
rights = utf8_text(unicode(metadata.rights[0]))
|
||||||
|
except:
|
||||||
|
rights = b'Unknown'
|
||||||
|
exth.write(pack(b'>II', EXTH_CODES['rights'], len(rights) + 8))
|
||||||
|
exth.write(rights)
|
||||||
|
nrecs += 1
|
||||||
|
continue
|
||||||
|
|
||||||
for item in items:
|
for item in items:
|
||||||
data = unicode(item)
|
data = unicode(item)
|
||||||
if term != 'description':
|
if term != 'description':
|
||||||
@ -68,18 +80,14 @@ def build_exth(metadata, prefer_author_sort=False, is_periodical=False,
|
|||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
continue
|
continue
|
||||||
|
if term == 'language':
|
||||||
|
d2 = lang_as_iso639_1(data)
|
||||||
|
if d2:
|
||||||
|
data = d2
|
||||||
data = utf8_text(data)
|
data = utf8_text(data)
|
||||||
exth.write(pack(b'>II', code, len(data) + 8))
|
exth.write(pack(b'>II', code, len(data) + 8))
|
||||||
exth.write(data)
|
exth.write(data)
|
||||||
nrecs += 1
|
nrecs += 1
|
||||||
if term == 'rights' :
|
|
||||||
try:
|
|
||||||
rights = utf8_text(unicode(metadata.rights[0]))
|
|
||||||
except:
|
|
||||||
rights = b'Unknown'
|
|
||||||
exth.write(pack(b'>II', EXTH_CODES['rights'], len(rights) + 8))
|
|
||||||
exth.write(rights)
|
|
||||||
nrecs += 1
|
|
||||||
|
|
||||||
# Write UUID as ASIN
|
# Write UUID as ASIN
|
||||||
uuid = None
|
uuid = None
|
||||||
@ -132,7 +140,7 @@ def build_exth(metadata, prefer_author_sort=False, is_periodical=False,
|
|||||||
nrecs += 1
|
nrecs += 1
|
||||||
|
|
||||||
if be_kindlegen2:
|
if be_kindlegen2:
|
||||||
vals = {204:201, 205:2, 206:2, 207:35621}
|
vals = {204:201, 205:2, 206:5, 207:0}
|
||||||
elif is_periodical:
|
elif is_periodical:
|
||||||
# Pretend to be amazon's super secret periodical generator
|
# Pretend to be amazon's super secret periodical generator
|
||||||
vals = {204:201, 205:2, 206:0, 207:101}
|
vals = {204:201, 205:2, 206:0, 207:101}
|
||||||
|
@ -277,6 +277,8 @@ class KF8Book(object):
|
|||||||
self.exth_flags = 0b1010000
|
self.exth_flags = 0b1010000
|
||||||
if writer.opts.mobi_periodical:
|
if writer.opts.mobi_periodical:
|
||||||
self.exth_flags |= 0b1000
|
self.exth_flags |= 0b1000
|
||||||
|
if resources.has_fonts:
|
||||||
|
self.exth_flags |= 0b1000000000000
|
||||||
|
|
||||||
self.opts = writer.opts
|
self.opts = writer.opts
|
||||||
self.start_offset = writer.start_offset
|
self.start_offset = writer.start_offset
|
||||||
|
@ -223,7 +223,8 @@ class PDFWriter(QObject): # {{{
|
|||||||
if self.cover_data is None:
|
if self.cover_data is None:
|
||||||
return
|
return
|
||||||
item_path = os.path.join(self.tmp_path, 'cover.pdf')
|
item_path = os.path.join(self.tmp_path, 'cover.pdf')
|
||||||
printer = get_pdf_printer(self.opts, output_file_name=item_path)
|
printer = get_pdf_printer(self.opts, output_file_name=item_path,
|
||||||
|
for_comic=True)
|
||||||
self.combine_queue.insert(0, item_path)
|
self.combine_queue.insert(0, item_path)
|
||||||
p = QPixmap()
|
p = QPixmap()
|
||||||
p.loadFromData(self.cover_data)
|
p.loadFromData(self.cover_data)
|
||||||
|
@ -573,17 +573,24 @@ class FileDialog(QObject):
|
|||||||
if not isinstance(initial_dir, basestring):
|
if not isinstance(initial_dir, basestring):
|
||||||
initial_dir = os.path.expanduser(default_dir)
|
initial_dir = os.path.expanduser(default_dir)
|
||||||
self.selected_files = []
|
self.selected_files = []
|
||||||
|
use_native_dialog = not os.environ.has_key('CALIBRE_NO_NATIVE_FILEDIALOGS')
|
||||||
with SanitizeLibraryPath():
|
with SanitizeLibraryPath():
|
||||||
|
opts = QFileDialog.Option()
|
||||||
|
if not use_native_dialog:
|
||||||
|
opts |= QFileDialog.DontUseNativeDialog
|
||||||
if mode == QFileDialog.AnyFile:
|
if mode == QFileDialog.AnyFile:
|
||||||
f = unicode(QFileDialog.getSaveFileName(parent, title, initial_dir, ftext, ""))
|
f = unicode(QFileDialog.getSaveFileName(parent, title,
|
||||||
|
initial_dir, ftext, "", opts))
|
||||||
if f:
|
if f:
|
||||||
self.selected_files.append(f)
|
self.selected_files.append(f)
|
||||||
elif mode == QFileDialog.ExistingFile:
|
elif mode == QFileDialog.ExistingFile:
|
||||||
f = unicode(QFileDialog.getOpenFileName(parent, title, initial_dir, ftext, ""))
|
f = unicode(QFileDialog.getOpenFileName(parent, title,
|
||||||
|
initial_dir, ftext, "", opts))
|
||||||
if f and os.path.exists(f):
|
if f and os.path.exists(f):
|
||||||
self.selected_files.append(f)
|
self.selected_files.append(f)
|
||||||
elif mode == QFileDialog.ExistingFiles:
|
elif mode == QFileDialog.ExistingFiles:
|
||||||
fs = QFileDialog.getOpenFileNames(parent, title, initial_dir, ftext, "")
|
fs = QFileDialog.getOpenFileNames(parent, title, initial_dir,
|
||||||
|
ftext, "", opts)
|
||||||
for f in fs:
|
for f in fs:
|
||||||
f = unicode(f)
|
f = unicode(f)
|
||||||
if not f: continue
|
if not f: continue
|
||||||
@ -594,7 +601,8 @@ class FileDialog(QObject):
|
|||||||
if f and os.path.exists(f):
|
if f and os.path.exists(f):
|
||||||
self.selected_files.append(f)
|
self.selected_files.append(f)
|
||||||
else:
|
else:
|
||||||
opts = QFileDialog.ShowDirsOnly if mode == QFileDialog.Directory else QFileDialog.Option()
|
if mode == QFileDialog.Directory:
|
||||||
|
opts |= QFileDialog.ShowDirsOnly
|
||||||
f = unicode(QFileDialog.getExistingDirectory(parent, title, initial_dir, opts))
|
f = unicode(QFileDialog.getExistingDirectory(parent, title, initial_dir, opts))
|
||||||
if os.path.exists(f):
|
if os.path.exists(f):
|
||||||
self.selected_files.append(f)
|
self.selected_files.append(f)
|
||||||
|
@ -16,7 +16,6 @@ from PyQt4.Qt import (QLineEdit, QAbstractListModel, Qt,
|
|||||||
from calibre.utils.icu import sort_key
|
from calibre.utils.icu import sort_key
|
||||||
from calibre.gui2 import NONE
|
from calibre.gui2 import NONE
|
||||||
from calibre.gui2.widgets import EnComboBox, LineEditECM
|
from calibre.gui2.widgets import EnComboBox, LineEditECM
|
||||||
from calibre.utils.config_base import tweaks
|
|
||||||
|
|
||||||
class CompleteModel(QAbstractListModel):
|
class CompleteModel(QAbstractListModel):
|
||||||
|
|
||||||
@ -27,7 +26,7 @@ class CompleteModel(QAbstractListModel):
|
|||||||
|
|
||||||
def set_items(self, items):
|
def set_items(self, items):
|
||||||
items = [unicode(x.strip()) for x in items]
|
items = [unicode(x.strip()) for x in items]
|
||||||
if len(items) < tweaks['completion_change_to_ascii_sorting']:
|
if len(items) < 2500:
|
||||||
self.items = sorted(items, key=sort_key)
|
self.items = sorted(items, key=sort_key)
|
||||||
self.sorting = QCompleter.UnsortedModel
|
self.sorting = QCompleter.UnsortedModel
|
||||||
else:
|
else:
|
||||||
|
@ -16,6 +16,7 @@ from PyQt4.Qt import (QLineEdit, QAbstractListModel, Qt, pyqtSignal, QObject,
|
|||||||
from calibre.utils.icu import sort_key, primary_startswith
|
from calibre.utils.icu import sort_key, primary_startswith
|
||||||
from calibre.gui2 import NONE
|
from calibre.gui2 import NONE
|
||||||
from calibre.gui2.widgets import EnComboBox, LineEditECM
|
from calibre.gui2.widgets import EnComboBox, LineEditECM
|
||||||
|
from calibre.utils.config import tweaks
|
||||||
|
|
||||||
class CompleteModel(QAbstractListModel): # {{{
|
class CompleteModel(QAbstractListModel): # {{{
|
||||||
|
|
||||||
@ -157,8 +158,8 @@ class Completer(QListView): # {{{
|
|||||||
|
|
||||||
p.setGeometry(pos.x(), pos.y(), w, h)
|
p.setGeometry(pos.x(), pos.y(), w, h)
|
||||||
|
|
||||||
if (select_first and not self.currentIndex().isValid() and
|
if (tweaks['preselect_first_completion'] and select_first and not
|
||||||
self.model().rowCount() > 0):
|
self.currentIndex().isValid() and self.model().rowCount() > 0):
|
||||||
self.setCurrentIndex(self.model().index(0))
|
self.setCurrentIndex(self.model().index(0))
|
||||||
|
|
||||||
if not p.isVisible():
|
if not p.isVisible():
|
||||||
@ -189,12 +190,11 @@ class Completer(QListView): # {{{
|
|||||||
e.accept()
|
e.accept()
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
if key in (Qt.Key_End, Qt.Key_Home, Qt.Key_Up, Qt.Key_Down,
|
if key in (Qt.Key_PageUp, Qt.Key_PageDown):
|
||||||
Qt.Key_PageUp, Qt.Key_PageDown):
|
|
||||||
# Let the list view handle these keys
|
# Let the list view handle these keys
|
||||||
return False
|
return False
|
||||||
if key in (Qt.Key_Tab, Qt.Key_Backtab):
|
if key in (Qt.Key_Tab, Qt.Key_Backtab, Qt.Key_Up, Qt.Key_Down):
|
||||||
self.next_match(previous=key == Qt.Key_Backtab)
|
self.next_match(previous=key in (Qt.Key_Backtab, Qt.Key_Up))
|
||||||
e.accept()
|
e.accept()
|
||||||
return True
|
return True
|
||||||
# Send to widget
|
# Send to widget
|
||||||
@ -284,6 +284,8 @@ class LineEdit(QLineEdit, LineEditECM):
|
|||||||
if self.no_popup: return
|
if self.no_popup: return
|
||||||
self.update_completions()
|
self.update_completions()
|
||||||
select_first = len(self.mcompleter.model().current_prefix) > 0
|
select_first = len(self.mcompleter.model().current_prefix) > 0
|
||||||
|
if not select_first:
|
||||||
|
self.mcompleter.setCurrentIndex(QModelIndex())
|
||||||
self.complete(select_first=select_first)
|
self.complete(select_first=select_first)
|
||||||
|
|
||||||
def update_completions(self):
|
def update_completions(self):
|
||||||
|
78
src/calibre/gui2/store/stores/mills_boon_uk_plugin.py
Normal file
78
src/calibre/gui2/store/stores/mills_boon_uk_plugin.py
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||||
|
|
||||||
|
__license__ = 'GPL 3'
|
||||||
|
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
||||||
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
import urllib2
|
||||||
|
from contextlib import closing
|
||||||
|
|
||||||
|
from lxml import html
|
||||||
|
|
||||||
|
from PyQt4.Qt import QUrl
|
||||||
|
|
||||||
|
from calibre import browser, url_slash_cleaner
|
||||||
|
from calibre.gui2 import open_url
|
||||||
|
from calibre.gui2.store import StorePlugin
|
||||||
|
from calibre.gui2.store.basic_config import BasicStoreConfig
|
||||||
|
from calibre.gui2.store.search_result import SearchResult
|
||||||
|
from calibre.gui2.store.web_store_dialog import WebStoreDialog
|
||||||
|
|
||||||
|
class MillsBoonUKStore(BasicStoreConfig, StorePlugin):
|
||||||
|
|
||||||
|
def open(self, parent=None, detail_item=None, external=False):
|
||||||
|
url = 'http://www.awin1.com/awclick.php?mid=1150&id=120917'
|
||||||
|
detail_url = 'http://www.awin1.com/cread.php?awinmid=1150&awinaffid=120917&clickref=&p='
|
||||||
|
|
||||||
|
if external or self.config.get('open_external', False):
|
||||||
|
if detail_item:
|
||||||
|
url = detail_url + detail_item
|
||||||
|
open_url(QUrl(url_slash_cleaner(url)))
|
||||||
|
else:
|
||||||
|
detail_url = None
|
||||||
|
if detail_item:
|
||||||
|
detail_url = url + detail_item
|
||||||
|
d = WebStoreDialog(self.gui, url, parent, detail_url)
|
||||||
|
d.setWindowTitle(self.name)
|
||||||
|
d.set_tags(self.config.get('tags', ''))
|
||||||
|
d.exec_()
|
||||||
|
|
||||||
|
def search(self, query, max_results=10, timeout=60):
|
||||||
|
base_url = 'http://millsandboon.co.uk'
|
||||||
|
url = base_url + '/pages/searchres.htm?search=true&booktypesearch=ebook&first=yes&inputsearch=' + urllib2.quote(query)
|
||||||
|
br = browser()
|
||||||
|
|
||||||
|
counter = max_results
|
||||||
|
with closing(br.open(url, timeout=timeout)) as f:
|
||||||
|
doc = html.fromstring(f.read())
|
||||||
|
for data in doc.xpath('//div[@class="catProdDiv"]'):
|
||||||
|
if counter <= 0:
|
||||||
|
break
|
||||||
|
id_ = ''.join(data.xpath('.//div[@class="catProdImage"]/div/a/@href')).strip()
|
||||||
|
id_ = base_url + id_[2:]
|
||||||
|
if not id_:
|
||||||
|
continue
|
||||||
|
|
||||||
|
cover_url = ''.join(data.xpath('.//div[@class="catProdImage"]/div/a/img/@src'))
|
||||||
|
cover_url = base_url + cover_url[2:]
|
||||||
|
title = ''.join(data.xpath('.//div[@class="catProdImage"]/div/a/img/@alt')).strip()
|
||||||
|
title = title[23:]
|
||||||
|
author = ''.join(data.xpath('.//div[@class="catProdDetails"]/div[@class="catProdDetails-top"]/p[1]/a/text()'))
|
||||||
|
price = ''.join(data.xpath('.//span[@class="priceBold"]/text()'))
|
||||||
|
format_ = ''.join(data.xpath('.//p[@class="doc-meta-format"]/span[last()]/text()'))
|
||||||
|
drm = SearchResult.DRM_LOCKED
|
||||||
|
|
||||||
|
counter -= 1
|
||||||
|
|
||||||
|
s = SearchResult()
|
||||||
|
s.cover_url = cover_url
|
||||||
|
s.title = title.strip()
|
||||||
|
s.author = author.strip()
|
||||||
|
s.price = price
|
||||||
|
s.detail_item = id_
|
||||||
|
s.drm = drm
|
||||||
|
s.formats = format_
|
||||||
|
|
||||||
|
yield s
|
@ -1096,8 +1096,11 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
|||||||
identical_book_ids = set([])
|
identical_book_ids = set([])
|
||||||
if mi.authors:
|
if mi.authors:
|
||||||
try:
|
try:
|
||||||
|
quathors = mi.authors[:10] # Too many authors causes parsing of
|
||||||
|
# the search expression to fail
|
||||||
query = u' and '.join([u'author:"=%s"'%(a.replace('"', '')) for a in
|
query = u' and '.join([u'author:"=%s"'%(a.replace('"', '')) for a in
|
||||||
mi.authors])
|
quathors])
|
||||||
|
qauthors = mi.authors[10:]
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return identical_book_ids
|
return identical_book_ids
|
||||||
try:
|
try:
|
||||||
@ -1105,6 +1108,18 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
|||||||
except:
|
except:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
return identical_book_ids
|
return identical_book_ids
|
||||||
|
if qauthors and book_ids:
|
||||||
|
matches = set()
|
||||||
|
qauthors = {lower(x) for x in qauthors}
|
||||||
|
for book_id in book_ids:
|
||||||
|
aut = self.authors(book_id, index_is_id=True)
|
||||||
|
if aut:
|
||||||
|
aut = {lower(x.replace('|', ',')) for x in
|
||||||
|
aut.split(',')}
|
||||||
|
if aut.issuperset(qauthors):
|
||||||
|
matches.add(book_id)
|
||||||
|
book_ids = matches
|
||||||
|
|
||||||
for book_id in book_ids:
|
for book_id in book_ids:
|
||||||
fbook_title = self.title(book_id, index_is_id=True)
|
fbook_title = self.title(book_id, index_is_id=True)
|
||||||
fbook_title = fuzzy_title(fbook_title)
|
fbook_title = fuzzy_title(fbook_title)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user