#!/usr/bin/env bash # Install a published ServeMedia release into ~/.servemedia (userscope desktop + bin). set -euo pipefail REPO="${SERVEMEDIA_REPO:-AlyShmahell/servemedia}" DEST="${SERVEMEDIA_HOME:-$HOME/.servemedia}" BIN_DIR="${XDG_BIN_HOME:-$HOME/.local/bin}" APP_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/applications" ICON_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/icons/hicolor/scalable/apps" usage() { cat <&2 exit 1 fi if ! command -v curl >/dev/null 2>&1; then echo "error: curl is required" >&2 exit 1 fi if ! command -v python3 >/dev/null 2>&1; then echo "error: python3 is required" >&2 exit 1 fi menu() { local opts=("$@") local n=${#opts[@]} local i=0 local key rest j printf '\e[?25l' restore() { printf '\e[?25h\e[0m'; } trap restore EXIT draw() { for j in "${!opts[@]}"; do if (( j == i )); then printf '\e[7m> %s\e[0m\n' "${opts[j]}" else printf ' %s\n' "${opts[j]}" fi done } draw while true; do IFS= read -rsn1 key "$json_tmp"; then echo "error: could not list releases for ${REPO}" >&2 exit 1 fi mapfile -t TAGS < <(python3 -c ' import json, sys rels = json.load(open(sys.argv[1])) if not isinstance(rels, list) or not rels: sys.exit(2) for r in rels: tag = (r.get("tag_name") or "").strip() if tag: print(tag) ' "$json_tmp") || { echo "error: no releases published for ${REPO}" >&2 exit 1 } if [[ ${#TAGS[@]} -eq 0 ]]; then echo "error: no releases published for ${REPO}" >&2 exit 1 fi echo "Select a ServeMedia release (${REPO})" CHOICE= menu "${TAGS[@]}" TAG="${TAGS[$CHOICE]}" ASSET_URL="$(python3 -c ' import json, sys tag = sys.argv[2] rels = json.load(open(sys.argv[1])) rel = next((r for r in rels if (r.get("tag_name") or "") == tag), None) if not rel: sys.exit(2) assets = rel.get("assets") or [] bundled, plain = [], [] for a in assets: name = a.get("name") or "" url = a.get("browser_download_url") or "" if name.endswith("-linux-amd64-bundled.tar.gz"): bundled.append(url) elif name.endswith("-linux-amd64.tar.gz") and "bundled" not in name: plain.append(url) if bundled: print(bundled[0]) elif plain: print(plain[0]) else: sys.exit(3) ' "$json_tmp" "$TAG")" || { echo "error: linux-amd64 tarball not found on ${TAG}" >&2 exit 1 } work="$(mktemp -d)" trap 'rm -rf "$work" "$json_tmp"' EXIT tarball="$work/servemedia.tar.gz" echo "Downloading ${ASSET_URL}" curl -fL -A servemedia-install --retry 3 --retry-delay 1 -o "$tarball" "$ASSET_URL" tar -xzf "$tarball" -C "$work" src="" if [[ -x "$work/servemedia/servemedia" ]]; then src="$work/servemedia" elif [[ -x "$work/servemedia" && -d "$work/config" ]]; then src="$work" else echo "error: unexpected archive layout" >&2 exit 1 fi mkdir -p "$DEST" for item in servemedia config public tools vendor share LICENSE; do if [[ -e "$src/$item" ]]; then rm -rf "$DEST/$item" cp -a "$src/$item" "$DEST/" fi done chmod +x "$DEST/servemedia" mkdir -p "$BIN_DIR" "$APP_DIR" "$ICON_DIR" ln -sfn "$DEST/servemedia" "$BIN_DIR/servemedia" icon_src="$DEST/public/logo.svg" if [[ -f "$icon_src" ]]; then cp -a "$icon_src" "$ICON_DIR/servemedia.svg" fi if command -v gtk-update-icon-cache >/dev/null 2>&1; then gtk-update-icon-cache -f -t "$(dirname "$(dirname "$ICON_DIR")")" >/dev/null 2>&1 || true fi desktop_src="$DEST/share/applications/servemedia.desktop" desktop_dst="$APP_DIR/servemedia.desktop" if [[ ! -f "$desktop_src" ]]; then echo "error: desktop file missing from release" >&2 exit 1 fi icon_path="$ICON_DIR/servemedia.svg" if [[ ! -f "$icon_path" ]]; then icon_path="$icon_src" fi sed -e "s|^Exec=.*|Exec=${DEST}/servemedia|" -e "s|^Icon=.*|Icon=${icon_path}|" "$desktop_src" >"$desktop_dst" if command -v update-desktop-database >/dev/null 2>&1; then update-desktop-database "$APP_DIR" >/dev/null 2>&1 || true fi echo "Installed ${TAG} to ${DEST}" echo " ${BIN_DIR}/servemedia" echo " ${desktop_dst}" echo "Add ${BIN_DIR} to PATH if servemedia is not found."