mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Browser viewer: Allow showing the time left in the current chapter/book in the header and footer areas of the book.
To use go to the preferences of the browser viewer and customize the headers and footers to display the time left. Note that time left in chapter only works correctly if chapters are in separate HTML files in the book.
This commit is contained in:
parent
575422bab2
commit
18b86edc5b
@ -14,8 +14,6 @@ New features for the in-browser viewer
|
||||
- Allow loading fonts from the computer running calibre and using them
|
||||
for reading.
|
||||
|
||||
- Add a time left for chapter/book footer.
|
||||
|
||||
|
||||
New features for the server generally
|
||||
---------------------------------------
|
||||
|
@ -273,7 +273,7 @@ class IframeBoss:
|
||||
if cfi != self.last_cfi:
|
||||
self.last_cfi = cfi
|
||||
self.send_message('update_cfi', cfi=cfi, replace_history=self.replace_history_on_next_cfi_update,
|
||||
progress_frac=self.calculate_progress_frac(current_name, index))
|
||||
progress_frac=self.calculate_progress_frac(current_name, index), file_progress_frac=progress_frac())
|
||||
self.replace_history_on_next_cfi_update = True
|
||||
|
||||
def update_toc_position(self):
|
||||
|
@ -466,7 +466,8 @@ def scroll_by_page(backward, by_screen):
|
||||
pos = previous_col_location() if backward else next_col_location()
|
||||
pages = 1
|
||||
if pos is -1:
|
||||
get_boss().report_human_scroll()
|
||||
# dont report human scroll since we dont know if a full page was
|
||||
# scrolled or not
|
||||
get_boss().send_message('next_spine_item', previous=backward)
|
||||
else:
|
||||
if not backward:
|
||||
|
@ -34,6 +34,9 @@ def create_item(region, label):
|
||||
opt(_('Current section'), 'section'),
|
||||
sep(),
|
||||
opt(_('Progress'), 'progress'),
|
||||
opt(_('Time to read book'), 'time-book'),
|
||||
opt(_('Time to read chapter'), 'time-chapter'),
|
||||
opt(_('Time to read chapter and book'), 'time-chapter-book'),
|
||||
opt(_('Clock'), 'clock'),
|
||||
))
|
||||
)
|
||||
@ -103,14 +106,26 @@ def commit_head_foot(onchange, container):
|
||||
|
||||
|
||||
if window.Intl?.DateTimeFormat:
|
||||
date_formatter = window.Intl.DateTimeFormat(undefined, {'hour':'numeric', 'minute':'numeric'})
|
||||
time_formatter = window.Intl.DateTimeFormat(undefined, {'hour':'numeric', 'minute':'numeric'})
|
||||
else:
|
||||
date_formatter = {'format': def(date):
|
||||
time_formatter = {'format': def(date):
|
||||
return '{}:{}'.format(date.getHours(), date.getMinutes())
|
||||
}
|
||||
|
||||
|
||||
def render_head_foot(div, which, region, progress_frac, metadata, current_toc_node, current_toc_toplevel_node):
|
||||
def format_time_left(seconds):
|
||||
hours, minutes = divmod(int(seconds / 60), 60)
|
||||
if hours <= 0:
|
||||
if minutes < 1:
|
||||
return _('almost done')
|
||||
minutes = minutes
|
||||
return _('{} mins').format(minutes)
|
||||
if not minutes:
|
||||
return _('{} hours').format(hours)
|
||||
return _('{} h {} mins').format(hours, minutes)
|
||||
|
||||
|
||||
def render_head_foot(div, which, region, progress_frac, metadata, current_toc_node, current_toc_toplevel_node, book_time, chapter_time):
|
||||
template = get_session_data().get(which) or {}
|
||||
field = template[region] or 'empty'
|
||||
interface_data = get_interface_data()
|
||||
@ -128,7 +143,7 @@ def render_head_foot(div, which, region, progress_frac, metadata, current_toc_no
|
||||
ival = fmt_sidx(ival, use_roman=interface_data.use_roman_numerals_for_series_number)
|
||||
text = _('{0} of {1}').format(ival, metadata.series)
|
||||
elif field is 'clock':
|
||||
text = date_formatter.format(Date())
|
||||
text = time_formatter.format(Date())
|
||||
has_clock = True
|
||||
elif field is 'section':
|
||||
text = current_toc_node.title if current_toc_node else ''
|
||||
@ -136,6 +151,16 @@ def render_head_foot(div, which, region, progress_frac, metadata, current_toc_no
|
||||
text = current_toc_toplevel_node.title if current_toc_toplevel_node else ''
|
||||
if not text:
|
||||
text = current_toc_node.title if current_toc_node else ''
|
||||
elif field.startswith('time-'):
|
||||
if book_time is None or chapter_time is None:
|
||||
text = _('Calculating...')
|
||||
else:
|
||||
if field is 'time-book':
|
||||
text = format_time_left(book_time)
|
||||
elif field is 'time-chapter':
|
||||
text = format_time_left(chapter_time)
|
||||
else:
|
||||
text = '{} ({})'.format(format_time_left(chapter_time), format_time_left(book_time))
|
||||
if text is not div.textContent:
|
||||
div.textContent = text
|
||||
div.style.display = 'block' if text else 'none'
|
||||
|
@ -31,8 +31,8 @@ class Timers:
|
||||
self.average = avg
|
||||
sq = 0
|
||||
for v'var i = 0; i < rlen; i++':
|
||||
x = rates[i]
|
||||
sq += (x - avg) * (x - avg)
|
||||
x = rates[i] - avg
|
||||
sq += x * x
|
||||
self.stddev = Math.sqrt(sq / (rlen - 1))
|
||||
else:
|
||||
self.average = self.stddev = 0
|
||||
@ -42,7 +42,7 @@ class Timers:
|
||||
self.last_scroll_at = now = window.performance.now()
|
||||
if last_scroll_at is None:
|
||||
return
|
||||
time_since_last_scroll = now - last_scroll_at
|
||||
time_since_last_scroll = (now - last_scroll_at) / 1000
|
||||
if time_since_last_scroll <= 0 or time_since_last_scroll >= 300:
|
||||
return
|
||||
if is_large_scroll and time_since_last_scroll < 2:
|
||||
@ -54,3 +54,8 @@ class Timers:
|
||||
self.rates.shift()
|
||||
self.rates.push(rate)
|
||||
self.calculate()
|
||||
|
||||
def time_for(self, length):
|
||||
if length >= 0 and self.rates.length >= THRESHOLD and self.average > 0:
|
||||
return length / self.average
|
||||
return None
|
||||
|
@ -119,7 +119,7 @@ class View:
|
||||
self.ui = ui
|
||||
self.timers = Timers()
|
||||
self.loaded_resources = {}
|
||||
self.current_progress_frac = 0
|
||||
self.current_progress_frac = self.current_file_progress_frac = 0
|
||||
self.current_toc_node = self.current_toc_toplevel_node = None
|
||||
self.clock_timer_id = 0
|
||||
sd = get_session_data()
|
||||
@ -504,6 +504,7 @@ class View:
|
||||
self.ui.db.update_last_read_time(self.book)
|
||||
lrd = {'device':get_device_uuid(), 'cfi':data.cfi, 'pos_frac':data.progress_frac}
|
||||
self.current_progress_frac = data.progress_frac
|
||||
self.current_file_progress_frac = data.file_progress_frac
|
||||
self.update_header_footer()
|
||||
key = self.book.key
|
||||
if username:
|
||||
@ -517,13 +518,24 @@ class View:
|
||||
sd = get_session_data()
|
||||
has_clock = False
|
||||
|
||||
if self.book?.manifest:
|
||||
book_length = self.book.manifest.total_length or 0
|
||||
name = self.currently_showing.name
|
||||
chapter_length = self.book.manifest.files[name]?.length or 0
|
||||
else:
|
||||
book_length = chapter_length = 0
|
||||
book_length *= max(0, 1 - self.current_progress_frac)
|
||||
chapter_length *= max(0, 1 - self.current_file_progress_frac)
|
||||
book_time = self.timers.time_for(book_length)
|
||||
chapter_time = self.timers.time_for(chapter_length)
|
||||
|
||||
def render_template(div, sz_attr, name):
|
||||
nonlocal has_clock
|
||||
if sd.get(sz_attr, 20) > 5:
|
||||
mi = self.book.metadata
|
||||
texta, hca = render_head_foot(div.firstChild, name, 'left', self.current_progress_frac, mi, self.current_toc_node, self.current_toc_toplevel_node)
|
||||
textb, hcb = render_head_foot(div.firstChild.nextSibling, name, 'middle', self.current_progress_frac, mi, self.current_toc_node, self.current_toc_toplevel_node)
|
||||
textc, hcc = render_head_foot(div.lastChild, name, 'right', self.current_progress_frac, mi, self.current_toc_node, self.current_toc_toplevel_node)
|
||||
texta, hca = render_head_foot(div.firstChild, name, 'left', self.current_progress_frac, mi, self.current_toc_node, self.current_toc_toplevel_node, book_time, chapter_time)
|
||||
textb, hcb = render_head_foot(div.firstChild.nextSibling, name, 'middle', self.current_progress_frac, mi, self.current_toc_node, self.current_toc_toplevel_node, book_time, chapter_time)
|
||||
textc, hcc = render_head_foot(div.lastChild, name, 'right', self.current_progress_frac, mi, self.current_toc_node, self.current_toc_toplevel_node, book_time, chapter_time)
|
||||
has_clock = hca or hcb or hcc
|
||||
if textc and not textb and not texta:
|
||||
# Want right-aligned
|
||||
|
Loading…
x
Reference in New Issue
Block a user