Dotfiles/create-webapp.sh

132 lines
3.9 KiB
Bash
Executable File

#!/usr/bin/env bash
# Creates a Chromium webapp .desktop entry with favicon as icon.
# Usage: create-webapp.sh <url> [display-name]
set -euo pipefail
usage() {
echo "Usage: $(basename "$0") <url> [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 <title> 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[^>]*>([^<]+)</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"<link([^>]+?)/??>", 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"