mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Change how background image works in preparation for profiles
Allow multiple background images and allow settings from browser and desktop readers to work with each other
This commit is contained in:
parent
558992008a
commit
3cbcd0acc9
@ -77,18 +77,26 @@ def get_data(name):
|
|||||||
return None, None
|
return None, None
|
||||||
|
|
||||||
|
|
||||||
def background_image():
|
@lru_cache(maxsize=4)
|
||||||
ans = getattr(background_image, 'ans', None)
|
def background_image(encoded_fname=''):
|
||||||
if ans is None:
|
if not encoded_fname:
|
||||||
img_path = os.path.join(viewer_config_dir, 'bg-image.data')
|
img_path = os.path.join(viewer_config_dir, 'bg-image.data')
|
||||||
if os.path.exists(img_path):
|
try:
|
||||||
with open(img_path, 'rb') as f:
|
with open(img_path, 'rb') as f:
|
||||||
data = f.read()
|
data = f.read()
|
||||||
mt, data = data.split(b'|', 1)
|
mt, data = data.split(b'|', 1)
|
||||||
else:
|
mt = mt.decode()
|
||||||
ans = b'image/jpeg', b''
|
return mt, data
|
||||||
ans = background_image.ans = mt.decode('utf-8'), data
|
except FileNotFoundError:
|
||||||
return ans
|
return 'image/jpeg', b''
|
||||||
|
fname = bytes.fromhex(encoded_fname).decode()
|
||||||
|
img_path = os.path.join(viewer_config_dir, 'background-images', fname)
|
||||||
|
mt = guess_type(fname)[0] or 'image/jpeg'
|
||||||
|
try:
|
||||||
|
with open(img_path, 'rb') as f:
|
||||||
|
return mt, f.read()
|
||||||
|
except FileNotFoundError:
|
||||||
|
return mt, b''
|
||||||
|
|
||||||
|
|
||||||
@lru_cache(maxsize=2)
|
@lru_cache(maxsize=2)
|
||||||
@ -161,10 +169,11 @@ class UrlSchemeHandler(QWebEngineUrlSchemeHandler):
|
|||||||
send_reply(rq, set_book_path.manifest_mime, data)
|
send_reply(rq, set_book_path.manifest_mime, data)
|
||||||
elif name == 'reader-background':
|
elif name == 'reader-background':
|
||||||
mt, data = background_image()
|
mt, data = background_image()
|
||||||
if data:
|
send_reply(rq, mt, data) if data else rq.fail(QWebEngineUrlRequestJob.Error.UrlNotFound)
|
||||||
send_reply(rq, mt, data)
|
elif name.startswith('reader-background-'):
|
||||||
else:
|
encoded_fname = name[len('reader-background-'):]
|
||||||
rq.fail(QWebEngineUrlRequestJob.Error.UrlNotFound)
|
mt, data = background_image(encoded_fname)
|
||||||
|
send_reply(rq, mt, data) if data else rq.fail(QWebEngineUrlRequestJob.Error.UrlNotFound)
|
||||||
elif name.startswith('mathjax/'):
|
elif name.startswith('mathjax/'):
|
||||||
handle_mathjax_request(rq, name)
|
handle_mathjax_request(rq, name)
|
||||||
elif not name:
|
elif not name:
|
||||||
@ -693,14 +702,16 @@ class WebView(RestartingWebEngineView):
|
|||||||
self.execute_when_ready('show_home_page')
|
self.execute_when_ready('show_home_page')
|
||||||
|
|
||||||
def change_background_image(self, img_id):
|
def change_background_image(self, img_id):
|
||||||
files = choose_images(self, 'viewer-background-image', _('Choose background image'), formats=['png', 'gif', 'jpg', 'jpeg'])
|
files = choose_images(self, 'viewer-background-image', _('Choose background image'), formats=['png', 'gif', 'jpg', 'jpeg', 'webp'])
|
||||||
if files:
|
if files:
|
||||||
img = files[0]
|
img = files[0]
|
||||||
with open(img, 'rb') as src, open(os.path.join(viewer_config_dir, 'bg-image.data'), 'wb') as dest:
|
d = os.path.join(viewer_config_dir, 'background-images')
|
||||||
dest.write(as_bytes(guess_type(img)[0] or 'image/jpeg') + b'|')
|
os.makedirs(d, exist_ok=True)
|
||||||
shutil.copyfileobj(src, dest)
|
fname = os.path.basename(img)
|
||||||
|
shutil.copyfile(img, os.path.join(d, fname))
|
||||||
background_image.ans = None
|
background_image.ans = None
|
||||||
self.execute_when_ready('background_image_changed', img_id)
|
encoded = fname.encode().hex()
|
||||||
|
self.execute_when_ready('background_image_changed', img_id, f'{FAKE_PROTOCOL}://{FAKE_HOST}/reader-background-{encoded}')
|
||||||
|
|
||||||
def goto_frac(self, frac):
|
def goto_frac(self, frac):
|
||||||
self.execute_when_ready('goto_frac', frac)
|
self.execute_when_ready('goto_frac', frac)
|
||||||
|
@ -30,7 +30,9 @@ from calibre.srv.routes import endpoint, json
|
|||||||
from calibre.srv.utils import get_db, get_use_roman, http_date
|
from calibre.srv.utils import get_db, get_use_roman, 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, atomic_rename
|
from calibre.utils.filenames import (
|
||||||
|
ascii_filename, atomic_rename, make_long_path_useable,
|
||||||
|
)
|
||||||
from calibre.utils.img import image_from_data, scale_image
|
from calibre.utils.img import image_from_data, scale_image
|
||||||
from calibre.utils.localization import _
|
from calibre.utils.localization import _
|
||||||
from calibre.utils.resources import get_image_path as I, get_path as P
|
from calibre.utils.resources import get_image_path as I, get_path as P
|
||||||
@ -303,6 +305,19 @@ def icon(ctx, rd, which):
|
|||||||
return ans
|
return ans
|
||||||
|
|
||||||
|
|
||||||
|
@endpoint('/reader-background/{encoded_fname}', android_workaround=True)
|
||||||
|
def reader_background(ctx, rd, encoded_fname):
|
||||||
|
base = os.path.abspath(os.path.normapth(os.path.join(config_dir, 'viewer', 'background-images')))
|
||||||
|
fname = bytes.fromhex(encoded_fname)
|
||||||
|
q = os.path.abspath(os.path.normpath(os.path.join(base, fname)))
|
||||||
|
if not q.startswith(base):
|
||||||
|
raise HTTPNotFound(f'Reader background {encoded_fname} not found')
|
||||||
|
try:
|
||||||
|
return share_open(make_long_path_useable(q), 'rb')
|
||||||
|
except FileNotFoundError:
|
||||||
|
raise HTTPNotFound(f'Reader background {encoded_fname} not found')
|
||||||
|
|
||||||
|
|
||||||
@endpoint('/get/{what}/{book_id}/{library_id=None}', android_workaround=True)
|
@endpoint('/get/{what}/{book_id}/{library_id=None}', android_workaround=True)
|
||||||
def get(ctx, rd, what, book_id, library_id):
|
def get(ctx, rd, what, book_id, library_id):
|
||||||
book_id, rest = book_id.partition('_')[::2]
|
book_id, rest = book_id.partition('_')[::2]
|
||||||
|
@ -3,14 +3,16 @@
|
|||||||
from __python__ import bound_methods, hash_literals
|
from __python__ import bound_methods, hash_literals
|
||||||
|
|
||||||
from elementmaker import E
|
from elementmaker import E
|
||||||
from gettext import gettext as _
|
|
||||||
|
|
||||||
|
from ajax import absolute_path
|
||||||
from book_list.globals import get_session_data
|
from book_list.globals import get_session_data
|
||||||
from dom import ensure_id, unique_id
|
from dom import ensure_id, unique_id
|
||||||
|
from encodings import hexlify
|
||||||
|
from gettext import gettext as _
|
||||||
from read_book.globals import runtime, ui_operations
|
from read_book.globals import runtime, ui_operations
|
||||||
from read_book.prefs.utils import create_button_box
|
from read_book.prefs.utils import create_button_box
|
||||||
from session import session_defaults
|
from session import session_defaults
|
||||||
from viewer.constants import READER_BACKGROUND_URL
|
from viewer.constants import FAKE_HOST, FAKE_PROTOCOL
|
||||||
from widgets import create_button
|
from widgets import create_button
|
||||||
|
|
||||||
BLANK = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'
|
BLANK = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'
|
||||||
@ -21,19 +23,33 @@ def change_background_image(img_id):
|
|||||||
|
|
||||||
|
|
||||||
def clear_image(img_id):
|
def clear_image(img_id):
|
||||||
document.getElementById(img_id).src = BLANK
|
i = document.getElementById(img_id)
|
||||||
|
i.src = BLANK
|
||||||
|
i.dataset.url = ''
|
||||||
|
|
||||||
|
|
||||||
|
def modify_background_image_url_for_fetch(url):
|
||||||
|
if not url:
|
||||||
|
return BLANK
|
||||||
|
if runtime.is_standalone_viewer:
|
||||||
|
if url.startswith(f'{FAKE_PROTOCOL}:'):
|
||||||
|
return url
|
||||||
|
encoded = hexlify(url)
|
||||||
|
return f'{FAKE_PROTOCOL}://{FAKE_HOST}/reader-background-{encoded}'
|
||||||
|
if url.startswith(f'{FAKE_PROTOCOL}:'):
|
||||||
|
x = str.split(url, '/')[-1].partition('?')[0].partition('-')[2]
|
||||||
|
return absolute_path(f'reader-background/{x}')
|
||||||
|
return url
|
||||||
|
|
||||||
|
|
||||||
def standalone_background_widget(sd):
|
def standalone_background_widget(sd):
|
||||||
if sd.get('background_image'):
|
url = sd.get('background_image')
|
||||||
src = READER_BACKGROUND_URL
|
src = modify_background_image_url_for_fetch(url)
|
||||||
else:
|
|
||||||
src = BLANK
|
|
||||||
img_id = unique_id('bg-image')
|
img_id = unique_id('bg-image')
|
||||||
|
|
||||||
return E.div(
|
return E.div(
|
||||||
style='display: flex; align-items: center',
|
style='display: flex; align-items: center',
|
||||||
E.div(E.img(src=src, id=img_id, class_='bg-image-preview', style='width: 75px; height: 75px; border: solid 1px')),
|
E.div(E.img(src=src, data_url=url, id=img_id, class_='bg-image-preview', style='width: 75px; height: 75px; border: solid 1px')),
|
||||||
E.div('\xa0', style='margin: 0.5rem'),
|
E.div('\xa0', style='margin: 0.5rem'),
|
||||||
create_button(_('Change image'), action=change_background_image.bind(None, img_id)),
|
create_button(_('Change image'), action=change_background_image.bind(None, img_id)),
|
||||||
E.div('\xa0', style='margin: 0.5rem'),
|
E.div('\xa0', style='margin: 0.5rem'),
|
||||||
@ -77,7 +93,8 @@ def restore_defaults():
|
|||||||
container = document.getElementById(create_user_stylesheet_panel.container_id)
|
container = document.getElementById(create_user_stylesheet_panel.container_id)
|
||||||
container.querySelector('[name=user-stylesheet]').value = ''
|
container.querySelector('[name=user-stylesheet]').value = ''
|
||||||
if runtime.is_standalone_viewer:
|
if runtime.is_standalone_viewer:
|
||||||
clear_image(container.querySelector('img').id)
|
i = container.querySelector('img')
|
||||||
|
clear_image(i.id)
|
||||||
else:
|
else:
|
||||||
container.querySelector('[name=background_image]').value = ''
|
container.querySelector('[name=background_image]').value = ''
|
||||||
container.querySelector('select[name=background_image_style]').value = session_defaults().background_image_style
|
container.querySelector('select[name=background_image_style]').value = session_defaults().background_image_style
|
||||||
@ -132,8 +149,8 @@ def commit_user_stylesheet(onchange, container):
|
|||||||
sd.set('user_stylesheet', val)
|
sd.set('user_stylesheet', val)
|
||||||
changed = True
|
changed = True
|
||||||
if runtime.is_standalone_viewer:
|
if runtime.is_standalone_viewer:
|
||||||
bg_image = container.querySelector('img.bg-image-preview').src
|
bg_image = container.querySelector('img.bg-image-preview').dataset.url
|
||||||
if bg_image is BLANK:
|
if bg_image is BLANK or not bg_image:
|
||||||
bg_image = None
|
bg_image = None
|
||||||
else:
|
else:
|
||||||
bg_image = container.querySelector('input[name=background_image]').value
|
bg_image = container.querySelector('input[name=background_image]').value
|
||||||
|
@ -33,6 +33,7 @@ from read_book.prefs.head_foot import render_head_foot
|
|||||||
from read_book.prefs.scrolling import (
|
from read_book.prefs.scrolling import (
|
||||||
MIN_SCROLL_SPEED_AUTO as SCROLL_SPEED_STEP, change_scroll_speed
|
MIN_SCROLL_SPEED_AUTO as SCROLL_SPEED_STEP, change_scroll_speed
|
||||||
)
|
)
|
||||||
|
from read_book.prefs.user_stylesheet import modify_background_image_url_for_fetch
|
||||||
from read_book.read_aloud import ReadAloud
|
from read_book.read_aloud import ReadAloud
|
||||||
from read_book.read_audio_ebook import ReadAudioEbook
|
from read_book.read_audio_ebook import ReadAudioEbook
|
||||||
from read_book.resources import load_resources
|
from read_book.resources import load_resources
|
||||||
@ -49,7 +50,6 @@ from utils import (
|
|||||||
default_context_menu_should_be_allowed, html_escape, is_ios, parse_url_params,
|
default_context_menu_should_be_allowed, html_escape, is_ios, parse_url_params,
|
||||||
safe_set_inner_html, username_key
|
safe_set_inner_html, username_key
|
||||||
)
|
)
|
||||||
from viewer.constants import READER_BACKGROUND_URL
|
|
||||||
|
|
||||||
add_extra_css(def():
|
add_extra_css(def():
|
||||||
sel = '.book-side-margin'
|
sel = '.book-side-margin'
|
||||||
@ -851,7 +851,7 @@ class View:
|
|||||||
iframe.style.backgroundColor = ans.background or 'white'
|
iframe.style.backgroundColor = ans.background or 'white'
|
||||||
bg_image = sd.get('background_image')
|
bg_image = sd.get('background_image')
|
||||||
if bg_image:
|
if bg_image:
|
||||||
iframe.style.backgroundImage = f'url({READER_BACKGROUND_URL}?{Date().getTime()})' if runtime.is_standalone_viewer else f'url({bg_image})'
|
iframe.style.backgroundImage = f'url({modify_background_image_url_for_fetch(bg_image)})'
|
||||||
else:
|
else:
|
||||||
iframe.style.backgroundImage = 'none'
|
iframe.style.backgroundImage = 'none'
|
||||||
if sd.get('background_image_style') is 'scaled':
|
if sd.get('background_image_style') is 'scaled':
|
||||||
|
@ -26,7 +26,7 @@ from read_book.prefs.head_foot import set_time_formatter
|
|||||||
from read_book.view import View
|
from read_book.view import View
|
||||||
from session import local_storage, session_defaults, default_interface_data
|
from session import local_storage, session_defaults, default_interface_data
|
||||||
from utils import debounce, encode_query_with_path, parse_url_params
|
from utils import debounce, encode_query_with_path, parse_url_params
|
||||||
from viewer.constants import FAKE_HOST, FAKE_PROTOCOL, READER_BACKGROUND_URL
|
from viewer.constants import FAKE_HOST, FAKE_PROTOCOL
|
||||||
|
|
||||||
runtime.is_standalone_viewer = True
|
runtime.is_standalone_viewer = True
|
||||||
runtime.FAKE_HOST = FAKE_HOST
|
runtime.FAKE_HOST = FAKE_HOST
|
||||||
@ -267,10 +267,11 @@ def goto_frac(frac):
|
|||||||
|
|
||||||
|
|
||||||
@from_python
|
@from_python
|
||||||
def background_image_changed(img_id):
|
def background_image_changed(img_id, url):
|
||||||
img = document.getElementById(img_id)
|
img = document.getElementById(img_id)
|
||||||
if img:
|
if img:
|
||||||
img.src = READER_BACKGROUND_URL + '?' + Date().getTime()
|
img.src = f'{url}?{Date().getTime()}'
|
||||||
|
img.dataset.url = url
|
||||||
|
|
||||||
|
|
||||||
@from_python
|
@from_python
|
||||||
|
@ -5,4 +5,3 @@ from __python__ import bound_methods, hash_literals
|
|||||||
|
|
||||||
FAKE_PROTOCOL = '__FAKE_PROTOCOL__'
|
FAKE_PROTOCOL = '__FAKE_PROTOCOL__'
|
||||||
FAKE_HOST = '__FAKE_HOST__'
|
FAKE_HOST = '__FAKE_HOST__'
|
||||||
READER_BACKGROUND_URL = f'{FAKE_PROTOCOL}://{FAKE_HOST}/reader-background'
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user