From ababbd7aadc59a9cf773137186cdfc0b0778d995 Mon Sep 17 00:00:00 2001 From: NiLuJe Date: Thu, 6 Sep 2018 01:35:48 +0200 Subject: [PATCH] Enhanced visual feedback around tar & zsync! (#4194) * Move Kindle scripts to FBInk * Proper progress bars around tar invocations * Spinner during zsync processing --- Makefile | 6 +- base | 2 +- frontend/ui/otamanager.lua | 62 +++++++++++++----- platform/common/spinning_zsync | 42 ++++++++++++ platform/kindle/koreader.sh | 29 ++++----- platform/kindle/kotar_cpoint | 44 ------------- platform/kindle/libkohelper.sh | 112 +++----------------------------- platform/kindle/zsync_status.sh | 27 -------- platform/kobo/koreader.sh | 9 ++- platform/kobo/kotar_cpoint | 28 -------- 10 files changed, 121 insertions(+), 240 deletions(-) create mode 100755 platform/common/spinning_zsync delete mode 100755 platform/kindle/kotar_cpoint delete mode 100755 platform/kindle/zsync_status.sh delete mode 100755 platform/kobo/kotar_cpoint diff --git a/Makefile b/Makefile index 5629edfaf..37c822105 100644 --- a/Makefile +++ b/Makefile @@ -31,6 +31,7 @@ INSTALL_DIR=koreader-$(DIST)-$(MACHINE) # platform directories PLATFORM_DIR=platform +COMMON_DIR=$(PLATFORM_DIR)/common KINDLE_DIR=$(PLATFORM_DIR)/kindle KOBO_DIR=$(PLATFORM_DIR)/kobo POCKETBOOK_DIR=$(PLATFORM_DIR)/pocketbook @@ -165,8 +166,7 @@ kindleupdate: all ln -sf ../$(KINDLE_DIR)/launchpad $(INSTALL_DIR)/ ln -sf ../../$(KINDLE_DIR)/koreader.sh $(INSTALL_DIR)/koreader ln -sf ../../$(KINDLE_DIR)/libkohelper.sh $(INSTALL_DIR)/koreader - ln -sf ../../$(KINDLE_DIR)/kotar_cpoint $(INSTALL_DIR)/koreader - ln -sf ../../$(KINDLE_DIR)/zsync_status.sh $(INSTALL_DIR)/koreader + ln -sf ../../$(COMMON_DIR)/spinning_zsync $(INSTALL_DIR)/koreader # create new package cd $(INSTALL_DIR) && pwd && \ zip -9 -r \ @@ -198,7 +198,7 @@ koboupdate: all cp $(KOBO_DIR)/koreader.png $(INSTALL_DIR)/koreader.png cp $(KOBO_DIR)/fmon/README.txt $(INSTALL_DIR)/README_kobo.txt cp $(KOBO_DIR)/*.sh $(INSTALL_DIR)/koreader - cp $(KOBO_DIR)/kotar_cpoint $(INSTALL_DIR)/koreader + cp $(COMMON_DIR)/spinning_zsync $(INSTALL_DIR)/koreader # create new package cd $(INSTALL_DIR) && \ zip -9 -r \ diff --git a/base b/base index 22d94e931..f43689452 160000 --- a/base +++ b/base @@ -1 +1 @@ -Subproject commit 22d94e931c9f272eca96fbaab389afca6aaff3dc +Subproject commit f436894521f53a1440f40c35315da14d4b2a2894 diff --git a/frontend/ui/otamanager.lua b/frontend/ui/otamanager.lua index 144c0d828..17f69f047 100644 --- a/frontend/ui/otamanager.lua +++ b/frontend/ui/otamanager.lua @@ -36,6 +36,7 @@ local OTAManager = { installed_package = ota_dir .. "koreader.installed.tar", package_indexfile = "ota/package.index", updated_package = ota_dir .. "koreader.updated.tar", + can_pretty_print = lfs.attributes("./fbink", "mode") == "file" and true or false, } local ota_channels = { @@ -156,12 +157,14 @@ function OTAManager:fetchAndProcessUpdate() text = _("KOReader will be updated on next restart."), }) -- Make it clear that zsync is done - if Device:isKindle() then - os.execute("./zsync_status.sh clear") - elseif Device:isKobo() then - os.execute("./fbink -q -y -7 -pm ' ' ' '") + if self.can_pretty_print then + os.execute("./fbink -q -y -7 -pm ' ' ' '") end else + -- Make it clear that zsync is done + if self.can_pretty_print then + os.execute("./fbink -q -y -7 -pm ' ' ' '") + end UIManager:show(ConfirmBox:new{ text = _("Error updating KOReader. Would you like to delete temporary files?"), ok_callback = function() @@ -191,10 +194,33 @@ function OTAManager:_buildLocalPackage() self.installed_package, self.package_indexfile)) else -- With visual feedback if supported... - if lfs.attributes("./kotar_cpoint", "mode") == "file" then + if self.can_pretty_print then + os.execute("./fbink -q -y -7 -pmh 'Preparing local OTA package'") + -- We need a vague idea of how much space the tarball we're creating will take to compute a proper percentage... + -- Get the size from the latest zsync package, which'll be a closer match than anything else we might come up with. + local zsync_file = self:getZsyncFilename() + local local_zsync_file = ota_dir .. zsync_file + local tarball_size = nil + local zsync = io.open(local_zsync_file, "r") + if zsync then + for line in zsync:lines() do + tarball_size = line:match("^Length: (%d*)$") + if tarball_size then break end + end + zsync:close() + end + -- Next, we need to compute the amount of tar blocks that'll take, knowing that tar's default blocksize is 20 * 512 bytes. + -- c.f., https://superuser.com/questions/168749 & http://www.noah.org/wiki/tar_notes + -- Defaults to a sane-ish value as-of now, in case shit happens... + local blocks = 6405 + if tarball_size then + blocks = tarball_size / (512 * 20) + end + -- And since we want a percentage, devise the exact value we need for tar to spit out exactly 100 checkpoints ;). + local cpoints = blocks / 100 return os.execute(string.format( - "./tar --no-recursion -cf %s -C .. -T %s --checkpoint=200 --checkpoint-action=exec='./kotar_cpoint $TAR_CHECKPOINT create'", - self.installed_package, self.package_indexfile)) + "./tar --no-recursion -cf %s -C .. -T %s --checkpoint=%d --checkpoint-action=exec='./fbink -q -y -6 -P $(($TAR_CHECKPOINT/%d))'", + self.installed_package, self.package_indexfile, cpoints, cpoints)) else return os.execute(string.format( "./tar --no-recursion -cf %s -C .. -T %s", @@ -205,19 +231,19 @@ end function OTAManager:zsync() if self:_buildLocalPackage() == 0 then - -- Make it clear that it's now zsync churning CPU time, instead of tar churning IO ;). - if Device:isKindle() then - os.execute("./zsync_status.sh") - elseif Device:isKobo() then - os.execute("./fbink -q -y -7 -pmh 'Computing zsync delta . . .'") + local zsync_wrapper = "zsync" + -- With visual feedback if supported... + if self.can_pretty_print then + zsync_wrapper = "spinning_zsync" end return os.execute( - ("./zsync -i %s -o %s -u %s %s%s"):format( - self.installed_package, - self.updated_package, - self:getOTAServer(), - ota_dir, - self:getZsyncFilename()) + ("./%s -i '%s' -o '%s' -u '%s' '%s%s'"):format( + zsync_wrapper, + self.installed_package, + self.updated_package, + self:getOTAServer(), + ota_dir, + self:getZsyncFilename()) ) end end diff --git a/platform/common/spinning_zsync b/platform/common/spinning_zsync new file mode 100755 index 000000000..0454ae998 --- /dev/null +++ b/platform/common/spinning_zsync @@ -0,0 +1,42 @@ +#!/bin/sh + +# Small zsync wrapper so we can get a pretty spinner while it works... +./fbink -q -y -7 -pmh 'Computing zsync delta !' +# Clear any potential leftover from the local OTA tarball creation. +./fbink -q -y -6 -pm ' ' + +# Spin in the background while we work ;). +( + # See https://stackoverflow.com/questions/2685435 for inspiration + # as well as https://www.npmjs.com/package/cli-spinners + # & https://github.com/swelljoe/spinner + # http://www.fileformat.info/info/unicode/block/block_elements/list.htm + ## + # Simple bars, they look better when a bit smoother, with a snappier interval (~250ms) + #SPINNER="� ▁ ▂ ▃ ▄ ▅ ▆ ▇ █ ▇ ▆ ▅ ▄ ▃ ▂ ▁" + #SPINNER="� ▏ ▎ ▍ ▌ ▋ ▊ ▉ █ ▉ ▊ ▋ ▌ ▍ ▎ ▏" + # Spinning blocks + SPINNER="▖ ▘ ▝ ▗" + #SPINNER="▜ ▟ ▙ ▛" + # Snaking blocks + #SPINNER="▌ ▀ ▐ ▄" + #SPINNER="▌ ▛ ▀ ▜ ▐ ▟ ▄ ▙" + while :; do + for spin in ${SPINNER}; do + usleep 500000 + # NOTE: Throw stderr to the void because I'm cheating w/ U+FFFD for a blank character, + # which FBInk replaces by a blank, but not before shouting at us on stderr ;). + ./fbink -q -y -7 -pmh "Computing zsync delta ${spin}" 2>/dev/null + done + done +) & + +# Launch zsync, and remember its exit code... +./zsync "$@" +rc=$? + +# Kill the spinner subshell now that we're done +kill -15 $! + +# And return with zsync's exit code, not kill's ;). +exit ${rc} diff --git a/platform/kindle/koreader.sh b/platform/kindle/koreader.sh index 3341a1c7c..06db68b5b 100755 --- a/platform/kindle/koreader.sh +++ b/platform/kindle/koreader.sh @@ -7,7 +7,7 @@ PROC_FIVEWAY="/proc/fiveway" [ -e "${PROC_FIVEWAY}" ] && echo unlock >"${PROC_FIVEWAY}" # KOReader's working directory -KOREADER_DIR="/mnt/us/koreader" +export KOREADER_DIR="/mnt/us/koreader" # Load our helper functions... if [ -f "${KOREADER_DIR}/libkohelper.sh" ]; then @@ -99,21 +99,17 @@ ko_update_check() { NEWUPDATE="${KOREADER_DIR}/ota/koreader.updated.tar" INSTALLED="${KOREADER_DIR}/ota/koreader.installed.tar" if [ -f "${NEWUPDATE}" ]; then - logmsg "Updating koreader . . ." - # Look for our own GNU tar build to do a fancy progress tracking... - GNUTAR_BIN="${KOREADER_DIR}/tar" - if [ -x "${GNUTAR_BIN}" ]; then - # Let our checkpoint script handle the detailed visual feedback... - eips_print_bottom_centered "Updating KOReader" 3 - # shellcheck disable=SC2016 - ${GNUTAR_BIN} -C "/mnt/us" --no-same-owner --no-same-permissions --checkpoint=200 --checkpoint-action=exec='./kotar_cpoint $TAR_CHECKPOINT' -xf "${NEWUPDATE}" - fail=$? - else - # Fall back to busybox tar - eips_print_bottom_centered "Updating KOReader . . ." 3 - tar -C "/mnt/us" -xf "${NEWUPDATE}" - fail=$? - fi + logmsg "Updating KOReader . . ." + # Let our checkpoint script handle the detailed visual feedback... + eips_print_bottom_centered "Updating KOReader" 3 + # NOTE: See frontend/ui/otamanager.lua for a few more details on how we squeeze a percentage out of tar's checkpoint feature + # NOTE: %B should always be 512 in our case, so let stat do part of the maths for us instead of using %s ;). + FILESIZE="$(stat -c %b "${NEWUPDATE}")" + BLOCKS="$((FILESIZE / 20))" + export CPOINTS="$((BLOCKS / 100))" + # shellcheck disable=SC2016 + ${KOREADER_DIR}/tar -C "/mnt/us" --no-same-owner --no-same-permissions --checkpoint="${CPOINTS}" --checkpoint-action=exec='$KOREADER_DIR/fbink -q -y -6 -P $(($TAR_CHECKPOINT/$CPOINTS))' -xf "${NEWUPDATE}" + fail=$? # Cleanup behind us... if [ "${fail}" -eq 0 ]; then mv "${NEWUPDATE}" "${INSTALLED}" @@ -127,6 +123,7 @@ ko_update_check() { eips_print_bottom_centered "KOReader may fail to function properly" 1 fi rm -f "${NEWUPDATE}" # always purge newupdate in all cases to prevent update loop + unset BLOCKS CPOINTS fi } # NOTE: Keep doing an initial update check, in addition to one during the restart loop, so we can pickup potential updates of this very script... diff --git a/platform/kindle/kotar_cpoint b/platform/kindle/kotar_cpoint deleted file mode 100755 index f476ec521..000000000 --- a/platform/kindle/kotar_cpoint +++ /dev/null @@ -1,44 +0,0 @@ -#!/bin/sh - -# KOReader's working directory -KOREADER_DIR="/mnt/us/koreader" - -# We do NOT want to sleep during eips calls! -export EIPS_NO_SLEEP="true" - -# Load our helper functions... -if [ -f "${KOREADER_DIR}/libkohelper.sh" ]; then - # shellcheck source=/dev/null - . "${KOREADER_DIR}/libkohelper.sh" -else - echo "Can't source helper functions, aborting!" - exit 1 -fi - -## First arg is the checkpoint number, and we get one every 200 checkpoints. -CHECKPOINT_NUM="${1}" -CHECKPOINT_GRANULARITY="200" - -# Use that to build a progress bar -PROGRESS_AMOUNT="$((CHECKPOINT_NUM / CHECKPOINT_GRANULARITY))" - -# Which text do we want to use? -case "${2}" in - "create") - PROGRESS_BASE_STRING="Preparing local OTA package" - ;; - *) - PROGRESS_BASE_STRING="Updating KOReader" - ;; -esac - -# And start drawing the progress bar... -PROGRESS_STRING="" -for _ in $(seq 1 ${PROGRESS_AMOUNT}); do - # Fill the progress bar... - PROGRESS_STRING="${PROGRESS_STRING}." -done - -# Print our progress :) -eips_print_bottom_centered "${PROGRESS_BASE_STRING}" 3 -eips_print_bottom_centered "${PROGRESS_STRING}" 2 diff --git a/platform/kindle/libkohelper.sh b/platform/kindle/libkohelper.sh index 9819fd02b..7a606b7af 100644 --- a/platform/kindle/libkohelper.sh +++ b/platform/kindle/libkohelper.sh @@ -14,91 +14,10 @@ else [ -f /etc/rc.d/functions ] && . /etc/rc.d/functions fi -# We need to get the proper constants for our model... -kmodel="$(cut -c3-4 /proc/usid)" -case "${kmodel}" in - "13" | "54" | "2A" | "4F" | "52" | "53") - # Voyage - SCREEN_X_RES=1088 # NOTE: Yes, 1088, not 1072 or 1080... - SCREEN_Y_RES=1448 - EIPS_X_RES=16 - EIPS_Y_RES=24 # Manually mesured, should be accurate. - ;; - "24" | "1B" | "1D" | "1F" | "1C" | "20" | "D4" | "5A" | "D5" | "D6" | "D7" | "D8" | "F2" | "17" | "60" | "F4" | "F9" | "62" | "61" | "5F") - # PaperWhite & PaperWhite 2 - SCREEN_X_RES=768 # NOTE: Yes, 768, not 758... - SCREEN_Y_RES=1024 - EIPS_X_RES=16 - EIPS_Y_RES=24 # Manually mesured, should be accurate. - ;; - "C6" | "DD") - # KT2 - SCREEN_X_RES=608 - SCREEN_Y_RES=800 - EIPS_X_RES=16 - EIPS_Y_RES=24 - ;; - "0F" | "11" | "10" | "12") - # Touch - SCREEN_X_RES=600 # _v_width @ upstart/functions - SCREEN_Y_RES=800 # _v_height @ upstart/functions - EIPS_X_RES=12 # from f_puts @ upstart/functions - EIPS_Y_RES=20 # from f_puts @ upstart/functions - ;; - *) - # Handle legacy devices... - if [ -f "/etc/rc.d/functions" ] && grep "EIPS" "/etc/rc.d/functions" >/dev/null 2>&1; then - # Already done... - #. /etc/rc.d/functions - echo "foo" >/dev/null - else - # Try the new device ID scheme... - kmodel="$(cut -c4-6 /proc/usid)" - case "${kmodel}" in - "0G1" | "0G2" | "0G4" | "0G5" | "0G6" | "0G7" | "0KB" | "0KC" | "0KD" | "0KE" | "0KF" | "0KG" | "0LK" | "0LL") - # PW3 - SCREEN_X_RES=1088 - SCREEN_Y_RES=1448 - EIPS_X_RES=16 - EIPS_Y_RES=24 - ;; - "0GC" | "0GD" | "0GR" | "0GS" | "0GT" | "0GU") - # Oasis - SCREEN_X_RES=1088 - SCREEN_Y_RES=1448 - EIPS_X_RES=16 - EIPS_Y_RES=24 - ;; - "0LM" | "0LN" | "0LP" | "0LQ" | "0P1" | "0P2" | "0P6" | "0P7" | "0P8" | "0S1" | "0S2" | "0S3" | "0S4" | "0S7" | "0SA") - # Oasis 2 - SCREEN_X_RES=1280 # NOTE: Yep, line_length/xres_virtual, not xres (1264) - SCREEN_Y_RES=1680 - EIPS_X_RES=16 # TBD 19? - EIPS_Y_RES=24 # TBD 28? 25?! - ;; - "0DU" | "0K9" | "0KA") - # KT3 - SCREEN_X_RES=608 - SCREEN_Y_RES=800 - EIPS_X_RES=16 - EIPS_Y_RES=24 - ;; - *) - # Fallback... We shouldn't ever hit that. - SCREEN_X_RES=600 - SCREEN_Y_RES=800 - EIPS_X_RES=12 - EIPS_Y_RES=20 - ;; - esac - fi - ;; -esac -# And now we can do the maths ;) -EIPS_MAXCHARS="$((SCREEN_X_RES / EIPS_X_RES))" -EIPS_MAXLINES="$((SCREEN_Y_RES / EIPS_Y_RES))" - # Adapted from libkh[5] +FBINK_BIN="/mnt/us/koreader/fbink" + +# NOTE: Yeah, the name becomes a bit of a lie now that we're exclusively using FBInk ;p. eips_print_bottom_centered() { # We need at least two args if [ $# -lt 2 ]; then @@ -109,22 +28,10 @@ eips_print_bottom_centered() { kh_eips_string="${1}" kh_eips_y_shift_up="${2}" - # Get the real string length now - kh_eips_strlen="${#kh_eips_string}" - - # Add the right amount of left & right padding, since we're centered, and eips doesn't trigger a full refresh, - # so we'll have to padd our string with blank spaces to make sure two consecutive messages don't run into each other - kh_padlen="$(((EIPS_MAXCHARS - kh_eips_strlen) / 2))" - - # Left padding... - while [ ${#kh_eips_string} -lt $((kh_eips_strlen + kh_padlen)) ]; do - kh_eips_string=" ${kh_eips_string}" - done - - # Right padding (crop to the edge of the screen) - while [ ${#kh_eips_string} -lt ${EIPS_MAXCHARS} ]; do - kh_eips_string="${kh_eips_string} " - done + # Unlike eips, we need at least a single space to even try to print something ;). + if [ "${kh_eips_string}" = "" ]; then + kh_eips_string=" " + fi # Sleep a tiny bit to workaround the logic in the 'new' (K4+) eInk controllers that tries to bundle updates, # otherwise it may drop part of our messages because of other screen updates from KUAL... @@ -133,6 +40,7 @@ eips_print_bottom_centered() { usleep 150000 # 150ms fi - # And finally, show our formatted message centered on the bottom of the screen (NOTE: Redirect to /dev/null to kill unavailable character & pixel not in range warning messages) - eips 0 $((EIPS_MAXLINES - 2 - kh_eips_y_shift_up)) "${kh_eips_string}" >/dev/null + # NOTE: FBInk will handle the padding. FBInk's default font is square, not tall like eips, + # so we compensate by tweaking the baseline ;). This matches the baseline we use on Kobo, too. + ${FBINK_BIN} -qpm -y $((-4 - kh_eips_y_shift_up)) "${kh_eips_string}" } diff --git a/platform/kindle/zsync_status.sh b/platform/kindle/zsync_status.sh deleted file mode 100755 index 9a3c601b6..000000000 --- a/platform/kindle/zsync_status.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/sh - -# KOReader's working directory -KOREADER_DIR="/mnt/us/koreader" - -# We do NOT want to sleep during eips calls! -export EIPS_NO_SLEEP="true" - -# Load our helper functions... -if [ -f "${KOREADER_DIR}/libkohelper.sh" ]; then - # shellcheck source=/dev/null - . "${KOREADER_DIR}/libkohelper.sh" -else - echo "Can't source helper functions, aborting!" - exit 1 -fi - -# What are we printing? -case "${1}" in - "clear") - eips_print_bottom_centered " " 3 - eips_print_bottom_centered " " 2 - ;; - *) - eips_print_bottom_centered "Computing zsync delta . . ." 3 - ;; -esac diff --git a/platform/kobo/koreader.sh b/platform/kobo/koreader.sh index 784793782..96dc01417 100755 --- a/platform/kobo/koreader.sh +++ b/platform/kobo/koreader.sh @@ -28,8 +28,14 @@ ko_update_check() { NEWUPDATE="${KOREADER_DIR}/ota/koreader.updated.tar" INSTALLED="${KOREADER_DIR}/ota/koreader.installed.tar" if [ -f "${NEWUPDATE}" ]; then + ./fbink -q -y -7 -pmh "Updating KOReader" + # NOTE: See frontend/ui/otamanager.lua for a few more details on how we squeeze a percentage out of tar's checkpoint feature + # NOTE: %B should always be 512 in our case, so let stat do part of the maths for us instead of using %s ;). + FILESIZE="$(stat -c %b "${NEWUPDATE}")" + BLOCKS="$((FILESIZE / 20))" + export CPOINTS="$((BLOCKS / 100))" # shellcheck disable=SC2016 - ./tar xf "${NEWUPDATE}" --strip-components=1 --no-same-permissions --no-same-owner --checkpoint=200 --checkpoint-action=exec='./kotar_cpoint $TAR_CHECKPOINT' + ./tar xf "${NEWUPDATE}" --strip-components=1 --no-same-permissions --no-same-owner --checkpoint="${CPOINTS}" --checkpoint-action=exec='./fbink -q -y -6 -P $(($TAR_CHECKPOINT/$CPOINTS))' fail=$? # Cleanup behind us... if [ "${fail}" -eq 0 ]; then @@ -42,6 +48,7 @@ ko_update_check() { ./fbink -q -y -5 -pm "KOReader may fail to function properly!" fi rm -f "${NEWUPDATE}" # always purge newupdate in all cases to prevent update loop + unset BLOCKS CPOINTS fi } # NOTE: Keep doing an initial update check, in addition to one during the restart loop, so we can pickup potential updates of this very script... diff --git a/platform/kobo/kotar_cpoint b/platform/kobo/kotar_cpoint deleted file mode 100755 index 75dca4667..000000000 --- a/platform/kobo/kotar_cpoint +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/sh - -## First arg is the checkpoint number, and we get one every 200 checkpoints. -CHECKPOINT_NUM="${1}" -CHECKPOINT_GRANULARITY="200" - -# Use that to build a progress bar -PROGRESS_AMOUNT="$((CHECKPOINT_NUM / CHECKPOINT_GRANULARITY))" - -# Which text do we want to use? -case "${2}" in - "create") - PROGRESS_BASE_STRING="Preparing local OTA package" - ;; - *) - PROGRESS_BASE_STRING="Updating KOReader" - ;; -esac - -# And start drawing the progress bar... -PROGRESS_STRING="" -for _ in $(seq 1 ${PROGRESS_AMOUNT}); do - # Fill the progress bar... - PROGRESS_STRING="${PROGRESS_STRING}." -done - -# Print our progress :) -./fbink -q -y -7 -pmh "${PROGRESS_BASE_STRING}" "${PROGRESS_STRING}"