diff --git a/create-webapp.sh b/create-webapp.sh new file mode 100755 index 0000000..519125a --- /dev/null +++ b/create-webapp.sh @@ -0,0 +1,131 @@ +#!/usr/bin/env bash +# Creates a Chromium webapp .desktop entry with favicon as icon. +# Usage: create-webapp.sh [display-name] + +set -euo pipefail + +usage() { + echo "Usage: $(basename "$0") [display-name]" + echo + echo " Fetches the page title and favicon, then writes a .desktop" + echo " entry that opens the URL in Chromium app-window mode (no tabs/address bar)." + exit 1 +} + +[[ $# -lt 1 ]] && usage + +URL="$1" +CUSTOM_NAME="${2:-}" + +# Ensure scheme +[[ "$URL" != http://* && "$URL" != https://* ]] && URL="https://$URL" + +BASE_URL=$(echo "$URL" | grep -oE 'https?://[^/]+') +DOMAIN=$(echo "$BASE_URL" | sed -E 's|https?://(www\.)?||') + +echo "Fetching: $URL" +PAGE_HTML=$(curl -sL --max-time 15 \ + -A "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36" \ + "$URL" 2>/dev/null || echo "") + +# Resolve app name from or argument +if [[ -n "$CUSTOM_NAME" ]]; then + APP_NAME="$CUSTOM_NAME" +else + APP_NAME=$(echo "$PAGE_HTML" | python3 -c ' +import sys, re, html as html_mod +content = sys.stdin.read() +m = re.search(r"<title[^>]*>([^<]+)", content, re.IGNORECASE) +print(html_mod.unescape(m.group(1).strip()) if m else "", end="") +' 2>/dev/null || true) + APP_NAME="${APP_NAME:-$DOMAIN}" +fi + +# Safe identifier for filenames / WM_CLASS +SAFE_ID=$(echo "$APP_NAME" | tr '[:upper:]' '[:lower:]' | tr -cs 'a-z0-9' '-' | sed 's/^-*//;s/-*$//') +[[ -z "$SAFE_ID" ]] && SAFE_ID="$DOMAIN" + +# Find best favicon from HTML link tags (largest declared size wins) +FAVICON_URL=$(echo "$PAGE_HTML" | python3 -c ' +import sys, re +from urllib.parse import urljoin + +base = sys.argv[1] +content = sys.stdin.read() + +candidates = [] +for link in re.findall(r"]+?)/??>", content, re.IGNORECASE | re.DOTALL): + rel_m = re.search(r"rel=[\"'"'"']([^\"'"'"']+)[\"'"'"']", link, re.IGNORECASE) + if not rel_m or "icon" not in rel_m.group(1).lower(): + continue + href_m = re.search(r"href=[\"'"'"']([^\"'"'"']+)[\"'"'"']", link, re.IGNORECASE) + if not href_m: + continue + size = 0 + sizes_m = re.search(r"sizes=[\"'"'"']([^\"'"'"']+)[\"'"'"']", link, re.IGNORECASE) + if sizes_m: + val = sizes_m.group(1).lower().split("x")[0] + try: + size = 999 if val == "any" else int(val) + except ValueError: + pass + candidates.append((size, href_m.group(1))) + +if candidates: + candidates.sort(key=lambda x: x[0], reverse=True) + print(urljoin(base, candidates[0][1]), end="") +' "$BASE_URL" 2>/dev/null || true) + +FAVICON_URL="${FAVICON_URL:-$BASE_URL/favicon.ico}" +echo "Favicon: $FAVICON_URL" + +# Download icon +ICON_DIR="$HOME/.local/share/icons/webapps" +mkdir -p "$ICON_DIR" + +TMP=$(mktemp /tmp/webapp-icon.XXXXXX) +trap 'rm -f "$TMP"' EXIT + +ICON_PATH="chromium" # fallback + +if curl -sL --max-time 10 -A "Mozilla/5.0" -o "$TMP" "$FAVICON_URL" && [[ -s "$TMP" ]]; then + if command -v convert &>/dev/null \ + && convert "${TMP}[0]" -resize 128x128\> "${ICON_DIR}/${SAFE_ID}.png" 2>/dev/null; then + ICON_PATH="${ICON_DIR}/${SAFE_ID}.png" + echo "Icon: $ICON_PATH (PNG via ImageMagick)" + else + MIME=$(file -b --mime-type "$TMP") + EXT="${MIME##*/}" + [[ "$EXT" == "x-icon" || "$EXT" == "vnd.microsoft.icon" ]] && EXT="ico" + cp "$TMP" "${ICON_DIR}/${SAFE_ID}.${EXT}" + ICON_PATH="${ICON_DIR}/${SAFE_ID}.${EXT}" + echo "Icon: $ICON_PATH ($MIME)" + fi +else + echo "Warning: could not fetch favicon — using chromium default icon" +fi + +# Write .desktop file +DESKTOP_DIR="$HOME/.local/share/applications" +mkdir -p "$DESKTOP_DIR" +DESKTOP_FILE="${DESKTOP_DIR}/webapp-${SAFE_ID}.desktop" + +cat > "$DESKTOP_FILE" << DESKTOP +[Desktop Entry] +Version=1.0 +Type=Application +Name=${APP_NAME} +Exec=chromium --app=${URL} +Icon=${ICON_PATH} +Terminal=false +Categories=Network;WebBrowser; +StartupWMClass=${SAFE_ID} +DESKTOP + +chmod +x "$DESKTOP_FILE" + +echo +echo "Created : $DESKTOP_FILE" +echo " Name : $APP_NAME" +echo " URL : $URL" +echo " Icon : $ICON_PATH"