mirror of
https://github.com/PR0M3TH3AN/VoxVera.git
synced 2026-10-28 10:10:40 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e1aa0513e9 | ||
|
|
79207a0feb | ||
|
|
2aebc93507 | ||
|
|
c5b989767f | ||
|
|
246bbb5730 | ||
|
|
9ab26ff943 | ||
|
|
0dec5b7928 |
No files matched your search
@@ -49,9 +49,11 @@ dist_bin/
|
||||
voxvera/resources/bin/*
|
||||
!voxvera/resources/bin/.gitkeep
|
||||
site/download/*.zip
|
||||
!site/download/voxvera-source.zip
|
||||
site/download/*.exe
|
||||
site/download/*.deb
|
||||
site/download/*.dmg
|
||||
site/download/*.AppImage
|
||||
site/download/*.flatpak
|
||||
site/download/voxvera-linux
|
||||
site/download/voxvera-linux-x86_64
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
+94
-120
@@ -5,7 +5,16 @@ command_exists() {
|
||||
command -v "$1" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
# Detect package manager
|
||||
# Detect package manager and sudo
|
||||
if command_exists sudo; then
|
||||
SUDO="sudo"
|
||||
elif [ "$(id -u)" -eq 0 ]; then
|
||||
SUDO=""
|
||||
else
|
||||
echo "This script requires root privileges. Please install sudo or run as root." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if command_exists apt-get; then
|
||||
PM="apt"
|
||||
elif command_exists dnf; then
|
||||
@@ -23,44 +32,68 @@ else
|
||||
exit 1
|
||||
fi
|
||||
|
||||
msg() { echo -e "\e[32m$*\e[0m"; }
|
||||
warn() { echo -e "\e[33m$*\e[0m" >&2; }
|
||||
die() { echo -e "\e[31m$*\e[0m" >&2; exit 1; }
|
||||
|
||||
install_pkg() {
|
||||
case "$PM" in
|
||||
apt)
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y "$@"
|
||||
$SUDO apt-get install -y "$@"
|
||||
;;
|
||||
dnf)
|
||||
sudo dnf install -y "$@"
|
||||
$SUDO dnf install -y "$@"
|
||||
;;
|
||||
yum)
|
||||
sudo yum install -y "$@"
|
||||
$SUDO yum install -y "$@"
|
||||
;;
|
||||
pacman)
|
||||
sudo pacman -Sy --noconfirm "$@"
|
||||
$SUDO pacman -S --noconfirm "$@"
|
||||
;;
|
||||
brew)
|
||||
brew install "$@"
|
||||
;;
|
||||
apk)
|
||||
sudo apk add --no-cache "$@"
|
||||
$SUDO apk add --no-cache "$@"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
require_pkg() {
|
||||
local cmd=$1
|
||||
local pkg=$2
|
||||
if ! command_exists "$cmd"; then
|
||||
install_pkg "$pkg"
|
||||
fi
|
||||
}
|
||||
|
||||
# Only external runtime dependencies needed (for voxvera serve)
|
||||
require_pkg tor tor
|
||||
if [ "$PM" = "brew" ]; then
|
||||
require_pkg onionshare onionshare
|
||||
# Ensure core dependencies are present (always run to ensure they are up to date)
|
||||
msg "Checking and updating system dependencies..."
|
||||
if [ "$PM" = "apt" ]; then
|
||||
$SUDO apt-get update
|
||||
SYSTEM_PKGS=(tor curl git python3-pip python3-venv)
|
||||
[ -n "$(command -v pipx)" ] || SYSTEM_PKGS+=(pipx)
|
||||
install_pkg "${SYSTEM_PKGS[@]}"
|
||||
elif [ "$PM" = "brew" ]; then
|
||||
brew install tor onionshare curl git
|
||||
else
|
||||
require_pkg onionshare-cli onionshare-cli
|
||||
install_pkg tor curl git
|
||||
fi
|
||||
|
||||
# Onionshare-cli - ensure it's installed and working (always attempt update if using pipx)
|
||||
msg "Ensuring onionshare-cli is installed and up-to-date..."
|
||||
if command_exists pipx && (pipx list | grep -q onionshare || ! command_exists onionshare-cli); then
|
||||
msg "Installing/Updating onionshare-cli via pipx..."
|
||||
pipx install --force git+https://github.com/onionshare/onionshare.git#subdirectory=cli || warn "pipx onionshare-cli install/update failed"
|
||||
elif ! command_exists onionshare-cli && ! command_exists onionshare; then
|
||||
if ! install_pkg onionshare-cli; then
|
||||
warn "Onionshare-cli not found in system repositories. Attempting pipx install fallback."
|
||||
if ! command_exists pipx; then
|
||||
install_pkg pipx
|
||||
pipx ensurepath --force
|
||||
export PATH="$HOME/.local/bin:$PATH"
|
||||
fi
|
||||
if command_exists pipx; then
|
||||
msg "Installing onionshare-cli via pipx..."
|
||||
pipx install --force git+https://github.com/onionshare/onionshare.git#subdirectory=cli || warn "pipx onionshare-cli install failed"
|
||||
else
|
||||
warn "Could not install pipx. Please install onionshare-cli manually."
|
||||
fi
|
||||
fi
|
||||
else
|
||||
msg "onionshare-cli found (system package), skipping pipx install. Run 'pipx install --force ...' if you want the latest git version."
|
||||
fi
|
||||
|
||||
download_binary() {
|
||||
@@ -69,119 +102,60 @@ download_binary() {
|
||||
if command_exists curl; then
|
||||
local status
|
||||
status=$(curl -w "%{http_code}" -fsSL "$url" -o "$dest" || true)
|
||||
if [ "$status" = "404" ]; then
|
||||
return 2
|
||||
elif [ "$status" != "200" ]; then
|
||||
return 1
|
||||
fi
|
||||
if [ "$status" = "404" ]; then return 2; elif [ "$status" != "200" ]; then return 1; fi
|
||||
elif command_exists wget; then
|
||||
local out
|
||||
out=$(wget --server-response -q "$url" -O "$dest" 2>&1 || true)
|
||||
if echo "$out" | grep -q "404 Not Found"; then
|
||||
return 2
|
||||
elif ! echo "$out" | grep -q "200 OK"; then
|
||||
return 1
|
||||
fi
|
||||
if echo "$out" | grep -q "404 Not Found"; then return 2; elif ! echo "$out" | grep -q "200 OK"; then return 1; fi
|
||||
else
|
||||
echo "Install curl or wget to download voxvera" >&2
|
||||
warn "Install curl or wget to download voxvera"
|
||||
return 1
|
||||
fi
|
||||
chmod +x "$dest"
|
||||
}
|
||||
|
||||
check_local_bin() {
|
||||
if [[ ":$PATH:" != *":$HOME/.local/bin:"* ]]; then
|
||||
echo "Add \$HOME/.local/bin to your PATH to run VoxVera."
|
||||
fi
|
||||
}
|
||||
|
||||
pip_fallback() {
|
||||
if command_exists pip; then
|
||||
echo "Attempting pip install as fallback..."
|
||||
if pip install --user voxvera; then
|
||||
echo "VoxVera installed successfully via pip."
|
||||
exit 0
|
||||
fi
|
||||
echo "pip installation failed." >&2
|
||||
else
|
||||
echo "pip not found for fallback installation" >&2
|
||||
fi
|
||||
exit 1
|
||||
}
|
||||
|
||||
pip_repo_fallback() {
|
||||
if command_exists pip; then
|
||||
echo "Attempting pip install from repository as fallback..."
|
||||
if pip install --user git+https://github.com/PR0M3TH3AN/VoxVera; then
|
||||
echo "VoxVera installed successfully from repository."
|
||||
exit 0
|
||||
fi
|
||||
echo "pip installation from repository failed." >&2
|
||||
else
|
||||
echo "pip not found for fallback installation" >&2
|
||||
fi
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Install VoxVera (Always use --force to ensure fresh installation)
|
||||
if command_exists pipx; then
|
||||
if ! pipx install --force 'voxvera@git+https://github.com/PR0M3TH3AN/VoxVera.git@main'; then
|
||||
echo "pipx install failed, downloading binary"
|
||||
install_dir="$HOME/.local/bin"
|
||||
mkdir -p "$install_dir"
|
||||
|
||||
# Detect OS for binary selection
|
||||
OS="$(uname -s | tr '[:upper:]' '[:lower:]')"
|
||||
case "$OS" in
|
||||
linux*) BINARY="voxvera-linux" ;;
|
||||
darwin*) BINARY="voxvera-macos" ;;
|
||||
msys*|cygwin*|mingw*) BINARY="voxvera-windows.exe" ;;
|
||||
*) BINARY="voxvera-linux" ;; # fallback to linux
|
||||
esac
|
||||
|
||||
url="https://github.com/PR0M3TH3AN/VoxVera/releases/latest/download/${BINARY}"
|
||||
dest="$install_dir/voxvera"
|
||||
if download_binary "$url" "$dest"; then
|
||||
check_local_bin
|
||||
else
|
||||
rc=$?
|
||||
echo "Binary download failed." >&2
|
||||
if [ $rc -eq 2 ]; then
|
||||
echo "Release asset not found, installing from repository." >&2
|
||||
pip_repo_fallback
|
||||
else
|
||||
pip_fallback
|
||||
fi
|
||||
fi
|
||||
msg "Installing/Re-installing VoxVera via pipx..."
|
||||
if pipx install --force 'voxvera@git+https://github.com/PR0M3TH3AN/VoxVera.git@main'; then
|
||||
pipx ensurepath --force
|
||||
msg "\nVoxVera installed/updated successfully via pipx."
|
||||
msg "IMPORTANT: Please restart your terminal or run 'source ~/.bashrc' (or your shell config) to use 'voxvera'."
|
||||
exit 0
|
||||
fi
|
||||
else
|
||||
install_dir="$HOME/.local/bin"
|
||||
mkdir -p "$install_dir"
|
||||
|
||||
# Detect OS for binary selection
|
||||
OS="$(uname -s | tr '[:upper:]' '[:lower:]')"
|
||||
case "$OS" in
|
||||
linux*) BINARY="voxvera-linux" ;;
|
||||
darwin*) BINARY="voxvera-macos" ;;
|
||||
msys*|cygwin*|mingw*) BINARY="voxvera-windows.exe" ;;
|
||||
*) BINARY="voxvera-linux" ;; # fallback to linux
|
||||
esac
|
||||
warn "pipx install failed, trying binary fallback..."
|
||||
fi
|
||||
|
||||
url="https://github.com/PR0M3TH3AN/VoxVera/releases/latest/download/${BINARY}"
|
||||
dest="$install_dir/voxvera"
|
||||
if download_binary "$url" "$dest"; then
|
||||
check_local_bin
|
||||
install_dir="$HOME/.local/bin"
|
||||
mkdir -p "$install_dir"
|
||||
OS="$(uname -s | tr '[:upper:]' '[:lower:]')"
|
||||
case "$OS" in
|
||||
linux*) BINARY="voxvera-linux" ;;
|
||||
darwin*) BINARY="voxvera-macos" ;;
|
||||
*) BINARY="voxvera-linux" ;;
|
||||
esac
|
||||
|
||||
url="https://github.com/PR0M3TH3AN/VoxVera/releases/latest/download/${BINARY}"
|
||||
dest="$install_dir/voxvera"
|
||||
msg "Downloading VoxVera binary from $url..."
|
||||
if download_binary "$url" "$dest"; then
|
||||
msg "VoxVera binary downloaded to $dest"
|
||||
[[ ":$PATH:" != *":$HOME/.local/bin:"* ]] && warn "Add \$HOME/.local/bin to your PATH."
|
||||
else
|
||||
warn "Binary download failed. Attempting pip install as last resort..."
|
||||
# Use --break-system-packages on Debian 12+ if necessary
|
||||
PIP_OPTS="--user --upgrade"
|
||||
if [ -f /etc/debian_version ]; then
|
||||
DEB_VER=$(cat /etc/debian_version | cut -d. -f1)
|
||||
if [[ "$DEB_VER" =~ ^[0-9]+$ ]] && [ "$DEB_VER" -ge 12 ]; then
|
||||
PIP_OPTS="$PIP_OPTS --break-system-packages"
|
||||
fi
|
||||
fi
|
||||
if pip install $PIP_OPTS 'voxvera@git+https://github.com/PR0M3TH3AN/VoxVera.git@main'; then
|
||||
msg "VoxVera installed via pip."
|
||||
else
|
||||
rc=$?
|
||||
echo "Binary download failed." >&2
|
||||
if [ $rc -eq 2 ]; then
|
||||
echo "Release asset not found, installing from repository." >&2
|
||||
pip_repo_fallback
|
||||
else
|
||||
pip_fallback
|
||||
fi
|
||||
die "Installation failed."
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "VoxVera installed successfully."
|
||||
echo "Run 'voxvera check' to verify your setup."
|
||||
msg "\nVoxVera installed/updated successfully. Run 'voxvera check' to verify."
|
||||
+1
-1
@@ -14,7 +14,7 @@ voxvera = ["templates/**", "src/**", "resources/**", "locales/**", "resources/bi
|
||||
|
||||
[project]
|
||||
name = "voxvera"
|
||||
version = "0.1.8"
|
||||
version = "0.2.0"
|
||||
description = "VoxVera CLI utilities"
|
||||
readme = "README.md"
|
||||
authors = [{name = "VoxVera"}]
|
||||
|
||||
+55
-31
@@ -4,46 +4,70 @@ set -euo pipefail
|
||||
VERSION="${VERSION:-15.0.7}"
|
||||
BASE_URL="https://dist.torproject.org/torbrowser"
|
||||
|
||||
ARCH=$(uname -m)
|
||||
if [ "$ARCH" == "aarch64" ] || [ "$ARCH" == "arm64" ]; then
|
||||
TOR_ARCH="aarch64"
|
||||
else
|
||||
TOR_ARCH="x86_64"
|
||||
fi
|
||||
# ARCH=$(uname -m) # Not needed for multi-platform download if we hardcode/loop targets
|
||||
|
||||
case "$(uname -s)" in
|
||||
Linux*) PLATFORM=linux; ARCHIVE="tor-expert-bundle-linux-${TOR_ARCH}-${VERSION}.tar.gz"; EXE=tor;;
|
||||
Darwin*) PLATFORM=mac; ARCHIVE="tor-expert-bundle-macos-${TOR_ARCH}-${VERSION}.tar.gz"; EXE=tor;;
|
||||
MINGW*|MSYS*|CYGWIN*) PLATFORM=win; ARCHIVE="tor-expert-bundle-windows-x86_64-${VERSION}.tar.gz"; EXE=tor.exe;;
|
||||
*) echo "Unsupported OS" >&2; exit 1;;
|
||||
esac
|
||||
download_platform() {
|
||||
local platform=$1
|
||||
local arch=$2
|
||||
local archive=$3
|
||||
local exe=$4
|
||||
|
||||
local tmpdir=$(mktemp -d)
|
||||
# trap 'rm -rf "$tmpdir"' EXIT # We'll clean up manually in the loop
|
||||
|
||||
TMPDIR=$(mktemp -d)
|
||||
trap 'rm -rf "$TMPDIR"' EXIT
|
||||
local url="$BASE_URL/${VERSION}/${archive}"
|
||||
local dest="$(dirname "$0")/../voxvera/resources/tor/$platform"
|
||||
|
||||
echo "--- Platform: $platform ($arch) ---"
|
||||
echo "Downloading $url"
|
||||
if ! curl -L "$url" -o "$tmpdir/$archive"; then
|
||||
echo "Failed to download $url" >&2
|
||||
rm -rf "$tmpdir"
|
||||
return 1
|
||||
fi
|
||||
|
||||
URL="$BASE_URL/${VERSION}/${ARCHIVE}"
|
||||
echo "Extracting..."
|
||||
if [[ "$archive" == *.zip ]]; then
|
||||
unzip -q "$tmpdir/$archive" -d "$tmpdir"
|
||||
else
|
||||
tar -xf "$tmpdir/$archive" -C "$tmpdir"
|
||||
fi
|
||||
|
||||
echo "Downloading $URL"
|
||||
curl -L "$URL" -o "$TMPDIR/$ARCHIVE"
|
||||
mkdir -p "$dest"
|
||||
|
||||
echo "Extracting..."
|
||||
tar -xf "$TMPDIR/$ARCHIVE" -C "$TMPDIR"
|
||||
local tor_bin=$(find "$tmpdir" -type f -name "$exe" | head -n 1)
|
||||
local obfs_bin=$(find "$tmpdir" -type f \( -name "lyrebird*" -o -name "obfs4proxy*" \) | head -n 1)
|
||||
|
||||
DEST="$(dirname "$0")/../voxvera/resources/tor/$PLATFORM"
|
||||
mkdir -p "$DEST"
|
||||
if [[ -z "$tor_bin" || -z "$obfs_bin" ]]; then
|
||||
echo "Failed to locate tor or lyrebird/obfs4proxy in archive for $platform" >&2
|
||||
rm -rf "$tmpdir"
|
||||
return 1
|
||||
fi
|
||||
|
||||
TOR_BIN=$(find "$TMPDIR" -type f -name "$EXE" | head -n 1)
|
||||
OBFS_BIN=$(find "$TMPDIR" -type f \( -name "lyrebird*" -o -name "obfs4proxy*" \) | head -n 1)
|
||||
echo "Installing binaries to $dest"
|
||||
cp "$tor_bin" "$dest/$(basename "$exe")"
|
||||
cp "$obfs_bin" "$dest/$(basename "$obfs_bin")"
|
||||
|
||||
if [[ -z "$TOR_BIN" || -z "$OBFS_BIN" ]]; then
|
||||
echo "Failed to locate tor or lyrebird/obfs4proxy in archive" >&2
|
||||
exit 1
|
||||
fi
|
||||
chmod +x "$dest/$(basename "$exe")" "$dest/$(basename "$obfs_bin")"
|
||||
|
||||
# If it's lyrebird, create a symlink for obfs4proxy for compatibility
|
||||
if [[ "$(basename "$obfs_bin")" == "lyrebird"* ]]; then
|
||||
local obfs_name="obfs4proxy"
|
||||
[[ "$platform" == "win" ]] && obfs_name="obfs4proxy.exe"
|
||||
echo "Creating symlink $obfs_name -> $(basename "$obfs_bin")"
|
||||
(cd "$dest" && rm -f "$obfs_name" && ln -s "$(basename "$obfs_bin")" "$obfs_name")
|
||||
fi
|
||||
|
||||
cp "$TOR_BIN" "$DEST/$(basename "$EXE")"
|
||||
cp "$OBFS_BIN" "$DEST/$(basename "$OBFS_BIN")"
|
||||
rm -rf "$tmpdir"
|
||||
}
|
||||
|
||||
chmod +x "$DEST/$(basename "$EXE")" "$DEST/$(basename "$OBFS_BIN")"
|
||||
# Linux x86_64
|
||||
download_platform "linux" "x86_64" "tor-expert-bundle-linux-x86_64-${VERSION}.tar.gz" "tor"
|
||||
|
||||
echo "Installed binaries to $DEST"
|
||||
# macOS x86_64 (Intel) - works on M1/M2 via Rosetta if needed, but expert bundle usually has universal or specific
|
||||
download_platform "mac" "x86_64" "tor-expert-bundle-macos-x86_64-${VERSION}.tar.gz" "tor"
|
||||
|
||||
# Windows x86_64
|
||||
download_platform "win" "x86_64" "tor-expert-bundle-windows-x86_64-${VERSION}.tar.gz" "tor.exe"
|
||||
|
||||
echo "All platforms updated."
|
||||
@@ -10,15 +10,29 @@ check_cmd() {
|
||||
}
|
||||
|
||||
# Ensure pip is available
|
||||
if ! check_cmd pip3 && ! check_cmd pip; then
|
||||
echo "pip not found. Install Python 3 and pip first."
|
||||
if ! check_cmd pip3 && ! check_cmd pip && ! check_cmd pipx; then
|
||||
echo "pip or pipx not found. Install Python 3 and pip/pipx first."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PIP=$(check_cmd pip3 && echo pip3 || echo pip)
|
||||
|
||||
# Install Python packages used by the CLI and build pipeline
|
||||
$PIP install --user InquirerPy rich qrcode Pillow jsmin htmlmin2 pypdf
|
||||
if check_cmd pipx; then
|
||||
echo "Using pipx/pip for dependency installation..."
|
||||
# For development, we might want to install in a local venv instead of pipx
|
||||
# but for simple setup, pipx is safest on modern systems.
|
||||
# However, setup.sh usually implies installing into the current env for development.
|
||||
PIP_OPTS="--user --upgrade"
|
||||
if [ -f /etc/debian_version ]; then
|
||||
DEB_VER=$(cat /etc/debian_version | cut -d. -f1)
|
||||
if [[ "$DEB_VER" =~ ^[0-9]+$ ]] && [ "$DEB_VER" -ge 12 ]; then
|
||||
PIP_OPTS="$PIP_OPTS --break-system-packages"
|
||||
fi
|
||||
fi
|
||||
pip3 install $PIP_OPTS InquirerPy rich qrcode Pillow jsmin htmlmin2 pypdf wcwidth || \
|
||||
pip install $PIP_OPTS InquirerPy rich qrcode Pillow jsmin htmlmin2 pypdf wcwidth
|
||||
else
|
||||
PIP=$(check_cmd pip3 && echo pip3 || echo pip)
|
||||
$PIP install --user --upgrade InquirerPy rich qrcode Pillow jsmin htmlmin2 pypdf wcwidth
|
||||
fi
|
||||
|
||||
echo "All dependencies are installed."
|
||||
echo "Run 'voxvera check' to verify your setup."
|
||||
Binary file not shown.
+74
-27
@@ -2,8 +2,6 @@
|
||||
# --------------------------------------------------------------------
|
||||
# VoxVera all-in-one installer (Linux) – run once as a normal user.
|
||||
# Installs Tor + OnionShare and VoxVera itself.
|
||||
# Build dependencies (QR, HTML minification, PDF parsing) are Python
|
||||
# packages installed automatically — no Node.js or ImageMagick needed.
|
||||
# --------------------------------------------------------------------
|
||||
set -euo pipefail
|
||||
|
||||
@@ -14,46 +12,92 @@ msg() { printf "\e[32m%s\e[0m\n" "$*"; }
|
||||
warn() { printf "\e[33m%s\e[0m\n" "$*" >&2; }
|
||||
die() { printf "\e[31mERROR: %s\e[0m\n" "$*" >&2; exit 1; }
|
||||
|
||||
# Detect sudo/root
|
||||
if command_exists sudo; then
|
||||
SUDO="sudo"
|
||||
elif [ "$(id -u)" -eq 0 ]; then
|
||||
SUDO=""
|
||||
else
|
||||
die "This script requires root privileges. Please install sudo or run as root."
|
||||
fi
|
||||
|
||||
## -------- determine package manager --------------------------------
|
||||
if command_exists apt-get; then PM=apt ; UPDATE="sudo apt-get update" ; INSTALL="sudo apt-get install -y"
|
||||
elif command_exists dnf; then PM=dnf ; UPDATE="sudo dnf -y makecache" ; INSTALL="sudo dnf install -y"
|
||||
elif command_exists yum; then PM=yum ; UPDATE="sudo yum makecache" ; INSTALL="sudo yum install -y"
|
||||
elif command_exists pacman; then PM=pacman; UPDATE="sudo pacman -Sy" ; INSTALL="sudo pacman -S --noconfirm"
|
||||
elif command_exists apk; then PM=apk ; UPDATE="sudo apk update" ; INSTALL="sudo apk add --no-cache"
|
||||
if command_exists apt-get; then PM=apt ; UPDATE="$SUDO apt-get update" ; INSTALL="$SUDO apt-get install -y"
|
||||
elif command_exists dnf; then PM=dnf ; UPDATE="$SUDO dnf -y makecache" ; INSTALL="$SUDO dnf install -y"
|
||||
elif command_exists yum; then PM=yum ; UPDATE="$SUDO yum makecache" ; INSTALL="$SUDO yum install -y"
|
||||
elif command_exists pacman; then PM=pacman; UPDATE="$SUDO pacman -Sy" ; INSTALL="$SUDO pacman -S --noconfirm"
|
||||
elif command_exists apk; then PM=apk ; UPDATE="$SUDO apk update" ; INSTALL="$SUDO apk add --no-cache"
|
||||
else die "Unsupported distro – can't find apt/yum/dnf/pacman/apk."
|
||||
fi
|
||||
|
||||
msg ">> Detected package manager: $PM"
|
||||
|
||||
## -------- install system packages ----------------------------------
|
||||
# Only runtime dependencies for Tor hosting are needed as system packages.
|
||||
# Build tools (QR, minification, PDF parsing) are all Python packages now.
|
||||
SYSTEM_PKGS=(tor curl)
|
||||
# OnionShare package name differs per repo:
|
||||
[[ $PM =~ (apt|dnf|yum) ]] && SYSTEM_PKGS+=(onionshare-cli) || SYSTEM_PKGS+=(onionshare)
|
||||
|
||||
msg ">> Installing system dependencies..."
|
||||
SYSTEM_PKGS=(tor curl git python3-pip python3-venv)
|
||||
$UPDATE && $INSTALL "${SYSTEM_PKGS[@]}"
|
||||
|
||||
# Onionshare-cli - ensure it's installed and working (always attempt update if using pipx)
|
||||
msg ">> Ensuring onionshare-cli is installed and up-to-date..."
|
||||
if command_exists pipx && (pipx list | grep -q onionshare || ! command_exists onionshare-cli); then
|
||||
msg "Installing/Updating onionshare-cli via pipx..."
|
||||
pipx install --force git+https://github.com/onionshare/onionshare.git#subdirectory=cli || warn "pipx OnionShare install/update failed"
|
||||
elif ! command_exists onionshare-cli && ! command_exists onionshare; then
|
||||
msg "Attempting to install OnionShare..."
|
||||
OS_PKG="onionshare-cli"
|
||||
[[ ! $PM =~ (apt|dnf|yum) ]] && OS_PKG="onionshare"
|
||||
|
||||
if ! $INSTALL "$OS_PKG"; then
|
||||
warn "OnionShare not found in system repositories. Attempting pipx install fallback."
|
||||
if ! command_exists pipx; then
|
||||
msg "Installing pipx..."
|
||||
$INSTALL pipx
|
||||
pipx ensurepath --force
|
||||
export PATH="$HOME/.local/bin:$PATH"
|
||||
fi
|
||||
if command_exists pipx; then
|
||||
msg "Installing/Updating onionshare-cli via pipx..."
|
||||
pipx install --force git+https://github.com/onionshare/onionshare.git#subdirectory=cli || warn "pipx OnionShare install failed"
|
||||
else
|
||||
warn "pipx not found. Skipping OnionShare installation."
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
## -------- install VoxVera (prefers pipx, falls back) ---------------
|
||||
install_voxvera() {
|
||||
if command_exists pipx; then
|
||||
pipx install --force 'voxvera@git+https://github.com/PR0M3TH3AN/VoxVera.git@main' && return 0
|
||||
msg "Installing/Re-installing VoxVera via pipx..."
|
||||
if pipx install --force 'voxvera@git+https://github.com/PR0M3TH3AN/VoxVera.git@main'; then
|
||||
pipx ensurepath --force
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
# fallback: binary
|
||||
local install_dir="$HOME/.local/bin"
|
||||
mkdir -p "$install_dir"
|
||||
local url latest dest="$install_dir/voxvera"
|
||||
url=$(curl -fsSLI -o /dev/null -w '%{url_effective}' \
|
||||
https://github.com/PR0M3TH3AN/VoxVera/releases/latest)
|
||||
latest="${url%/}" ; latest="${latest##*/}" # vX.Y.Z tag
|
||||
url="https://github.com/PR0M3TH3AN/VoxVera/releases/download/${latest/\/}/voxvera-linux"
|
||||
if curl -fsSL "$url" -o "$dest"; then
|
||||
chmod +x "$dest"
|
||||
PATH="$install_dir:$PATH"
|
||||
return 0
|
||||
if command_exists curl; then
|
||||
msg "Downloading VoxVera binary..."
|
||||
url=$(curl -fsSLI -o /dev/null -w '%{url_effective}' \
|
||||
https://github.com/PR0M3TH3AN/VoxVera/releases/latest)
|
||||
latest="${url%/}" ; latest="${latest##*/}" # vX.Y.Z tag
|
||||
url="https://github.com/PR0M3TH3AN/VoxVera/releases/download/${latest/\/}/voxvera-linux"
|
||||
if curl -fsSL "$url" -o "$dest"; then
|
||||
chmod +x "$dest"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
# fallback: pip
|
||||
pip install --user 'voxvera@git+https://github.com/PR0M3TH3AN/VoxVera.git@main'
|
||||
msg "Installing/Updating VoxVera via pip..."
|
||||
PIP_OPTS="--user --upgrade"
|
||||
if [ -f /etc/debian_version ]; then
|
||||
DEB_VER=$(cat /etc/debian_version | cut -d. -f1)
|
||||
if [[ "$DEB_VER" =~ ^[0-9]+$ ]] && [ "$DEB_VER" -ge 12 ]; then
|
||||
PIP_OPTS="$PIP_OPTS --break-system-packages"
|
||||
fi
|
||||
fi
|
||||
pip install $PIP_OPTS 'voxvera@git+https://github.com/PR0M3TH3AN/VoxVera.git@main'
|
||||
}
|
||||
|
||||
msg ">> Installing VoxVera"
|
||||
@@ -70,13 +114,16 @@ EOF
|
||||
|
||||
# enable + start tor via systemctl if available
|
||||
if command_exists systemctl; then
|
||||
sudo systemctl enable tor || true
|
||||
sudo systemctl restart tor
|
||||
$SUDO systemctl enable tor || true
|
||||
$SUDO systemctl restart tor || true
|
||||
fi
|
||||
|
||||
msg "------------------------------------------------------------------"
|
||||
msg " VoxVera install finished!"
|
||||
msg " Tor rc file : $TOR_DIR/torrc"
|
||||
msg " Make sure \$HOME/.local/bin is in your PATH."
|
||||
msg " Run 'voxvera check' to verify your setup."
|
||||
msg ""
|
||||
msg " IMPORTANT: Please restart your terminal or run 'source ~/.bashrc'"
|
||||
msg " (or your shell config) to use 'voxvera'."
|
||||
msg ""
|
||||
msg " Run 'voxvera check' to verify your setup after refreshing."
|
||||
msg "------------------------------------------------------------------"
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
__version__ = "0.1.8"
|
||||
__version__ = "0.2.0"
|
||||
|
||||
# Add the vendor directory to sys.path as a fallback.
|
||||
# For standard installs (pip/pipx), the installed dependencies in the venv
|
||||
|
||||
+54
-15
@@ -22,16 +22,23 @@ CURRENT_LOCALE = {}
|
||||
|
||||
def _template_res(*parts) -> Traversable:
|
||||
"""Return a Traversable for files under the packaged ``templates`` folder."""
|
||||
if getattr(sys, 'frozen', False):
|
||||
# PyInstaller bundle: use sys._MEIPASS
|
||||
return Path(sys._MEIPASS).joinpath("voxvera", "templates", *parts)
|
||||
return resources.files(__package__).joinpath("templates", *parts)
|
||||
|
||||
|
||||
def _src_res(*parts) -> Traversable:
|
||||
"""Return a Traversable for files under the packaged ``src`` folder."""
|
||||
if getattr(sys, 'frozen', False):
|
||||
return Path(sys._MEIPASS).joinpath("voxvera", "src", *parts)
|
||||
return resources.files(__package__).joinpath("src", *parts)
|
||||
|
||||
|
||||
def _locale_res(*parts) -> Traversable:
|
||||
"""Return a Traversable for files under the packaged ``locales`` folder."""
|
||||
if getattr(sys, 'frozen', False):
|
||||
return Path(sys._MEIPASS).joinpath("voxvera", "locales", *parts)
|
||||
return resources.files(__package__).joinpath("locales", *parts)
|
||||
|
||||
|
||||
@@ -39,13 +46,22 @@ def load_locale(lang_code: str = "en"):
|
||||
"""Load the specified locale JSON file."""
|
||||
global CURRENT_LOCALE
|
||||
try:
|
||||
with resources.as_file(_locale_res(f"{lang_code}.json")) as locale_file:
|
||||
res = _locale_res(f"{lang_code}.json")
|
||||
if getattr(sys, 'frozen', False):
|
||||
# When frozen, we already have a Path object
|
||||
locale_file = res
|
||||
if not locale_file.exists():
|
||||
lang_code = "en" # Fallback
|
||||
with resources.as_file(_locale_res("en.json")) as fallback_file:
|
||||
CURRENT_LOCALE = json.loads(fallback_file.read_text(encoding="utf-8"))
|
||||
else:
|
||||
CURRENT_LOCALE = json.loads(locale_file.read_text(encoding="utf-8"))
|
||||
lang_code = "en"
|
||||
locale_file = _locale_res("en.json")
|
||||
CURRENT_LOCALE = json.loads(locale_file.read_text(encoding="utf-8"))
|
||||
else:
|
||||
with resources.as_file(res) as locale_file:
|
||||
if not locale_file.exists():
|
||||
lang_code = "en" # Fallback
|
||||
with resources.as_file(_locale_res("en.json")) as fallback_file:
|
||||
CURRENT_LOCALE = json.loads(fallback_file.read_text(encoding="utf-8"))
|
||||
else:
|
||||
CURRENT_LOCALE = json.loads(locale_file.read_text(encoding="utf-8"))
|
||||
except Exception as e:
|
||||
print(f"Error loading locale {lang_code}: {e}")
|
||||
# Final safety fallback to empty dict to avoid KeyErrors
|
||||
@@ -575,8 +591,15 @@ def _internal_onionshare():
|
||||
# Try to add bundled Tor to PATH so OnionShare can find it
|
||||
system = platform.system().lower()
|
||||
platform_dir = "win" if "windows" in system else "mac" if "darwin" in system else "linux"
|
||||
tor_dir = ROOT / "resources" / "tor" / platform_dir
|
||||
if tor_dir.exists():
|
||||
|
||||
if getattr(sys, 'frozen', False):
|
||||
tor_dir = Path(sys._MEIPASS).joinpath("voxvera", "resources", "tor", platform_dir)
|
||||
else:
|
||||
tor_dir = ROOT / "resources" / "tor" / platform_dir
|
||||
|
||||
# Check if bundled Tor is a real binary and not a placeholder (size > 100 bytes)
|
||||
bundled_tor = tor_dir / ("tor.exe" if "windows" in system else "tor")
|
||||
if bundled_tor.exists() and bundled_tor.stat().st_size > 100:
|
||||
os.environ["PATH"] = str(tor_dir) + os.pathsep + os.environ.get("PATH", "")
|
||||
|
||||
try:
|
||||
@@ -808,9 +831,15 @@ def import_multiple_sites(source_dir: str = None):
|
||||
|
||||
def build_docs():
|
||||
"""Generate localized documentation from templates and locale data."""
|
||||
template_dir = ROOT.parent / "docs" / "templates"
|
||||
if getattr(sys, 'frozen', False):
|
||||
template_dir = Path(sys._MEIPASS).joinpath("docs", "templates")
|
||||
doc_root = Path(sys._MEIPASS).joinpath("docs")
|
||||
else:
|
||||
template_dir = ROOT.parent / "docs" / "templates"
|
||||
doc_root = ROOT.parent / "docs"
|
||||
|
||||
if not template_dir.exists():
|
||||
print("Error: docs/templates/ not found.")
|
||||
print(f"Error: {template_dir} not found.")
|
||||
return
|
||||
|
||||
# List all supported languages
|
||||
@@ -820,7 +849,7 @@ def build_docs():
|
||||
for lang in langs:
|
||||
print(f"Generating documentation for: {lang}")
|
||||
load_locale(lang)
|
||||
dest_dir = ROOT.parent / "docs" / lang
|
||||
dest_dir = doc_root / lang
|
||||
os.makedirs(dest_dir, exist_ok=True)
|
||||
|
||||
# Process each template
|
||||
@@ -855,14 +884,18 @@ def build_docs():
|
||||
|
||||
def build_site():
|
||||
"""Refresh the main site/ directory with the latest template, QRs, and portable bundle."""
|
||||
site_dir = ROOT.parent / "site"
|
||||
if getattr(sys, 'frozen', False):
|
||||
site_dir = Path(sys._MEIPASS).joinpath("site")
|
||||
else:
|
||||
site_dir = ROOT.parent / "site"
|
||||
|
||||
if not site_dir.exists():
|
||||
print("Error: site/ directory not found.")
|
||||
print(f"Error: {site_dir} directory not found.")
|
||||
return
|
||||
|
||||
config_path = site_dir / "config.json"
|
||||
if not config_path.exists():
|
||||
print("Error: site/config.json not found.")
|
||||
print(f"Error: {config_path} not found.")
|
||||
return
|
||||
|
||||
print("Refreshing site/ assets...")
|
||||
@@ -873,9 +906,15 @@ def build_site():
|
||||
|
||||
# 2. Build the HTML from master template
|
||||
data = load_config(config_path)
|
||||
with resources.as_file(_src_res("index-master.html")) as template_path:
|
||||
res = _src_res("index-master.html")
|
||||
if getattr(sys, 'frozen', False):
|
||||
template_path = res
|
||||
with open(template_path, "r") as fh:
|
||||
html = fh.read()
|
||||
else:
|
||||
with resources.as_file(res) as template_path:
|
||||
with open(template_path, "r") as fh:
|
||||
html = fh.read()
|
||||
|
||||
# Bundle locales (Include landing tokens for the main site)
|
||||
all_locales = {}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
placeholder
|
||||
+1
@@ -0,0 +1 @@
|
||||
lyrebird
|
||||
Binary file not shown.
Executable
BIN
Binary file not shown.
@@ -1 +0,0 @@
|
||||
placeholder
|
||||
+1
@@ -0,0 +1 @@
|
||||
lyrebird
|
||||
Binary file not shown.
Executable
BIN
Binary file not shown.
@@ -1 +0,0 @@
|
||||
placeholder
|
||||
@@ -0,0 +1 @@
|
||||
lyrebird.exe
|
||||
Binary file not shown.
Reference in new issue
Block a user