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
a0cdf29379
@ -8,23 +8,36 @@ __copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net> edited by Huan T'
|
|||||||
from calibre.web.feeds.news import BasicNewsRecipe
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
class Slashdot(BasicNewsRecipe):
|
class Slashdot(BasicNewsRecipe):
|
||||||
title = u'Slashdot.org'
|
title = u'Slashdot.org'
|
||||||
description = '''Tech news. WARNING: This recipe downloads a lot
|
description = '''Tech news. WARNING: This recipe downloads a lot
|
||||||
of content and may result in your IP being banned from slashdot.org'''
|
of content and may result in your IP being banned from slashdot.org'''
|
||||||
oldest_article = 7
|
oldest_article = 7
|
||||||
simultaneous_downloads = 1
|
simultaneous_downloads = 1
|
||||||
delay = 3
|
delay = 3
|
||||||
max_articles_per_feed = 100
|
max_articles_per_feed = 100
|
||||||
language = 'en'
|
language = 'en'
|
||||||
|
|
||||||
__author__ = 'floweros edited by Huan T'
|
__author__ = 'floweros edited by Huan T'
|
||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
# keep_only_tags = [
|
keep_only_tags = [
|
||||||
# dict(name='div',attrs={'class':'article'}),
|
dict(name='div',attrs={'id':'article'}),
|
||||||
# dict(name='div',attrs={'class':'commentTop'}),
|
dict(name='div',attrs={'class':['postBody' 'details']}),
|
||||||
# ]
|
dict(name='footer',attrs={'class':['clearfix meta article-foot']}),
|
||||||
|
dict(name='article',attrs={'class':['fhitem fhitem-story article usermode thumbs grid_24']}),
|
||||||
|
dict(name='dl',attrs={'class':'relatedPosts'}),
|
||||||
|
dict(name='h2',attrs={'class':'story'}),
|
||||||
|
dict(name='span',attrs={'class':'comments'}),
|
||||||
|
]
|
||||||
|
|
||||||
feeds = [
|
|
||||||
|
remove_tags = [
|
||||||
|
dict(name='aside',attrs={'id':'slashboxes'}),
|
||||||
|
dict(name='div',attrs={'class':'paginate'}),
|
||||||
|
dict(name='section',attrs={'id':'comments'}),
|
||||||
|
dict(name='span',attrs={'class':'topic'}),
|
||||||
|
]
|
||||||
|
|
||||||
|
feeds = [
|
||||||
(u'Slashdot',
|
(u'Slashdot',
|
||||||
u'http://rss.slashdot.org/Slashdot/slashdot'),
|
u'http://rss.slashdot.org/Slashdot/slashdot'),
|
||||||
(u'/. IT',
|
(u'/. IT',
|
||||||
@ -37,5 +50,3 @@ class Slashdot(BasicNewsRecipe):
|
|||||||
u'http://rss.slashdot.org/Slashdot/slashdotYourRightsOnline')
|
u'http://rss.slashdot.org/Slashdot/slashdotYourRightsOnline')
|
||||||
]
|
]
|
||||||
|
|
||||||
def get_article_url(self, article):
|
|
||||||
return article.get('feedburner_origlink', None)
|
|
||||||
|
@ -48,7 +48,7 @@ authors_completer_append_separator = False
|
|||||||
# When this tweak is changed, the author_sort values stored with each author
|
# When this tweak is changed, the author_sort values stored with each author
|
||||||
# must be recomputed by right-clicking on an author in the left-hand tags pane,
|
# must be recomputed by right-clicking on an author in the left-hand tags pane,
|
||||||
# selecting 'manage authors', and pressing 'Recalculate all author sort values'.
|
# selecting 'manage authors', and pressing 'Recalculate all author sort values'.
|
||||||
author_sort_copy_method = 'invert'
|
author_sort_copy_method = 'comma'
|
||||||
|
|
||||||
#: Use author sort in Tag Browser
|
#: Use author sort in Tag Browser
|
||||||
# Set which author field to display in the tags pane (the list of authors,
|
# Set which author field to display in the tags pane (the list of authors,
|
||||||
|
@ -108,10 +108,10 @@ class ANDROID(USBMS):
|
|||||||
'SGH-T849', '_MB300', 'A70S', 'S_ANDROID', 'A101IT', 'A70H',
|
'SGH-T849', '_MB300', 'A70S', 'S_ANDROID', 'A101IT', 'A70H',
|
||||||
'IDEOS_TABLET', 'MYTOUCH_4G', 'UMS_COMPOSITE', 'SCH-I800_CARD',
|
'IDEOS_TABLET', 'MYTOUCH_4G', 'UMS_COMPOSITE', 'SCH-I800_CARD',
|
||||||
'7', 'A956', 'A955', 'A43', 'ANDROID_PLATFORM', 'TEGRA_2',
|
'7', 'A956', 'A955', 'A43', 'ANDROID_PLATFORM', 'TEGRA_2',
|
||||||
'MB860', 'MULTI-CARD', 'MID7015A']
|
'MB860', 'MULTI-CARD', 'MID7015A', 'INCREDIBLE']
|
||||||
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', 'SAMSUNG_ANDROID', 'GT-P1000_CARD',
|
'FILE-STOR_GADGET', 'SGH-T959', 'SAMSUNG_ANDROID', 'GT-P1000_CARD',
|
||||||
'A70S', 'A101IT', '7']
|
'A70S', 'A101IT', '7', 'INCREDIBLE']
|
||||||
|
|
||||||
OSX_MAIN_MEM = 'Android Device Main Memory'
|
OSX_MAIN_MEM = 'Android Device Main Memory'
|
||||||
|
|
||||||
|
@ -325,9 +325,8 @@ class Source(Plugin):
|
|||||||
tokens = title.split()
|
tokens = title.split()
|
||||||
for token in tokens:
|
for token in tokens:
|
||||||
token = token.strip()
|
token = token.strip()
|
||||||
if token and token.lower() not in ('a', 'and', 'the', '&') and strip_joiners:
|
if token and (not strip_joiners or token.lower() not in ('a',
|
||||||
yield token
|
'and', 'the', '&')):
|
||||||
elif token:
|
|
||||||
yield token
|
yield token
|
||||||
|
|
||||||
def split_jobs(self, jobs, num):
|
def split_jobs(self, jobs, num):
|
||||||
@ -375,7 +374,12 @@ class Source(Plugin):
|
|||||||
def get_book_url(self, identifiers):
|
def get_book_url(self, identifiers):
|
||||||
'''
|
'''
|
||||||
Return the URL for the book identified by identifiers at this source.
|
Return the URL for the book identified by identifiers at this source.
|
||||||
If no URL is found, return None.
|
This URL must be browseable to by a human using a browser. It is meant
|
||||||
|
to provide a clickable link for the user to easily visit the books page
|
||||||
|
at this source.
|
||||||
|
If no URL is found, return None. This method must be quick, and
|
||||||
|
consistent, so only implement it if it is possible to construct the URL
|
||||||
|
from a known scheme given identifiers.
|
||||||
'''
|
'''
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@ -433,7 +433,7 @@ def urls_from_identifiers(identifiers): # {{{
|
|||||||
pass
|
pass
|
||||||
isbn = identifiers.get('isbn', None)
|
isbn = identifiers.get('isbn', None)
|
||||||
if isbn:
|
if isbn:
|
||||||
ans.append(('ISBN',
|
ans.append((isbn,
|
||||||
'http://www.worldcat.org/search?q=bn%%3A%s&qt=advanced'%isbn))
|
'http://www.worldcat.org/search?q=bn%%3A%s&qt=advanced'%isbn))
|
||||||
return ans
|
return ans
|
||||||
# }}}
|
# }}}
|
||||||
@ -444,13 +444,18 @@ if __name__ == '__main__': # tests {{{
|
|||||||
from calibre.ebooks.metadata.sources.test import (test_identify,
|
from calibre.ebooks.metadata.sources.test import (test_identify,
|
||||||
title_test, authors_test)
|
title_test, authors_test)
|
||||||
tests = [
|
tests = [
|
||||||
|
(
|
||||||
|
{'title':'Magykal Papers',
|
||||||
|
'authors':['Sage']},
|
||||||
|
[title_test('The Magykal Papers', exact=True)],
|
||||||
|
),
|
||||||
|
|
||||||
|
|
||||||
( # An e-book ISBN not on Amazon, one of the authors is
|
( # An e-book ISBN not on Amazon, one of the authors is
|
||||||
# unknown to Amazon
|
# unknown to Amazon
|
||||||
{'identifiers':{'isbn': '9780307459671'},
|
{'identifiers':{'isbn': '9780307459671'},
|
||||||
'title':'Invisible Gorilla', 'authors':['Christopher Chabris']},
|
'title':'Invisible Gorilla', 'authors':['Christopher Chabris']},
|
||||||
[title_test('The Invisible Gorilla',
|
[title_test('The Invisible Gorilla', exact=True)]
|
||||||
exact=True), authors_test(['Christopher Chabris', 'Daniel Simons'])]
|
|
||||||
|
|
||||||
),
|
),
|
||||||
|
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
from __future__ import (unicode_literals, division, absolute_import,
|
||||||
|
print_function)
|
||||||
|
|
||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
@ -6,14 +9,13 @@ __docformat__ = 'restructuredtext en'
|
|||||||
'''
|
'''
|
||||||
Fetch metadata using Overdrive Content Reserve
|
Fetch metadata using Overdrive Content Reserve
|
||||||
'''
|
'''
|
||||||
import sys, re, random, urllib, mechanize, copy
|
import re, random, mechanize, copy
|
||||||
from threading import RLock
|
from threading import RLock
|
||||||
from Queue import Queue, Empty
|
from Queue import Queue, Empty
|
||||||
|
|
||||||
from lxml import html, etree
|
from lxml import html
|
||||||
from lxml.html import soupparser
|
from lxml.html import soupparser
|
||||||
|
|
||||||
from calibre import browser
|
|
||||||
from calibre.ebooks.metadata import check_isbn
|
from calibre.ebooks.metadata import check_isbn
|
||||||
from calibre.ebooks.metadata.sources.base import Source
|
from calibre.ebooks.metadata.sources.base import Source
|
||||||
from calibre.ebooks.metadata.book.base import Metadata
|
from calibre.ebooks.metadata.book.base import Metadata
|
||||||
@ -33,7 +35,7 @@ class OverDrive(Source):
|
|||||||
|
|
||||||
capabilities = frozenset(['identify', 'cover'])
|
capabilities = frozenset(['identify', 'cover'])
|
||||||
touched_fields = frozenset(['title', 'authors', 'tags', 'pubdate',
|
touched_fields = frozenset(['title', 'authors', 'tags', 'pubdate',
|
||||||
'comments', 'publisher', 'identifier:isbn', 'series', 'series_num',
|
'comments', 'publisher', 'identifier:isbn', 'series', 'series_index',
|
||||||
'language', 'identifier:overdrive'])
|
'language', 'identifier:overdrive'])
|
||||||
has_html_comments = True
|
has_html_comments = True
|
||||||
supports_gzip_transfer_encoding = False
|
supports_gzip_transfer_encoding = False
|
||||||
@ -43,7 +45,6 @@ class OverDrive(Source):
|
|||||||
Source.__init__(self, *args, **kwargs)
|
Source.__init__(self, *args, **kwargs)
|
||||||
self.prefs.defaults['ignore_fields'] =['tags', 'pubdate', 'comments', 'identifier:isbn', 'language']
|
self.prefs.defaults['ignore_fields'] =['tags', 'pubdate', 'comments', 'identifier:isbn', 'language']
|
||||||
|
|
||||||
|
|
||||||
def identify(self, log, result_queue, abort, title=None, authors=None, # {{{
|
def identify(self, log, result_queue, abort, title=None, authors=None, # {{{
|
||||||
identifiers={}, timeout=30):
|
identifiers={}, timeout=30):
|
||||||
ovrdrv_id = identifiers.get('overdrive', None)
|
ovrdrv_id = identifiers.get('overdrive', None)
|
||||||
@ -68,19 +69,6 @@ class OverDrive(Source):
|
|||||||
return None
|
return None
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
def get_book_url(self, identifiers): # {{{
|
|
||||||
ovrdrv_id = identifiers.get('overdrive', None)
|
|
||||||
if ovrdrv_id is not None:
|
|
||||||
ovrdrv_data = ovrdrv_data_cache.get(ovrdrv_id, None)
|
|
||||||
if ovrdrv_data:
|
|
||||||
return ovrdrv_data[1]
|
|
||||||
else:
|
|
||||||
br = browser()
|
|
||||||
ovrdrv_data = self.to_ovrdrv_data(br, None, None, ovrdrv_id)
|
|
||||||
return ovrdrv_data[1]
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
def download_cover(self, log, result_queue, abort, # {{{
|
def download_cover(self, log, result_queue, abort, # {{{
|
||||||
title=None, authors=None, identifiers={}, timeout=30):
|
title=None, authors=None, identifiers={}, timeout=30):
|
||||||
cached_url = self.get_cached_cover_url(identifiers)
|
cached_url = self.get_cached_cover_url(identifiers)
|
||||||
@ -136,28 +124,6 @@ class OverDrive(Source):
|
|||||||
return url
|
return url
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
def create_query(self, title=None, authors=None, identifiers={}):
|
|
||||||
q = ''
|
|
||||||
if title or authors:
|
|
||||||
def build_term(prefix, parts):
|
|
||||||
return ' '.join('in'+prefix + ':' + x for x in parts)
|
|
||||||
title_tokens = list(self.get_title_tokens(title, False, True))
|
|
||||||
if title_tokens:
|
|
||||||
q += build_term('title', title_tokens)
|
|
||||||
author_tokens = self.get_author_tokens(authors,
|
|
||||||
only_first_author=True)
|
|
||||||
if author_tokens:
|
|
||||||
q += ('+' if q else '') + build_term('author',
|
|
||||||
author_tokens)
|
|
||||||
|
|
||||||
if isinstance(q, unicode):
|
|
||||||
q = q.encode('utf-8')
|
|
||||||
if not q:
|
|
||||||
return None
|
|
||||||
return BASE_URL+urlencode({
|
|
||||||
'q':q,
|
|
||||||
})
|
|
||||||
|
|
||||||
def get_base_referer(self): # to be used for passing referrer headers to cover download
|
def get_base_referer(self): # to be used for passing referrer headers to cover download
|
||||||
choices = [
|
choices = [
|
||||||
'http://overdrive.chipublib.org/82DC601D-7DDE-4212-B43A-09D821935B01/10/375/en/',
|
'http://overdrive.chipublib.org/82DC601D-7DDE-4212-B43A-09D821935B01/10/375/en/',
|
||||||
@ -209,7 +175,6 @@ class OverDrive(Source):
|
|||||||
|
|
||||||
br.set_cookiejar(clean_cj)
|
br.set_cookiejar(clean_cj)
|
||||||
|
|
||||||
|
|
||||||
def overdrive_search(self, br, q, title, author):
|
def overdrive_search(self, br, q, title, author):
|
||||||
# re-initialize the cookiejar to so that it's clean
|
# re-initialize the cookiejar to so that it's clean
|
||||||
clean_cj = mechanize.CookieJar()
|
clean_cj = mechanize.CookieJar()
|
||||||
@ -275,11 +240,11 @@ class OverDrive(Source):
|
|||||||
if ovrdrv_id is not None and int(formatid) in [1, 50, 410, 900]:
|
if ovrdrv_id is not None and int(formatid) in [1, 50, 410, 900]:
|
||||||
#print "overdrive id is not None, searching based on format type priority"
|
#print "overdrive id is not None, searching based on format type priority"
|
||||||
return self.format_results(reserveid, od_title, subtitle, series, publisher,
|
return self.format_results(reserveid, od_title, subtitle, series, publisher,
|
||||||
creators, thumbimage, worldcatlink, formatid)
|
creators, thumbimage, worldcatlink, formatid)
|
||||||
else:
|
else:
|
||||||
creators = creators.split(', ')
|
creators = creators.split(', ')
|
||||||
# if an exact match in a preferred format occurs
|
# if an exact match in a preferred format occurs
|
||||||
if creators[0] == author[0] and od_title == title and int(formatid) in [1, 50, 410, 900]:
|
if (author and creators[0] == author[0]) and od_title == title and int(formatid) in [1, 50, 410, 900]:
|
||||||
return self.format_results(reserveid, od_title, subtitle, series, publisher,
|
return self.format_results(reserveid, od_title, subtitle, series, publisher,
|
||||||
creators, thumbimage, worldcatlink, formatid)
|
creators, thumbimage, worldcatlink, formatid)
|
||||||
else:
|
else:
|
||||||
@ -312,7 +277,6 @@ class OverDrive(Source):
|
|||||||
else:
|
else:
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
|
|
||||||
def overdrive_get_record(self, br, q, ovrdrv_id):
|
def overdrive_get_record(self, br, q, ovrdrv_id):
|
||||||
search_url = q+'SearchResults.aspx?ReserveID={'+ovrdrv_id+'}'
|
search_url = q+'SearchResults.aspx?ReserveID={'+ovrdrv_id+'}'
|
||||||
results_url = q+'SearchResults.svc/GetResults?sEcho=1&iColumns=18&sColumns=ReserveID%2CTitle%2CSubtitle%2CEdition%2CSeries%2CPublisher%2CFormat%2CFormatID%2CCreators%2CThumbImage%2CShortDescription%2CWorldCatLink%2CExcerptLink%2CCreatorFile%2CSortTitle%2CAvailableToLibrary%2CAvailableToRetailer%2CRelevancyRank&iDisplayStart=0&iDisplayLength=10&sSearch=&bEscapeRegex=true&iSortingCols=1&iSortCol_0=17&sSortDir_0=asc'
|
results_url = q+'SearchResults.svc/GetResults?sEcho=1&iColumns=18&sColumns=ReserveID%2CTitle%2CSubtitle%2CEdition%2CSeries%2CPublisher%2CFormat%2CFormatID%2CCreators%2CThumbImage%2CShortDescription%2CWorldCatLink%2CExcerptLink%2CCreatorFile%2CSortTitle%2CAvailableToLibrary%2CAvailableToRetailer%2CRelevancyRank&iDisplayStart=0&iDisplayLength=10&sSearch=&bEscapeRegex=true&iSortingCols=1&iSortCol_0=17&sSortDir_0=asc'
|
||||||
@ -390,7 +354,10 @@ class OverDrive(Source):
|
|||||||
if len(ovrdrv_data[3]) > 1:
|
if len(ovrdrv_data[3]) > 1:
|
||||||
mi.series = ovrdrv_data[3]
|
mi.series = ovrdrv_data[3]
|
||||||
if ovrdrv_data[4]:
|
if ovrdrv_data[4]:
|
||||||
mi.series_index = ovrdrv_data[4]
|
try:
|
||||||
|
mi.series_index = float(ovrdrv_data[4])
|
||||||
|
except:
|
||||||
|
pass
|
||||||
mi.publisher = ovrdrv_data[5]
|
mi.publisher = ovrdrv_data[5]
|
||||||
mi.authors = ovrdrv_data[6]
|
mi.authors = ovrdrv_data[6]
|
||||||
mi.title = ovrdrv_data[8]
|
mi.title = ovrdrv_data[8]
|
||||||
@ -407,7 +374,7 @@ class OverDrive(Source):
|
|||||||
if callable(getattr(e, 'getcode', None)) and \
|
if callable(getattr(e, 'getcode', None)) and \
|
||||||
e.getcode() == 404:
|
e.getcode() == 404:
|
||||||
return False
|
return False
|
||||||
raise
|
raise
|
||||||
raw = xml_to_unicode(raw, strip_encoding_pats=True,
|
raw = xml_to_unicode(raw, strip_encoding_pats=True,
|
||||||
resolve_entities=True)[0]
|
resolve_entities=True)[0]
|
||||||
try:
|
try:
|
||||||
@ -427,12 +394,12 @@ class OverDrive(Source):
|
|||||||
if lang:
|
if lang:
|
||||||
mi.language = lang[0].strip()
|
mi.language = lang[0].strip()
|
||||||
|
|
||||||
#if ebook_isbn:
|
if ebook_isbn:
|
||||||
# print "ebook isbn is "+str(ebook_isbn[0])
|
#print "ebook isbn is "+str(ebook_isbn[0])
|
||||||
# isbn = check_isbn(ebook_isbn[0].strip())
|
isbn = check_isbn(ebook_isbn[0].strip())
|
||||||
# if isbn:
|
if isbn:
|
||||||
# self.cache_isbn_to_identifier(isbn, ovrdrv_id)
|
self.cache_isbn_to_identifier(isbn, ovrdrv_id)
|
||||||
# mi.isbn = isbn
|
mi.isbn = isbn
|
||||||
if subjects:
|
if subjects:
|
||||||
mi.tags = [tag.strip() for tag in subjects[0].split(',')]
|
mi.tags = [tag.strip() for tag in subjects[0].split(',')]
|
||||||
|
|
||||||
@ -448,8 +415,25 @@ class OverDrive(Source):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def main(args=sys.argv):
|
|
||||||
return 0
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
sys.exit(main())
|
# To run these test use:
|
||||||
|
# calibre-debug -e src/calibre/ebooks/metadata/sources/overdrive.py
|
||||||
|
from calibre.ebooks.metadata.sources.test import (test_identify_plugin,
|
||||||
|
title_test, authors_test)
|
||||||
|
test_identify_plugin(OverDrive.name,
|
||||||
|
[
|
||||||
|
|
||||||
|
(
|
||||||
|
{'title':'Foundation and Earth',
|
||||||
|
'authors':['Asimov']},
|
||||||
|
[title_test('Foundation and Earth', exact=True),
|
||||||
|
authors_test(['Isaac Asimov'])]
|
||||||
|
),
|
||||||
|
|
||||||
|
(
|
||||||
|
{'title': 'Elephants', 'authors':['Agatha']},
|
||||||
|
[title_test('Elephants Can Remember', exact=False),
|
||||||
|
authors_test(['Agatha Christie'])]
|
||||||
|
),
|
||||||
|
])
|
||||||
|
|
||||||
|
@ -29,8 +29,7 @@ from calibre.ebooks.metadata.meta import set_metadata
|
|||||||
from calibre.constants import DEBUG
|
from calibre.constants import DEBUG
|
||||||
from calibre.utils.config import prefs, tweaks
|
from calibre.utils.config import prefs, tweaks
|
||||||
from calibre.utils.magick.draw import thumbnail
|
from calibre.utils.magick.draw import thumbnail
|
||||||
from calibre.library.save_to_disk import plugboard_any_device_value, \
|
from calibre.library.save_to_disk import find_plugboard
|
||||||
plugboard_any_format_value
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
class DeviceJob(BaseJob): # {{{
|
class DeviceJob(BaseJob): # {{{
|
||||||
@ -93,23 +92,6 @@ class DeviceJob(BaseJob): # {{{
|
|||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
def find_plugboard(device_name, format, plugboards):
|
|
||||||
cpb = None
|
|
||||||
if format in plugboards:
|
|
||||||
cpb = plugboards[format]
|
|
||||||
elif plugboard_any_format_value in plugboards:
|
|
||||||
cpb = plugboards[plugboard_any_format_value]
|
|
||||||
if cpb is not None:
|
|
||||||
if device_name in cpb:
|
|
||||||
cpb = cpb[device_name]
|
|
||||||
elif plugboard_any_device_value in cpb:
|
|
||||||
cpb = cpb[plugboard_any_device_value]
|
|
||||||
else:
|
|
||||||
cpb = None
|
|
||||||
if DEBUG:
|
|
||||||
prints('Device using plugboard', format, device_name, cpb)
|
|
||||||
return cpb
|
|
||||||
|
|
||||||
def device_name_for_plugboards(device_class):
|
def device_name_for_plugboards(device_class):
|
||||||
if hasattr(device_class, 'DEVICE_PLUGBOARD_NAME'):
|
if hasattr(device_class, 'DEVICE_PLUGBOARD_NAME'):
|
||||||
return device_class.DEVICE_PLUGBOARD_NAME
|
return device_class.DEVICE_PLUGBOARD_NAME
|
||||||
@ -607,6 +589,16 @@ class DeviceMenu(QMenu): # {{{
|
|||||||
|
|
||||||
class DeviceMixin(object): # {{{
|
class DeviceMixin(object): # {{{
|
||||||
|
|
||||||
|
#: This signal is emitted once, after metadata is downloaded from the
|
||||||
|
#: connected device.
|
||||||
|
#: The sequence: gui.device_manager.is_device_connected will become True,
|
||||||
|
#: then sometime later gui.device_metadata_available will be signaled.
|
||||||
|
#: This does not mean that there are no more jobs running. Automatic metadata
|
||||||
|
#: management might have kicked off a sync_booklists to write new metadata onto
|
||||||
|
#: the device, and that job might still be running when the signal is emitted.
|
||||||
|
device_metadata_available = pyqtSignal()
|
||||||
|
device_connection_changed = pyqtSignal(object)
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.device_error_dialog = error_dialog(self, _('Error'),
|
self.device_error_dialog = error_dialog(self, _('Error'),
|
||||||
_('Error communicating with device'), ' ')
|
_('Error communicating with device'), ' ')
|
||||||
@ -753,6 +745,7 @@ class DeviceMixin(object): # {{{
|
|||||||
self.location_manager.update_devices()
|
self.location_manager.update_devices()
|
||||||
self.library_view.set_device_connected(self.device_connected)
|
self.library_view.set_device_connected(self.device_connected)
|
||||||
self.refresh_ondevice()
|
self.refresh_ondevice()
|
||||||
|
self.device_connection_changed.emit(connected)
|
||||||
|
|
||||||
def info_read(self, job):
|
def info_read(self, job):
|
||||||
'''
|
'''
|
||||||
@ -791,6 +784,7 @@ class DeviceMixin(object): # {{{
|
|||||||
self.sync_news()
|
self.sync_news()
|
||||||
self.sync_catalogs()
|
self.sync_catalogs()
|
||||||
self.refresh_ondevice()
|
self.refresh_ondevice()
|
||||||
|
self.device_metadata_available.emit()
|
||||||
|
|
||||||
def refresh_ondevice(self, reset_only = False):
|
def refresh_ondevice(self, reset_only = False):
|
||||||
'''
|
'''
|
||||||
@ -892,7 +886,7 @@ class DeviceMixin(object): # {{{
|
|||||||
sub_dest_parts.append('')
|
sub_dest_parts.append('')
|
||||||
to = sub_dest_parts[0]
|
to = sub_dest_parts[0]
|
||||||
fmts = sub_dest_parts[1]
|
fmts = sub_dest_parts[1]
|
||||||
subject = ';'.join(sub_dest_parts[2:])
|
subject = ';'.join(sub_dest_parts[2:])
|
||||||
fmts = [x.strip().lower() for x in fmts.split(',')]
|
fmts = [x.strip().lower() for x in fmts.split(',')]
|
||||||
self.send_by_mail(to, fmts, delete, subject=subject)
|
self.send_by_mail(to, fmts, delete, subject=subject)
|
||||||
|
|
||||||
|
@ -30,7 +30,6 @@ from calibre.ebooks.metadata.book.base import Metadata
|
|||||||
from calibre.gui2 import error_dialog, NONE
|
from calibre.gui2 import error_dialog, NONE
|
||||||
from calibre.utils.date import utcnow, fromordinal, format_date
|
from calibre.utils.date import utcnow, fromordinal, format_date
|
||||||
from calibre.library.comments import comments_to_html
|
from calibre.library.comments import comments_to_html
|
||||||
from calibre.constants import islinux
|
|
||||||
from calibre import force_unicode
|
from calibre import force_unicode
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
@ -117,12 +116,10 @@ class CoverDelegate(QStyledItemDelegate): # {{{
|
|||||||
|
|
||||||
def paint(self, painter, option, index):
|
def paint(self, painter, option, index):
|
||||||
QStyledItemDelegate.paint(self, painter, option, index)
|
QStyledItemDelegate.paint(self, painter, option, index)
|
||||||
if islinux:
|
# Ensure the cover is rendered over any selection rect
|
||||||
# On linux for some reason the selected color is drawn on top of
|
style = QApplication.style()
|
||||||
# the decoration
|
style.drawItemPixmap(painter, option.rect, Qt.AlignTop|Qt.AlignHCenter,
|
||||||
style = QApplication.style()
|
QPixmap(index.data(Qt.DecorationRole)))
|
||||||
style.drawItemPixmap(painter, option.rect, Qt.AlignTop|Qt.AlignHCenter,
|
|
||||||
QPixmap(index.data(Qt.DecorationRole)))
|
|
||||||
if self.timer.isActive() and index.data(Qt.UserRole).toBool():
|
if self.timer.isActive() and index.data(Qt.UserRole).toBool():
|
||||||
rect = QRect(0, 0, self.spinner_width, self.spinner_width)
|
rect = QRect(0, 0, self.spinner_width, self.spinner_width)
|
||||||
rect.moveCenter(option.rect.center())
|
rect.moveCenter(option.rect.center())
|
||||||
@ -952,7 +949,7 @@ class CoverFetch(QDialog): # {{{
|
|||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
DEBUG_DIALOG = True
|
#DEBUG_DIALOG = True
|
||||||
app = QApplication([])
|
app = QApplication([])
|
||||||
d = FullFetch()
|
d = FullFetch()
|
||||||
d.start(title='great gatsby', authors=['fitzgerald'])
|
d.start(title='great gatsby', authors=['fitzgerald'])
|
||||||
|
@ -337,7 +337,13 @@ def show_config_widget(category, name, gui=None, show_restart_msg=False,
|
|||||||
bb.button(bb.RestoreDefaults).setEnabled(w.supports_restoring_to_defaults)
|
bb.button(bb.RestoreDefaults).setEnabled(w.supports_restoring_to_defaults)
|
||||||
bb.button(bb.Apply).setEnabled(False)
|
bb.button(bb.Apply).setEnabled(False)
|
||||||
bb.button(bb.Apply).clicked.connect(d.accept)
|
bb.button(bb.Apply).clicked.connect(d.accept)
|
||||||
w.changed_signal.connect(lambda : bb.button(bb.Apply).setEnabled(True))
|
def onchange():
|
||||||
|
b = bb.button(bb.Apply)
|
||||||
|
b.setEnabled(True)
|
||||||
|
b.setDefault(True)
|
||||||
|
b.setAutoDefault(True)
|
||||||
|
w.changed_signal.connect(onchange)
|
||||||
|
bb.button(bb.Cancel).setFocus(True)
|
||||||
l = QVBoxLayout()
|
l = QVBoxLayout()
|
||||||
d.setLayout(l)
|
d.setLayout(l)
|
||||||
l.addWidget(w)
|
l.addWidget(w)
|
||||||
|
@ -7,6 +7,7 @@ __docformat__ = 'restructuredtext en'
|
|||||||
|
|
||||||
from PyQt4.Qt import Qt, QLineEdit, QComboBox, SIGNAL, QListWidgetItem
|
from PyQt4.Qt import Qt, QLineEdit, QComboBox, SIGNAL, QListWidgetItem
|
||||||
|
|
||||||
|
from calibre.customize.ui import is_disabled
|
||||||
from calibre.gui2 import error_dialog
|
from calibre.gui2 import error_dialog
|
||||||
from calibre.gui2.device import device_name_for_plugboards
|
from calibre.gui2.device import device_name_for_plugboards
|
||||||
from calibre.gui2.dialogs.template_dialog import TemplateDialog
|
from calibre.gui2.dialogs.template_dialog import TemplateDialog
|
||||||
@ -15,6 +16,8 @@ from calibre.gui2.preferences.plugboard_ui import Ui_Form
|
|||||||
from calibre.customize.ui import metadata_writers, device_plugins
|
from calibre.customize.ui import metadata_writers, device_plugins
|
||||||
from calibre.library.save_to_disk import plugboard_any_format_value, \
|
from calibre.library.save_to_disk import plugboard_any_format_value, \
|
||||||
plugboard_any_device_value, plugboard_save_to_disk_value
|
plugboard_any_device_value, plugboard_save_to_disk_value
|
||||||
|
from calibre.library.server.content import plugboard_content_server_value, \
|
||||||
|
plugboard_content_server_formats
|
||||||
from calibre.utils.formatter import validation_formatter
|
from calibre.utils.formatter import validation_formatter
|
||||||
|
|
||||||
|
|
||||||
@ -68,19 +71,26 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
|
|||||||
self.device_label.setText(_('Device currently connected: None'))
|
self.device_label.setText(_('Device currently connected: None'))
|
||||||
|
|
||||||
self.devices = ['', 'APPLE', 'FOLDER_DEVICE']
|
self.devices = ['', 'APPLE', 'FOLDER_DEVICE']
|
||||||
|
self.device_to_formats_map = {}
|
||||||
for device in device_plugins():
|
for device in device_plugins():
|
||||||
n = device_name_for_plugboards(device)
|
n = device_name_for_plugboards(device)
|
||||||
|
self.device_to_formats_map[n] = device.FORMATS
|
||||||
if n not in self.devices:
|
if n not in self.devices:
|
||||||
self.devices.append(n)
|
self.devices.append(n)
|
||||||
self.devices.sort(cmp=lambda x, y: cmp(x.lower(), y.lower()))
|
self.devices.sort(cmp=lambda x, y: cmp(x.lower(), y.lower()))
|
||||||
self.devices.insert(1, plugboard_save_to_disk_value)
|
self.devices.insert(1, plugboard_save_to_disk_value)
|
||||||
self.devices.insert(2, plugboard_any_device_value)
|
self.devices.insert(1, plugboard_content_server_value)
|
||||||
|
self.device_to_formats_map[plugboard_content_server_value] = \
|
||||||
|
plugboard_content_server_formats
|
||||||
|
self.devices.insert(1, plugboard_any_device_value)
|
||||||
self.new_device.addItems(self.devices)
|
self.new_device.addItems(self.devices)
|
||||||
|
|
||||||
self.formats = ['']
|
self.formats = ['']
|
||||||
for w in metadata_writers():
|
for w in metadata_writers():
|
||||||
for f in w.file_types:
|
if not is_disabled(w):
|
||||||
self.formats.append(f)
|
for f in w.file_types:
|
||||||
|
if not f in self.formats:
|
||||||
|
self.formats.append(f)
|
||||||
self.formats.append('device_db')
|
self.formats.append('device_db')
|
||||||
self.formats.sort()
|
self.formats.sort()
|
||||||
self.formats.insert(1, plugboard_any_format_value)
|
self.formats.insert(1, plugboard_any_format_value)
|
||||||
@ -230,6 +240,15 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
|
|||||||
show=True)
|
show=True)
|
||||||
self.new_device.setCurrentIndex(0)
|
self.new_device.setCurrentIndex(0)
|
||||||
return
|
return
|
||||||
|
if self.current_device in self.device_to_formats_map:
|
||||||
|
allowable_formats = self.device_to_formats_map[self.current_device]
|
||||||
|
if self.current_format not in allowable_formats:
|
||||||
|
error_dialog(self, '',
|
||||||
|
_('The {0} device does not support the {1} format.').
|
||||||
|
format(self.current_device, self.current_format),
|
||||||
|
show=True)
|
||||||
|
self.new_device.setCurrentIndex(0)
|
||||||
|
return
|
||||||
self.set_fields()
|
self.set_fields()
|
||||||
|
|
||||||
def new_format_changed(self, txt):
|
def new_format_changed(self, txt):
|
||||||
|
@ -8,6 +8,7 @@ __docformat__ = 'restructuredtext en'
|
|||||||
|
|
||||||
import re
|
import re
|
||||||
import time
|
import time
|
||||||
|
import traceback
|
||||||
from contextlib import closing
|
from contextlib import closing
|
||||||
from random import shuffle
|
from random import shuffle
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
@ -20,9 +21,12 @@ from calibre import browser
|
|||||||
from calibre.gui2 import NONE
|
from calibre.gui2 import NONE
|
||||||
from calibre.gui2.progress_indicator import ProgressIndicator
|
from calibre.gui2.progress_indicator import ProgressIndicator
|
||||||
from calibre.gui2.store.search_ui import Ui_Dialog
|
from calibre.gui2.store.search_ui import Ui_Dialog
|
||||||
|
from calibre.library.caches import _match, CONTAINS_MATCH, EQUALS_MATCH, \
|
||||||
|
REGEXP_MATCH
|
||||||
from calibre.utils.config import DynamicConfig
|
from calibre.utils.config import DynamicConfig
|
||||||
from calibre.utils.icu import sort_key
|
from calibre.utils.icu import sort_key
|
||||||
from calibre.utils.magick.draw import thumbnail
|
from calibre.utils.magick.draw import thumbnail
|
||||||
|
from calibre.utils.search_query_parser import SearchQueryParser
|
||||||
|
|
||||||
HANG_TIME = 75000 # milliseconds seconds
|
HANG_TIME = 75000 # milliseconds seconds
|
||||||
TIMEOUT = 75 # seconds
|
TIMEOUT = 75 # seconds
|
||||||
@ -290,11 +294,15 @@ class SearchThread(Thread):
|
|||||||
while self._run and not self.tasks.empty():
|
while self._run and not self.tasks.empty():
|
||||||
try:
|
try:
|
||||||
query, store_name, store_plugin, timeout = self.tasks.get()
|
query, store_name, store_plugin, timeout = self.tasks.get()
|
||||||
for res in store_plugin.search(query, timeout=timeout):
|
squery = query
|
||||||
|
for loc in SearchFilter.USABLE_LOCATIONS:
|
||||||
|
squery = re.sub(r'%s:"?(?P<a>[^\s"]+)"?' % loc, '\g<a>', squery)
|
||||||
|
for res in store_plugin.search(squery, timeout=timeout):
|
||||||
if not self._run:
|
if not self._run:
|
||||||
return
|
return
|
||||||
res.store_name = store_name
|
res.store_name = store_name
|
||||||
self.results.put(res)
|
if SearchFilter(res).parse(query):
|
||||||
|
self.results.put(res)
|
||||||
self.tasks.task_done()
|
self.tasks.task_done()
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
@ -450,3 +458,82 @@ class Matches(QAbstractItemModel):
|
|||||||
if reset:
|
if reset:
|
||||||
self.reset()
|
self.reset()
|
||||||
|
|
||||||
|
|
||||||
|
class SearchFilter(SearchQueryParser):
|
||||||
|
|
||||||
|
USABLE_LOCATIONS = [
|
||||||
|
'all',
|
||||||
|
'author',
|
||||||
|
'authors',
|
||||||
|
'cover',
|
||||||
|
'price',
|
||||||
|
'title',
|
||||||
|
'store',
|
||||||
|
]
|
||||||
|
|
||||||
|
def __init__(self, search_result):
|
||||||
|
SearchQueryParser.__init__(self, locations=self.USABLE_LOCATIONS)
|
||||||
|
self.search_result = search_result
|
||||||
|
|
||||||
|
def universal_set(self):
|
||||||
|
return set([self.search_result])
|
||||||
|
|
||||||
|
def get_matches(self, location, query):
|
||||||
|
location = location.lower().strip()
|
||||||
|
if location == 'authors':
|
||||||
|
location = 'author'
|
||||||
|
|
||||||
|
matchkind = CONTAINS_MATCH
|
||||||
|
if len(query) > 1:
|
||||||
|
if query.startswith('\\'):
|
||||||
|
query = query[1:]
|
||||||
|
elif query.startswith('='):
|
||||||
|
matchkind = EQUALS_MATCH
|
||||||
|
query = query[1:]
|
||||||
|
elif query.startswith('~'):
|
||||||
|
matchkind = REGEXP_MATCH
|
||||||
|
query = query[1:]
|
||||||
|
if matchkind != REGEXP_MATCH: ### leave case in regexps because it can be significant e.g. \S \W \D
|
||||||
|
query = query.lower()
|
||||||
|
|
||||||
|
if location not in self.USABLE_LOCATIONS:
|
||||||
|
return set([])
|
||||||
|
matches = set([])
|
||||||
|
all_locs = set(self.USABLE_LOCATIONS) - set(['all'])
|
||||||
|
locations = all_locs if location == 'all' else [location]
|
||||||
|
q = {
|
||||||
|
'author': self.search_result.author.lower(),
|
||||||
|
'cover': self.search_result.cover_url,
|
||||||
|
'format': '',
|
||||||
|
'price': self.search_result.price,
|
||||||
|
'store': self.search_result.store_name.lower(),
|
||||||
|
'title': self.search_result.title.lower(),
|
||||||
|
}
|
||||||
|
for x in ('author', 'format'):
|
||||||
|
q[x+'s'] = q[x]
|
||||||
|
for locvalue in locations:
|
||||||
|
ac_val = q[locvalue]
|
||||||
|
if query == 'true':
|
||||||
|
if ac_val is not None:
|
||||||
|
matches.add(self.search_result)
|
||||||
|
continue
|
||||||
|
if query == 'false':
|
||||||
|
if ac_val is None:
|
||||||
|
matches.add(self.search_result)
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
### Can't separate authors because comma is used for name sep and author sep
|
||||||
|
### Exact match might not get what you want. For that reason, turn author
|
||||||
|
### exactmatch searches into contains searches.
|
||||||
|
if locvalue == 'author' and matchkind == EQUALS_MATCH:
|
||||||
|
m = CONTAINS_MATCH
|
||||||
|
else:
|
||||||
|
m = matchkind
|
||||||
|
|
||||||
|
vals = [ac_val]
|
||||||
|
if _match(query, vals, m):
|
||||||
|
matches.add(self.search_result)
|
||||||
|
break
|
||||||
|
except ValueError: # Unicode errors
|
||||||
|
traceback.print_exc()
|
||||||
|
return matches
|
||||||
|
@ -31,10 +31,14 @@ class NPWebView(QWebView):
|
|||||||
proxy_parts = urlparse(http_proxy)
|
proxy_parts = urlparse(http_proxy)
|
||||||
proxy = QNetworkProxy()
|
proxy = QNetworkProxy()
|
||||||
proxy.setType(QNetworkProxy.HttpProxy)
|
proxy.setType(QNetworkProxy.HttpProxy)
|
||||||
proxy.setUser(proxy_parts.username)
|
if proxy_parts.username:
|
||||||
proxy.setPassword(proxy_parts.password)
|
proxy.setUser(proxy_parts.username)
|
||||||
proxy.setHostName(proxy_parts.hostname)
|
if proxy_parts.password:
|
||||||
proxy.setPort(proxy_parts.port)
|
proxy.setPassword(proxy_parts.password)
|
||||||
|
if proxy_parts.hostname:
|
||||||
|
proxy.setHostName(proxy_parts.hostname)
|
||||||
|
if proxy_parts.port:
|
||||||
|
proxy.setPort(proxy_parts.port)
|
||||||
self.page().networkAccessManager().setProxy(proxy)
|
self.page().networkAccessManager().setProxy(proxy)
|
||||||
|
|
||||||
self.page().setForwardUnsupportedContent(True)
|
self.page().setForwardUnsupportedContent(True)
|
||||||
|
@ -51,6 +51,23 @@ for x in FORMAT_ARG_DESCS:
|
|||||||
FORMAT_ARGS[x] = ''
|
FORMAT_ARGS[x] = ''
|
||||||
|
|
||||||
|
|
||||||
|
def find_plugboard(device_name, format, plugboards):
|
||||||
|
cpb = None
|
||||||
|
if format in plugboards:
|
||||||
|
cpb = plugboards[format]
|
||||||
|
elif plugboard_any_format_value in plugboards:
|
||||||
|
cpb = plugboards[plugboard_any_format_value]
|
||||||
|
if cpb is not None:
|
||||||
|
if device_name in cpb:
|
||||||
|
cpb = cpb[device_name]
|
||||||
|
elif plugboard_any_device_value in cpb:
|
||||||
|
cpb = cpb[plugboard_any_device_value]
|
||||||
|
else:
|
||||||
|
cpb = None
|
||||||
|
if DEBUG:
|
||||||
|
prints('Device using plugboard', format, device_name, cpb)
|
||||||
|
return cpb
|
||||||
|
|
||||||
def config(defaults=None):
|
def config(defaults=None):
|
||||||
if defaults is None:
|
if defaults is None:
|
||||||
c = Config('save_to_disk', _('Options to control saving to disk'))
|
c = Config('save_to_disk', _('Options to control saving to disk'))
|
||||||
@ -279,20 +296,7 @@ def do_save_book_to_disk(id_, mi, cover, plugboards,
|
|||||||
written = False
|
written = False
|
||||||
for fmt in formats:
|
for fmt in formats:
|
||||||
global plugboard_save_to_disk_value, plugboard_any_format_value
|
global plugboard_save_to_disk_value, plugboard_any_format_value
|
||||||
dev_name = plugboard_save_to_disk_value
|
cpb = find_plugboard(plugboard_save_to_disk_value, fmt, plugboards)
|
||||||
cpb = None
|
|
||||||
if fmt in plugboards:
|
|
||||||
cpb = plugboards[fmt]
|
|
||||||
if dev_name in cpb:
|
|
||||||
cpb = cpb[dev_name]
|
|
||||||
else:
|
|
||||||
cpb = None
|
|
||||||
if cpb is None and plugboard_any_format_value in plugboards:
|
|
||||||
cpb = plugboards[plugboard_any_format_value]
|
|
||||||
if dev_name in cpb:
|
|
||||||
cpb = cpb[dev_name]
|
|
||||||
else:
|
|
||||||
cpb = None
|
|
||||||
# Leave this here for a while, in case problems arise.
|
# Leave this here for a while, in case problems arise.
|
||||||
if cpb is not None:
|
if cpb is not None:
|
||||||
prints('Save-to-disk using plugboard:', fmt, cpb)
|
prints('Save-to-disk using plugboard:', fmt, cpb)
|
||||||
|
@ -12,9 +12,14 @@ import cherrypy
|
|||||||
from calibre import fit_image, guess_type
|
from calibre import fit_image, guess_type
|
||||||
from calibre.utils.date import fromtimestamp
|
from calibre.utils.date import fromtimestamp
|
||||||
from calibre.library.caches import SortKeyGenerator
|
from calibre.library.caches import SortKeyGenerator
|
||||||
|
from calibre.library.save_to_disk import find_plugboard
|
||||||
|
|
||||||
from calibre.utils.magick.draw import save_cover_data_to, Image, \
|
from calibre.utils.magick.draw import save_cover_data_to, Image, \
|
||||||
thumbnail as generate_thumbnail
|
thumbnail as generate_thumbnail
|
||||||
|
|
||||||
|
plugboard_content_server_value = 'content_server'
|
||||||
|
plugboard_content_server_formats = ['epub']
|
||||||
|
|
||||||
class CSSortKeyGenerator(SortKeyGenerator):
|
class CSSortKeyGenerator(SortKeyGenerator):
|
||||||
|
|
||||||
def __init__(self, fields, fm, db_prefs):
|
def __init__(self, fields, fm, db_prefs):
|
||||||
@ -183,16 +188,30 @@ class ContentServer(object):
|
|||||||
if fmt is None:
|
if fmt is None:
|
||||||
raise cherrypy.HTTPError(404, 'book: %d does not have format: %s'%(id, format))
|
raise cherrypy.HTTPError(404, 'book: %d does not have format: %s'%(id, format))
|
||||||
if format == 'EPUB':
|
if format == 'EPUB':
|
||||||
|
# Get the original metadata
|
||||||
|
mi = self.db.get_metadata(id, index_is_id=True)
|
||||||
|
|
||||||
|
# Get any EPUB plugboards for the content server
|
||||||
|
plugboards = self.db.prefs.get('plugboards', {})
|
||||||
|
cpb = find_plugboard(plugboard_content_server_value,
|
||||||
|
'epub', plugboards)
|
||||||
|
if cpb:
|
||||||
|
# Transform the metadata via the plugboard
|
||||||
|
newmi = mi.deepcopy_metadata()
|
||||||
|
newmi.template_to_attribute(mi, cpb)
|
||||||
|
else:
|
||||||
|
newmi = mi
|
||||||
|
|
||||||
|
# Write the updated file
|
||||||
from tempfile import TemporaryFile
|
from tempfile import TemporaryFile
|
||||||
from calibre.ebooks.metadata.meta import set_metadata
|
from calibre.ebooks.metadata.meta import set_metadata
|
||||||
raw = fmt.read()
|
raw = fmt.read()
|
||||||
fmt = TemporaryFile()
|
fmt = TemporaryFile()
|
||||||
fmt.write(raw)
|
fmt.write(raw)
|
||||||
fmt.seek(0)
|
fmt.seek(0)
|
||||||
set_metadata(fmt, self.db.get_metadata(id, index_is_id=True,
|
set_metadata(fmt, newmi, 'epub')
|
||||||
get_cover=True),
|
|
||||||
'epub')
|
|
||||||
fmt.seek(0)
|
fmt.seek(0)
|
||||||
|
|
||||||
mt = guess_type('dummy.'+format.lower())[0]
|
mt = guess_type('dummy.'+format.lower())[0]
|
||||||
if mt is None:
|
if mt is None:
|
||||||
mt = 'application/octet-stream'
|
mt = 'application/octet-stream'
|
||||||
|
Loading…
x
Reference in New Issue
Block a user