neo-pod-desktop/scripts/build-appimage.sh

197 lines
5.5 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
APP_NAME="neo-pod-desktop"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
BUILD_DIR="$PROJECT_DIR/build/appimage"
APP_DIR="$BUILD_DIR/$APP_NAME.AppDir"
PYTHON="${PYTHON:-python3}"
PIP="${PIP:-pip3}"
VENV_DIR="$BUILD_DIR/venv"
ARCH="${ARCH:-x86_64}"
APPIMAGE_OUTPUT="$PROJECT_DIR/$APP_NAME-${ARCH}.AppImage"
APPIMAGETOOL_URL="https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-${ARCH}.AppImage"
RED='\033[0;31m'
GREEN='\033[0;32m'
CYAN='\033[0;36m'
NC='\033[0m'
info() { echo -e "${CYAN}[INFO]${NC} $*"; }
ok() { echo -e "${GREEN}[OK]${NC} $*"; }
err() { echo -e "${RED}[ERR]${NC} $*"; }
cleanup() {
info "Cleaning up build directory..."
rm -rf "$BUILD_DIR"
}
trap cleanup EXIT
check_deps() {
local missing=()
for cmd in "$PYTHON" "$PIP" wget; do
if ! command -v "$cmd" &>/dev/null; then
missing+=("$cmd")
fi
done
if [ ${#missing[@]} -gt 0 ]; then
err "Missing dependencies: ${missing[*]}"
exit 1
fi
}
install_pyinstaller() {
if [ -f "$VENV_DIR/bin/python" ] && "$VENV_DIR/bin/python" -c "import PyInstaller" 2>/dev/null; then
return 0
fi
info "Creating virtual environment..."
"$PYTHON" -m venv --copies "$VENV_DIR"
info "Installing PyInstaller and dependencies..."
"$VENV_DIR/bin/pip" install pyinstaller
# Install project dependencies for bundling
"$VENV_DIR/bin/pip" install -r "$PROJECT_DIR/requirements.txt"
}
download_appimagetool() {
local tool="$BUILD_DIR/appimagetool"
if [ -x "$tool" ]; then
return 0
fi
info "Downloading appimagetool..."
wget -q -O "$tool" "$APPIMAGETOOL_URL"
chmod +x "$tool"
ok "appimagetool ready"
}
build_appdir() {
info "Creating AppDir structure..."
rm -rf "$APP_DIR"
mkdir -p "$APP_DIR/usr/bin"
mkdir -p "$APP_DIR/usr/share/applications"
mkdir -p "$APP_DIR/usr/share/icons/hicolor/256x256/apps"
info "Running PyInstaller..."
cd "$PROJECT_DIR"
"$VENV_DIR/bin/pyinstaller" \
--onedir \
--name "$APP_NAME" \
--distpath "$APP_DIR/usr/bin" \
--workpath "$BUILD_DIR/pybuild" \
--specpath "$BUILD_DIR" \
--add-data "$PROJECT_DIR/src:$APP_NAME/src" \
--paths "$PROJECT_DIR/src" \
--hidden-import PyQt6.QtMultimedia \
--hidden-import PyQt6.QtMultimediaWidgets \
--collect-all PyQt6 \
--exclude-module PyQt6.QtBluetooth \
--exclude-module PyQt6.QtWebEngineWidgets \
--exclude-module PyQt6.QtWebEngineCore \
--exclude-module PyQt6.QtWebEngineQuick \
--exclude-module PyQt6.QtWebChannel \
--exclude-module PyQt6.QtPositioning \
--exclude-module PyQt6.QtSensors \
--exclude-module PyQt6.QtTest \
--exclude-module PyQt6.QtQuick3D \
--exclude-module PyQt6.QtShaderTools \
--exclude-module PyQt6.QtSpatialAudio \
src/main.py
}
create_desktop_file() {
cat > "$APP_DIR/$APP_NAME.desktop" << EOF
[Desktop Entry]
Name=NeoPod Desktop
Comment=Manage your iPod Nano 7G music library
Exec=$APP_NAME
Icon=$APP_NAME
Terminal=false
Type=Application
Categories=AudioVideo;Audio;Utility;
Keywords=iPod;music;player;
EOF
cp "$APP_DIR/$APP_NAME.desktop" "$APP_DIR/usr/share/applications/"
}
create_apprun() {
cat > "$APP_DIR/AppRun" << 'APPRUN'
#!/bin/bash
HERE="$(dirname "$(readlink -f "$0")")"
export QT_QPA_PLATFORMTHEME=gtk3
export QT_STYLE_OVERRIDE=
exec "$HERE/usr/bin/neo-pod-desktop/neo-pod-desktop" "$@"
APPRUN
chmod +x "$APP_DIR/AppRun"
}
create_icon() {
local icon="$APP_DIR/$APP_NAME.png"
local py="$VENV_DIR/bin/python3"
if [ ! -f "$py" ]; then
py="$PYTHON"
fi
if "$py" -c "from PIL import Image" 2>/dev/null; then
"$py" -c "
from PIL import Image, ImageDraw
img = Image.new('RGBA', (256, 256), (0, 0, 0, 0))
draw = ImageDraw.Draw(img)
# Simple iPod-style icon
draw.rounded_rectangle([20, 40, 236, 216], radius=30, fill=(50, 100, 200))
draw.rounded_rectangle([60, 80, 196, 180], radius=20, fill=(200, 220, 255))
draw.ellipse([100, 120, 156, 176], fill=(50, 100, 200))
img.save('$APP_DIR/$APP_NAME.png')
img.save('$APP_DIR/usr/share/icons/hicolor/256x256/apps/$APP_NAME.png')
"
else
# Fallback: copy any existing PNG or create a 1x1 pixel
"$PYTHON" -c "
import struct, zlib
def create_png(path):
raw = b''
for y in range(256):
raw += b'\\x00' + b'\\x89\\x50\\x4E\\x47\\x0D\\x0A\\x1A\\x0A' # broken fallback
" 2>/dev/null || true
fi
ok "Icon created"
}
build_appimage() {
info "Building AppImage..."
cd "$BUILD_DIR"
local tool="$BUILD_DIR/appimagetool"
local appdir="$APP_DIR"
local output="$APPIMAGE_OUTPUT"
ARCH="$ARCH" "$tool" "$appdir" "$output"
chmod +x "$output"
ok "AppImage built: $output ($(du -h "$output" | cut -f1))"
}
main() {
echo ""
echo -e "${CYAN}========================================${NC}"
echo -e "${CYAN} NeoPod Desktop AppImage Builder${NC}"
echo -e "${CYAN}========================================${NC}"
echo ""
install_pyinstaller
check_deps
mkdir -p "$BUILD_DIR"
download_appimagetool
build_appdir
create_desktop_file
create_apprun
create_icon
build_appimage
echo ""
echo -e "${GREEN}========================================${NC}"
echo -e "${GREEN} Done: $APPIMAGE_OUTPUT${NC}"
echo -e "${GREEN}========================================${NC}"
}
main "$@"