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:
Kovid Goyal 2024-02-15 09:35:09 +05:30
parent 558992008a
commit 3cbcd0acc9
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
6 changed files with 79 additions and 36 deletions

View File

@ -77,18 +77,26 @@ def get_data(name):
return None, None
def background_image():
ans = getattr(background_image, 'ans', None)
if ans is None:
@lru_cache(maxsize=4)
def background_image(encoded_fname=''):
if not encoded_fname:
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:
data = f.read()
mt, data = data.split(b'|', 1)
else:
ans = b'image/jpeg', b''
ans = background_image.ans = mt.decode('utf-8'), data
return ans
mt, data = data.split(b'|', 1)
mt = mt.decode()
return mt, data
except FileNotFoundError:
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)
@ -161,10 +169,11 @@ class UrlSchemeHandler(QWebEngineUrlSchemeHandler):
send_reply(rq, set_book_path.manifest_mime, data)
elif name == 'reader-background':
mt, data = background_image()
if data:
send_reply(rq, mt, data)
else:
rq.fail(QWebEngineUrlRequestJob.Error.UrlNotFound)
send_reply(rq, mt, data) if data else rq.fail(QWebEngineUrlRequestJob.Error.UrlNotFound)
elif name.startswith('reader-background-'):
encoded_fname = name[len('reader-background-'):]
mt, data = background_image(encoded_fname)
send_reply(rq, mt, data) if data else rq.fail(QWebEngineUrlRequestJob.Error.UrlNotFound)
elif name.startswith('mathjax/'):
handle_mathjax_request(rq, name)
elif not name:
@ -693,14 +702,16 @@ class WebView(RestartingWebEngineView):
self.execute_when_ready('show_home_page')
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:
img = files[0]
with open(img, 'rb') as src, open(os.path.join(viewer_config_dir, 'bg-image.data'), 'wb') as dest:
dest.write(as_bytes(guess_type(img)[0] or 'image/jpeg') + b'|')
shutil.copyfileobj(src, dest)
d = os.path.join(viewer_config_dir, 'background-images')
os.makedirs(d, exist_ok=True)
fname = os.path.basename(img)
shutil.copyfile(img, os.path.join(d, fname))
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):
self.execute_when_ready('goto_frac', frac)

View File

@ -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.utils.config_base import tweaks
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.localization import _
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
@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)
def get(ctx, rd, what, book_id, library_id):
book_id, rest = book_id.partition('_')[::2]

View File

@ -3,14 +3,16 @@
from __python__ import bound_methods, hash_literals
from elementmaker import E
from gettext import gettext as _
from ajax import absolute_path
from book_list.globals import get_session_data
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.prefs.utils import create_button_box
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
BLANK = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'
@ -21,19 +23,33 @@ def change_background_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):
if sd.get('background_image'):
src = READER_BACKGROUND_URL
else:
src = BLANK
url = sd.get('background_image')
src = modify_background_image_url_for_fetch(url)
img_id = unique_id('bg-image')
return E.div(
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'),
create_button(_('Change image'), action=change_background_image.bind(None, img_id)),
E.div('\xa0', style='margin: 0.5rem'),
@ -77,7 +93,8 @@ def restore_defaults():
container = document.getElementById(create_user_stylesheet_panel.container_id)
container.querySelector('[name=user-stylesheet]').value = ''
if runtime.is_standalone_viewer:
clear_image(container.querySelector('img').id)
i = container.querySelector('img')
clear_image(i.id)
else:
container.querySelector('[name=background_image]').value = ''
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)
changed = True
if runtime.is_standalone_viewer:
bg_image = container.querySelector('img.bg-image-preview').src
if bg_image is BLANK:
bg_image = container.querySelector('img.bg-image-preview').dataset.url
if bg_image is BLANK or not bg_image:
bg_image = None
else:
bg_image = container.querySelector('input[name=background_image]').value

View File

@ -33,6 +33,7 @@ from read_book.prefs.head_foot import render_head_foot
from read_book.prefs.scrolling import (
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_audio_ebook import ReadAudioEbook
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,
safe_set_inner_html, username_key
)
from viewer.constants import READER_BACKGROUND_URL
add_extra_css(def():
sel = '.book-side-margin'
@ -851,7 +851,7 @@ class View:
iframe.style.backgroundColor = ans.background or 'white'
bg_image = sd.get('background_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:
iframe.style.backgroundImage = 'none'
if sd.get('background_image_style') is 'scaled':

View File

@ -26,7 +26,7 @@ from read_book.prefs.head_foot import set_time_formatter
from read_book.view import View
from session import local_storage, session_defaults, default_interface_data
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.FAKE_HOST = FAKE_HOST
@ -267,10 +267,11 @@ def goto_frac(frac):
@from_python
def background_image_changed(img_id):
def background_image_changed(img_id, url):
img = document.getElementById(img_id)
if img:
img.src = READER_BACKGROUND_URL + '?' + Date().getTime()
img.src = f'{url}?{Date().getTime()}'
img.dataset.url = url
@from_python

View File

@ -5,4 +5,3 @@ from __python__ import bound_methods, hash_literals
FAKE_PROTOCOL = '__FAKE_PROTOCOL__'
FAKE_HOST = '__FAKE_HOST__'
READER_BACKGROUND_URL = f'{FAKE_PROTOCOL}://{FAKE_HOST}/reader-background'