From 9844dbcde38eb7933017d55b52e63e2b077f35d3 Mon Sep 17 00:00:00 2001 From: Constantin Piber <59023762+cpiber@users.noreply.github.com> Date: Fri, 20 Jun 2025 18:38:23 +0200 Subject: [PATCH] Download JCEF at docker build step (#132) * Add `LD_PRELOAD` to startup script Closes #131 * Script to download and extract a JCEF release Closely mirrors what KCEF does, but written in glorious AWK * Call kcef_download and link downloaded JCEF where Suwayomi expects it The script downloads JCEF into /opt. Link this folder at runtime into the data folder where Suwayomi expects it. This allows us to easily bundle the predownloaded release without modifying Suwayomi startup. Move `LD_PRELOAD` down a bit to avoid warnings about preloading libcef.so before the symlink exists. * Add KCEF url to workflow to embed JCEF into container --- .github/workflows/container.yml | 1 + Dockerfile | 7 ++- scripts/kcef_download.sh | 83 +++++++++++++++++++++++++++++++++ scripts/startup_script.sh | 8 ++++ 4 files changed, 98 insertions(+), 1 deletion(-) create mode 100755 scripts/kcef_download.sh diff --git a/.github/workflows/container.yml b/.github/workflows/container.yml index b6250eb..aed78fe 100644 --- a/.github/workflows/container.yml +++ b/.github/workflows/container.yml @@ -182,6 +182,7 @@ jobs: TACHIDESK_FILENAME=${{ steps.get_latest_release_metadata.outputs.release_filename }} TACHIDESK_DOCKER_GIT_COMMIT=${{ steps.get_latest_release_metadata.outputs.tachidesk_docker_git_commit }} TACHIDESK_KCEF= + TACHIDESK_KCEF_RELEASE_URL=https://api.github.com/repos/JetBrains/JetBrainsRuntime/releases/latest tags: | ${{ inputs.tachidesk_release_type == 'stable' && 'ghcr.io/suwayomi/tachidesk:latest' || '' }} ghcr.io/suwayomi/tachidesk:${{ inputs.tachidesk_release_type }} diff --git a/Dockerfile b/Dockerfile index 6c49d12..e2b3b5d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,6 +7,7 @@ ARG TACHIDESK_FILENAME ARG TACHIDESK_RELEASE_DOWNLOAD_URL ARG TACHIDESK_DOCKER_GIT_COMMIT ARG TACHIDESK_KCEF=y # y or n, leave empty for auto-detection +ARG TACHIDESK_KCEF_RELEASE_URL LABEL maintainer="suwayomi" \ org.opencontainers.image.title="Suwayomi Docker" \ @@ -35,12 +36,16 @@ RUN apt-get update && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* +COPY scripts/kcef_download.sh /root/kcef_download.sh + # install CEF dependencies RUN if [ "$TACHIDESK_KCEF" = "y" ] || ([ "$TACHIDESK_KCEF" = "" ] && ([ "$TARGETPLATFORM" = "linux/amd64" ] || [ "$TARGETPLATFORM" = "linux/arm64" ])); then \ apt-get update && \ apt-get -y install --no-install-recommends -y libxss1 libxext6 libxrender1 libxcomposite1 libxdamage1 libxkbcommon0 libxtst6 \ libjogl2-jni libgluegen2-jni libglib2.0-0t64 libnss3 libdbus-1-3 libpango-1.0-0 libcairo2 libasound2t64 \ - libatk-bridge2.0-0t64 libcups2t64 libdrm2 libgbm1 xvfb && \ + libatk-bridge2.0-0t64 libcups2t64 libdrm2 libgbm1 xvfb \ + curl jq gawk findutils && \ + /root/kcef_download.sh "$TACHIDESK_KCEF_RELEASE_URL" "$TARGETPLATFORM" && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* || exit 1; \ fi diff --git a/scripts/kcef_download.sh b/scripts/kcef_download.sh new file mode 100755 index 0000000..86ff5e9 --- /dev/null +++ b/scripts/kcef_download.sh @@ -0,0 +1,83 @@ +#!/bin/sh + +set -e + +url="$1" +arch="$2" +installdir="${3:-/opt/kcef/jcef}" + +echo "Will try to download matching KCEF to $installdir, arch=$arch, api url=$url" + +if [ -d "$installdir" ] && [ -f "$installdir/install.lock" ]; then + echo "JCEF already downloaded to $installdir, nothing to do" + exit 0 +fi + +if [ -z "$url" ]; then + echo "Not downloading KCEF since no URL specified" + exit 0 +fi + +arpath=/tmp/kcef/jcef.tar.gz +expath=/tmp/kcef/jcef +mkdir -p "$expath" + +if [ ! -f "$arpath" ]; then + body="`curl -# -H 'accept: application/vnd.github+json' "$url" | jq -r '.body'`" + archive="`echo "$body" | gawk -F'|' ' + function compare_sdk(i1, v1, i2, v2) { + # sort sdks last (https://github.com/DatL4g/KCEF/blob/0665269b7d6a91b0ee187f4432bb5be5ca41a112/kcef/src/main/kotlin/dev/datlag/kcef/KCEFBuilder.kt#L743-L755) + if (v1 ~ /sdk/ && v2 ~ /sdk/) return 0; + if (v1 ~ /sdk/) return 1; + if (v2 ~ /sdk/) return -1; + return 0; + } + BEGIN { + # ensure urls is an array + delete urls[0]; + # parse os/arch tuple + match("'"$arch"'", /(.*)\/(.*)/, a); + os=a[1]; + arch=a[2]; + if (arch == "amd64") arch="x64"; + if (arch == "arm64") arch="aarch64"; + } + # for each line, check that the third table column contains a url and if so, extract it + # also need to check that it contains JCEF and matches the os/arch tuple + match($4, /(https?:\/\/|www.)[-a-zA-Z0-9+&@#\/%?=~_|!:.;]*[-a-zA-Z0-9+&@#/%=~_|]/, m) { + # if so, push to the urls array; there is no push function, so do this cursed construction + # arrays by convention start at 1, so do that + if (m[0] ~ /jcef/ && m[0] ~ os && m[0] ~ arch && m[0] ~ /\.tar\.gz$/) urls[length(urls)+1] = m[0]; + } + END { + # now make sure sdk is sorted last, since we dont actually need the full sdk + asort(urls, sorted, "compare_sdk"); + for (x in sorted) print sorted[x]; + } + ' | head -n1`" + + if [ -z "$archive" ]; then + echo "No suitable archive found on release page, so not downloading" + exit 0 + fi + + echo "Found suitable JCEF release: $archive" + curl -# -L -H 'accept: application/x-tar' -o "$arpath" "$archive" +fi + +set -xe +tar -C "$expath" -xf "$arpath" +libfolder="`find "$expath" -type d -name lib`" + +if [ -z "$libfolder" ]; then + echo "lib folder not found in extracted archive, aborting" + rm -rf /tmp/kcef + exit 0 +fi + +mkdir -p "$installdir" +rmdir "$installdir" # we abuse -p to make sure all parent directories are created, then delete the actual target, since mv would move the libfolder inside otherwise +mv "$libfolder" "$installdir" +chmod -R a+x "$installdir" +touch "$installdir/install.lock" +rm -rf /tmp/kcef diff --git a/scripts/startup_script.sh b/scripts/startup_script.sh index d42c2a4..988d4f6 100755 --- a/scripts/startup_script.sh +++ b/scripts/startup_script.sh @@ -92,6 +92,14 @@ sed -i -r "s/server.opdsChapterSortOrder = \"(.*?)\"( #)?/server.opdsChapterSort if command -v Xvfb >/dev/null; then Xvfb :0 -screen 0 800x680x24 -nolisten tcp >/dev/null 2>&1 & export DISPLAY=:0 + + if [ ! -d /home/suwayomi/.local/share/Tachidesk/bin ]; then + mkdir -p /home/suwayomi/.local/share/Tachidesk/bin + fi + if [ ! -d /home/suwayomi/.local/share/Tachidesk/bin/kcef ] && [ ! -L /home/suwayomi/.local/share/Tachidesk/bin/kcef ]; then + ln -s /opt/kcef/jcef /home/suwayomi/.local/share/Tachidesk/bin/kcef + fi + export LD_PRELOAD=/home/suwayomi/.local/share/Tachidesk/bin/kcef/libcef.so else echo "Suwayomi built without KCEF support, not starting Xvfb" fi