diff --git a/.gitignore b/.gitignore index 117407d..a71ef04 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,9 @@ log.txt /lock.file settings.json /lang/English.json +# AppImage +/AppDir +/appimage-builder +/appimage-build +/*.AppImage +/*.AppImage.zsync diff --git a/appimage/AppImageBuilder.yml b/appimage/AppImageBuilder.yml new file mode 100644 index 0000000..612c8ac --- /dev/null +++ b/appimage/AppImageBuilder.yml @@ -0,0 +1,144 @@ +# Useful links: +# https://appimage-builder.readthedocs.io/en/latest/reference/recipe.html + +version: 1 + +script: + # Clean up any previous builds. + - rm -rf "$BUILD_DIR" "$TARGET_APPDIR" || true + - mkdir -p "$BUILD_DIR" "$TARGET_APPDIR" + - cd "$BUILD_DIR" + + # Build a recent version of libXft first. This fixes an issue where the app won't start with libXft 2.3.3 if an emoji font is installed on the system. + - curl -L https://xorg.freedesktop.org/releases/individual/lib/libXft-2.3.8.tar.xz -o libXft.tar.xz + - tar xvf libXft.tar.xz + - cd libXft-2.3.8 + - ./configure --prefix="$TARGET_APPDIR/usr" --disable-static + - make -j$(nproc) + - make install-strip + + # Package the app. + - mkdir -p "$TARGET_APPDIR"/usr/{src,share/icons/hicolor/128x128/apps} + - cp -r "$SOURCE_DIR/../lang" "$SOURCE_DIR/../pickaxe.ico" "$SOURCE_DIR"/../*.py "$TARGET_APPDIR/usr/src" + - cp "$SOURCE_DIR/pickaxe.png" "$TARGET_APPDIR/usr/share/icons/hicolor/128x128/apps/io.github.devilxd.twitchdropsminer.png" + + # Remove certifi from the line below if using Python 3.10+. + - python3.8 -m pip install --ignore-installed --prefix=/usr --root="$TARGET_APPDIR" -r "$SOURCE_DIR/../requirements.txt" certifi + +AppDir: + app_info: + id: io.github.devilxd.twitchdropsminer + name: Twitch Drops Miner + icon: io.github.devilxd.twitchdropsminer + version: '{{APP_VERSION}}' + exec: usr/bin/python3.8 + exec_args: '${APPDIR}/usr/src/main.py $@' + + apt: + arch: amd64 + sources: + - sourceline: deb http://archive.ubuntu.com/ubuntu/ focal main universe + - sourceline: deb http://archive.ubuntu.com/ubuntu/ focal-updates main universe + - sourceline: deb http://archive.ubuntu.com/ubuntu/ focal-backports main universe + - sourceline: deb http://archive.ubuntu.com/ubuntu/ focal-security main universe + key_url: http://keyserver.ubuntu.com/pks/lookup?op=get&search=0x3b4fe6acc0b21f32 + + include: + - gir1.2-appindicator3-0.1 + - python3-tk + + exclude: + - adwaita-icon-theme + - dconf-service + - glib-networking-services + - gsettings-desktop-schemas + - hicolor-icon-theme + - humanity-icon-theme + - libavahi-client3 + - libavahi-common3 + - libbrotli1 + - libcolord2 + - libcups2 + - libdb5.3 + - libdconf1 + - libgmp10 + - libgnutls30 + - libgssapi-krb5-2 + - libhogweed5 + - libicu66 + - libjson-glib-1.0-0 + - libk5crypto3 + - libkrb5-3 + - libkrb5support0 + - liblcms2-2 + - libncursesw6 + - libnettle7 + - libp11-kit0 + - libpangoxft-1.0-0 + - libpsl5 + - librest-0.7-0 + - libsoup2.4-1 + - libsoup-gnome2.4-1 + - libsqlite3-0 + - libtasn1-6 + - libtiff5 + - libtinfo6 + - libunistring2 + - libwebp6 + - libxft2 # We'll ship our own, updated version of this library. + - libxml2 + - mime-support + - readline-common + - tzdata + - xkb-data + + files: + exclude: + - etc + - usr/bin/normalizer + - usr/bin/pdb3* + - usr/bin/py3* + - usr/bin/pydoc* + - usr/bin/pygettext3* + - usr/include + # The next 2 files come from our own build of libXft, and they can be removed. + - usr/lib/libXft.la + - usr/lib/pkgconfig + - usr/lib/python3.9 + - usr/lib/valgrind + - usr/lib/*-linux-gnu/engines-1.1 + - usr/lib/*-linux-gnu/glib-2.0 + - usr/lib/*-linux-gnu/gtk-3.0 + - usr/lib/*-linux-gnu/libgtk-3.0 + - usr/share/applications + - usr/share/binfmts + - usr/share/bug + - usr/share/doc + - usr/share/doc-base + - usr/share/glib-2.0 + - usr/share/lintian + - usr/share/man + - usr/share/pixmaps + - usr/share/python3 + - usr/share/themes + + runtime: + env: + PATH: '${APPDIR}/usr/bin:${PATH}' + PYTHONHOME: '${APPDIR}/usr' + PYTHONPATH: '${APPDIR}/usr/lib/python3.8/tkinter:${APPDIR}/usr/lib/python3.8/site-packages' + APPDIR_LIBRARY_PATH: '${APPDIR}/usr/lib:${APPDIR}/usr/lib/x86_64-linux-gnu:${APPDIR}/lib/x86_64-linux-gnu' + TCL_LIBRARY: '${APPDIR}/usr/share/tcltk/tcl8.6' + TK_LIBRARY: '${APPDIR}/usr/lib/tcltk/x86_64-linux-gnu/tk8.6' + TKPATH: '${APPDIR}/usr/lib/tcltk/x86_64-linux-gnu/tk8.6' + # Remove the line below if using Python 3.10+. + SSL_CERT_FILE: '${APPDIR}/usr/lib/python3.8/site-packages/certifi/cacert.pem' + # The app seems to have problems running on Wayland at the moment. + # See: https://github.com/DevilXD/TwitchDropsMiner/issues/321 + GDK_BACKEND: x11 + +AppImage: + arch: x86_64 + file_name: Twitch.Drops.Miner-x86_64.AppImage + sign-key: None + update-information: guess diff --git a/appimage/pickaxe.png b/appimage/pickaxe.png new file mode 100644 index 0000000..40ceefa Binary files /dev/null and b/appimage/pickaxe.png differ diff --git a/constants.py b/constants.py index 7adf4ff..9163db5 100644 --- a/constants.py +++ b/constants.py @@ -1,5 +1,6 @@ from __future__ import annotations +import os import sys import random import logging @@ -18,8 +19,9 @@ if TYPE_CHECKING: from typing_extensions import TypeAlias -# True if we're running from a built EXE, False inside a dev build -IS_PACKAGED = hasattr(sys, "_MEIPASS") +# True if we're running from a built EXE (or a Linux AppImage), False inside a dev build +IS_APPIMAGE = "APPIMAGE" in os.environ and os.path.exists(os.environ["APPIMAGE"]) +IS_PACKAGED = hasattr(sys, "_MEIPASS") or IS_APPIMAGE # logging special levels CALL = logging.INFO - 1 logging.addLevelName(CALL, "CALL") @@ -39,7 +41,9 @@ def _resource_path(relative_path: Path | str) -> Path: Works for dev and for PyInstaller. """ - if IS_PACKAGED: + if IS_APPIMAGE: + base_path = Path(sys.argv[0]).absolute().parent + elif IS_PACKAGED: # PyInstaller's folder where the one-file app is unpacked meipass: str = getattr(sys, "_MEIPASS") base_path = Path(meipass) @@ -73,12 +77,15 @@ def _merge_vars(base_vars: JsonType, vars: JsonType) -> None: # Base Paths -# NOTE: pyinstaller will set this to its own executable when building, -# detect this to use __file__ and main.py redirection instead -SELF_PATH = Path(sys.argv[0]).absolute() -if SELF_PATH.stem == "pyinstaller": - SELF_PATH = Path(__file__).with_name("main.py").absolute() -WORKING_DIR = SELF_PATH.absolute().parent +if IS_APPIMAGE: + SELF_PATH = Path(os.environ["APPIMAGE"]).absolute() +else: + # NOTE: pyinstaller will set sys.argv[0] to its own executable when building, + # detect this to use __file__ and main.py redirection instead + SELF_PATH = Path(sys.argv[0]).absolute() + if SELF_PATH.stem == "pyinstaller": + SELF_PATH = Path(__file__).with_name("main.py").absolute() +WORKING_DIR = SELF_PATH.parent # Development paths VENV_PATH = Path(WORKING_DIR, "env") SITE_PACKAGES_PATH = Path(VENV_PATH, SYS_SITE_PACKAGES)