mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Get Books: Update Legimi and CDP store plugins
This commit is contained in:
commit
cbc14720d4
@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||||
store_version = 5 # Needed for dynamic plugin loading
|
store_version = 6 # Needed for dynamic plugin loading
|
||||||
|
|
||||||
__license__ = 'GPL 3'
|
__license__ = 'GPL 3'
|
||||||
__copyright__ = '2013-2015, Tomasz Długosz <tomek3d@gmail.com>'
|
__copyright__ = '2013-2015, Tomasz Długosz <tomek3d@gmail.com>'
|
||||||
@ -65,11 +65,10 @@ class CdpStore(BasicStoreConfig, StorePlugin):
|
|||||||
cover_url = ''.join(data.xpath('.//a[@class="product-image"]/img/@data-src'))
|
cover_url = ''.join(data.xpath('.//a[@class="product-image"]/img/@data-src'))
|
||||||
title = ''.join(data.xpath('.//h3[1]/a/@title'))
|
title = ''.join(data.xpath('.//h3[1]/a/@title'))
|
||||||
price = ''.join(data.xpath('.//span[@class="custom_price"]/text()'))+','+''.join(data.xpath('.//span[@class="custom_price"]/sup/text()'))
|
price = ''.join(data.xpath('.//span[@class="custom_price"]/text()'))+','+''.join(data.xpath('.//span[@class="custom_price"]/sup/text()'))
|
||||||
author = ''
|
author = ''.join(data.xpath('.//div[@class="authors"]/@title'))
|
||||||
formats = ''
|
formats = ''
|
||||||
with closing(br.open( id.strip(), timeout=timeout/4)) as nf:
|
with closing(br.open( id.strip(), timeout=timeout/4)) as nf:
|
||||||
idata = html.fromstring(nf.read())
|
idata = html.fromstring(nf.read())
|
||||||
author = ', '.join(idata.xpath('.//ul[@class="film-data"]/li[1]/p/text()'))
|
|
||||||
formats = idata.xpath('//div[@class="product-attributes-container"][2]/ul/li/span/text()')[-1]
|
formats = idata.xpath('//div[@class="product-attributes-container"][2]/ul/li/span/text()')[-1]
|
||||||
|
|
||||||
counter -= 1
|
counter -= 1
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||||
store_version = 5 # Needed for dynamic plugin loading
|
store_version = 6 # Needed for dynamic plugin loading
|
||||||
|
|
||||||
__license__ = 'GPL 3'
|
__license__ = 'GPL 3'
|
||||||
__copyright__ = '2011-2014, Tomasz Długosz <tomek3d@gmail.com>'
|
__copyright__ = '2011-2015, Tomasz Długosz <tomek3d@gmail.com>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
import re
|
import re
|
||||||
@ -68,7 +68,7 @@ class LegimiStore(BasicStoreConfig, StorePlugin):
|
|||||||
counter -= 1
|
counter -= 1
|
||||||
|
|
||||||
s = SearchResult()
|
s = SearchResult()
|
||||||
s.cover_url = 'http://www.legimi.com/' + cover_url
|
s.cover_url = 'http:' + cover_url
|
||||||
s.title = title.strip()
|
s.title = title.strip()
|
||||||
s.author = author.strip()
|
s.author = author.strip()
|
||||||
s.price = price
|
s.price = price
|
||||||
|
@ -7,11 +7,12 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2015, Kovid Goyal <kovid at kovidgoyal.net>'
|
__copyright__ = '2015, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
from binascii import hexlify
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from threading import Lock
|
from threading import Lock
|
||||||
|
|
||||||
from calibre import fit_image
|
from calibre import fit_image
|
||||||
from calibre.constants import config_dir
|
from calibre.constants import config_dir, iswindows
|
||||||
from calibre.db.errors import NoSuchFormat
|
from calibre.db.errors import NoSuchFormat
|
||||||
from calibre.ebooks.metadata import authors_to_string
|
from calibre.ebooks.metadata import authors_to_string
|
||||||
from calibre.ebooks.metadata.meta import set_metadata
|
from calibre.ebooks.metadata.meta import set_metadata
|
||||||
@ -22,8 +23,9 @@ from calibre.srv.routes import endpoint, json
|
|||||||
from calibre.srv.utils import http_date
|
from calibre.srv.utils import http_date
|
||||||
from calibre.utils.config_base import tweaks
|
from calibre.utils.config_base import tweaks
|
||||||
from calibre.utils.date import timestampfromdt
|
from calibre.utils.date import timestampfromdt
|
||||||
from calibre.utils.filenames import ascii_filename
|
from calibre.utils.filenames import ascii_filename, atomic_rename
|
||||||
from calibre.utils.magick.draw import thumbnail, Image
|
from calibre.utils.magick.draw import thumbnail, Image
|
||||||
|
from calibre.utils.shared_file import share_open
|
||||||
|
|
||||||
plugboard_content_server_value = 'content_server'
|
plugboard_content_server_value = 'content_server'
|
||||||
plugboard_content_server_formats = ['epub', 'mobi', 'azw3']
|
plugboard_content_server_formats = ['epub', 'mobi', 'azw3']
|
||||||
@ -35,6 +37,7 @@ lock = Lock()
|
|||||||
# We cannot store mtimes in the filesystem since some operating systems (OS X)
|
# We cannot store mtimes in the filesystem since some operating systems (OS X)
|
||||||
# have only one second precision for mtimes
|
# have only one second precision for mtimes
|
||||||
mtimes = {}
|
mtimes = {}
|
||||||
|
rename_counter = 0
|
||||||
|
|
||||||
def create_file_copy(ctx, rd, prefix, library_id, book_id, ext, mtime, copy_func, extra_etag_data=''):
|
def create_file_copy(ctx, rd, prefix, library_id, book_id, ext, mtime, copy_func, extra_etag_data=''):
|
||||||
''' We cannot copy files directly from the library folder to the output
|
''' We cannot copy files directly from the library folder to the output
|
||||||
@ -42,36 +45,53 @@ def create_file_copy(ctx, rd, prefix, library_id, book_id, ext, mtime, copy_func
|
|||||||
instead we copy out the data from the library folder into a temp folder. We
|
instead we copy out the data from the library folder into a temp folder. We
|
||||||
make sure to only do this copy once, using the previous copy, if there have
|
make sure to only do this copy once, using the previous copy, if there have
|
||||||
been no changes to the data for the file since the last copy. '''
|
been no changes to the data for the file since the last copy. '''
|
||||||
|
global rename_counter
|
||||||
|
|
||||||
# Avoid too many items in a single directory for performance
|
# Avoid too many items in a single directory for performance
|
||||||
base = os.path.join(rd.tdir, 'fcache', (('%x' % book_id)[-3:]))
|
base = os.path.join(rd.tdir, 'fcache', (('%x' % book_id)[-3:]))
|
||||||
|
if iswindows:
|
||||||
|
base = '\\\\?\\' + os.path.abspath(base) # Ensure fname is not too long for windows' API
|
||||||
|
|
||||||
library_id = library_id.replace('\\', '_').replace('/', '_')
|
bname = '%s-%s-%x.%s' % (prefix, library_id, book_id, ext)
|
||||||
bname = '%s-%s-%s.%s' % (prefix, library_id, book_id, ext)
|
if '\\' in bname or '/' in bname:
|
||||||
|
raise ValueError('File components must not contain path separators')
|
||||||
fname = os.path.join(base, bname)
|
fname = os.path.join(base, bname)
|
||||||
|
|
||||||
# TODO: Implement locking for this cache
|
|
||||||
|
|
||||||
previous_mtime = mtimes.get(bname)
|
|
||||||
used_cache = 'no'
|
used_cache = 'no'
|
||||||
if previous_mtime is None or previous_mtime < mtime:
|
|
||||||
try:
|
with lock:
|
||||||
ans = lopen(fname, 'w+b')
|
previous_mtime = mtimes.get(bname)
|
||||||
except EnvironmentError:
|
if previous_mtime is None or previous_mtime < mtime:
|
||||||
|
if previous_mtime is not None:
|
||||||
|
# File exists and may be open, so we cannot change its
|
||||||
|
# contents, as that would lead to corrupted downloads in any
|
||||||
|
# clients that are currently downloading the file.
|
||||||
|
if iswindows:
|
||||||
|
# On windows in order to re-use bname, we have to rename it
|
||||||
|
# before deleting it
|
||||||
|
rename_counter += 1
|
||||||
|
dname = os.path.join(base, '_%x' % rename_counter)
|
||||||
|
atomic_rename(fname, dname)
|
||||||
|
os.remove(dname)
|
||||||
|
else:
|
||||||
|
os.remove(fname)
|
||||||
try:
|
try:
|
||||||
os.makedirs(base)
|
ans = share_open(fname, 'w+b')
|
||||||
except EnvironmentError:
|
except EnvironmentError:
|
||||||
pass
|
try:
|
||||||
ans = lopen(fname, 'w+b')
|
os.makedirs(base)
|
||||||
copy_func(ans)
|
except EnvironmentError:
|
||||||
ans.seek(0)
|
pass
|
||||||
mtimes[bname] = mtime
|
ans = share_open(fname, 'w+b')
|
||||||
else:
|
mtimes[bname] = mtime
|
||||||
ans = lopen(fname, 'rb')
|
copy_func(ans)
|
||||||
used_cache = 'yes'
|
ans.seek(0)
|
||||||
if ctx.testing:
|
else:
|
||||||
rd.outheaders['Used-Cache'] = used_cache
|
ans = share_open(fname, 'rb')
|
||||||
return rd.filesystem_file_with_custom_etag(ans, prefix, library_id, book_id, mtime, extra_etag_data)
|
used_cache = 'yes'
|
||||||
|
if ctx.testing:
|
||||||
|
rd.outheaders['Used-Cache'] = used_cache
|
||||||
|
rd.outheaders['Tempfile'] = hexlify(fname.encode('utf-8'))
|
||||||
|
return rd.filesystem_file_with_custom_etag(ans, prefix, library_id, book_id, mtime, extra_etag_data)
|
||||||
|
|
||||||
def cover(ctx, rd, library_id, db, book_id, width=None, height=None):
|
def cover(ctx, rd, library_id, db, book_id, width=None, height=None):
|
||||||
mtime = db.cover_last_modified(book_id)
|
mtime = db.cover_last_modified(book_id)
|
||||||
@ -141,13 +161,13 @@ def static(ctx, rd, what):
|
|||||||
path = os.path.relpath(path, base).replace(os.sep, '/')
|
path = os.path.relpath(path, base).replace(os.sep, '/')
|
||||||
path = P('content-server/' + path)
|
path = P('content-server/' + path)
|
||||||
try:
|
try:
|
||||||
return lopen(path, 'rb')
|
return share_open(path, 'rb')
|
||||||
except EnvironmentError:
|
except EnvironmentError:
|
||||||
raise HTTPNotFound()
|
raise HTTPNotFound()
|
||||||
|
|
||||||
@endpoint('/favicon.png', auth_required=False, cache_control=24)
|
@endpoint('/favicon.png', auth_required=False, cache_control=24)
|
||||||
def favicon(ctx, rd):
|
def favicon(ctx, rd):
|
||||||
return lopen(I('lt.png'), 'rb')
|
return share_open(I('lt.png'), 'rb')
|
||||||
|
|
||||||
@endpoint('/icon/{+which}', auth_required=False, cache_control=24)
|
@endpoint('/icon/{+which}', auth_required=False, cache_control=24)
|
||||||
def icon(ctx, rd, which):
|
def icon(ctx, rd, which):
|
||||||
@ -173,18 +193,18 @@ def icon(ctx, rd, which):
|
|||||||
path = P('images/' + path)
|
path = P('images/' + path)
|
||||||
if sz == 'full':
|
if sz == 'full':
|
||||||
try:
|
try:
|
||||||
return lopen(path, 'rb')
|
return share_open(path, 'rb')
|
||||||
except EnvironmentError:
|
except EnvironmentError:
|
||||||
raise HTTPNotFound()
|
raise HTTPNotFound()
|
||||||
with lock:
|
with lock:
|
||||||
tdir = os.path.join(rd.tdir, 'icons')
|
tdir = os.path.join(rd.tdir, 'icons')
|
||||||
cached = os.path.join(tdir, '%d-%s.png' % (sz, which))
|
cached = os.path.join(tdir, '%d-%s.png' % (sz, which))
|
||||||
try:
|
try:
|
||||||
return lopen(cached, 'rb')
|
return share_open(cached, 'rb')
|
||||||
except EnvironmentError:
|
except EnvironmentError:
|
||||||
pass
|
pass
|
||||||
try:
|
try:
|
||||||
src = lopen(path, 'rb')
|
src = share_open(path, 'rb')
|
||||||
except EnvironmentError:
|
except EnvironmentError:
|
||||||
raise HTTPNotFound()
|
raise HTTPNotFound()
|
||||||
with src:
|
with src:
|
||||||
@ -195,13 +215,13 @@ def icon(ctx, rd, which):
|
|||||||
if scaled:
|
if scaled:
|
||||||
img.size = (width, height)
|
img.size = (width, height)
|
||||||
try:
|
try:
|
||||||
ans = lopen(cached, 'w+b')
|
ans = share_open(cached, 'w+b')
|
||||||
except EnvironmentError:
|
except EnvironmentError:
|
||||||
try:
|
try:
|
||||||
os.mkdir(tdir)
|
os.mkdir(tdir)
|
||||||
except EnvironmentError:
|
except EnvironmentError:
|
||||||
pass
|
pass
|
||||||
ans = lopen(cached, 'w+b')
|
ans = share_open(cached, 'w+b')
|
||||||
ans.write(img.export('png'))
|
ans.write(img.export('png'))
|
||||||
ans.seek(0)
|
ans.seek(0)
|
||||||
return ans
|
return ans
|
||||||
|
Loading…
x
Reference in New Issue
Block a user