#!/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"