Merge pull request #1 from outis1one/claude/create-bash-installer-GSmST
Add single-file bash installer for OpenWhispr
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Report a problem with the installer
|
||||
title: "[Bug] "
|
||||
labels: bug
|
||||
assignees: ""
|
||||
---
|
||||
|
||||
## Describe the bug
|
||||
|
||||
A clear and concise description of what went wrong.
|
||||
|
||||
## Steps to reproduce
|
||||
|
||||
1. Run `bash setup-openwhispr.sh`
|
||||
2. Choose option ...
|
||||
3. See error ...
|
||||
|
||||
## Expected behaviour
|
||||
|
||||
What you expected to happen.
|
||||
|
||||
## Environment
|
||||
|
||||
- **Distro**: (e.g., Ubuntu 24.04)
|
||||
- **Architecture**: (e.g., x86_64 / aarch64)
|
||||
- **Session type**: (X11 / Wayland)
|
||||
- **Desktop**: (GNOME / KDE / other)
|
||||
- **OpenWhispr version**: (if already installed)
|
||||
- **Script version**: (run `head -5 setup-openwhispr.sh`)
|
||||
|
||||
## Terminal output
|
||||
|
||||
```
|
||||
Paste relevant output here
|
||||
```
|
||||
|
||||
## Additional context
|
||||
|
||||
Anything else that might help.
|
||||
@@ -0,0 +1,33 @@
|
||||
---
|
||||
name: Distro compatibility report
|
||||
about: Report whether the installer works on your distro
|
||||
title: "[Distro] "
|
||||
labels: distro-report
|
||||
assignees: ""
|
||||
---
|
||||
|
||||
## Distribution details
|
||||
|
||||
- **Distro**: (e.g., Pop!_OS 24.04)
|
||||
- **Based on**: (e.g., Ubuntu / Debian / Arch / independent)
|
||||
- **Architecture**: (x86_64 / aarch64)
|
||||
- **Session type**: (X11 / Wayland)
|
||||
- **Desktop environment**: (GNOME / KDE / Sway / other)
|
||||
|
||||
## Result
|
||||
|
||||
- [ ] Script detected distro correctly
|
||||
- [ ] OpenWhispr installed successfully
|
||||
- [ ] Paste dependencies installed correctly
|
||||
- [ ] API keys stored correctly
|
||||
- [ ] OpenWhispr launched after install
|
||||
|
||||
## Notes
|
||||
|
||||
Any issues, workarounds, or suggestions for this distro.
|
||||
|
||||
## Terminal output
|
||||
|
||||
```
|
||||
Paste full script output here
|
||||
```
|
||||
@@ -0,0 +1,19 @@
|
||||
## What does this PR do?
|
||||
|
||||
Brief description of the change.
|
||||
|
||||
## Checklist
|
||||
|
||||
- [ ] `bash -n setup-openwhispr.sh` passes (no syntax errors)
|
||||
- [ ] ShellCheck passes with no new warnings
|
||||
- [ ] Tested on at least one supported distro
|
||||
- [ ] Updated CHANGELOG.md if user-facing
|
||||
- [ ] No hardcoded paths or usernames (uses YOUR_USERNAME placeholder or is generic)
|
||||
|
||||
## Testing
|
||||
|
||||
Describe how you tested this change.
|
||||
|
||||
## Related issues
|
||||
|
||||
Closes #
|
||||
@@ -0,0 +1,23 @@
|
||||
name: ShellCheck
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
shellcheck:
|
||||
name: ShellCheck
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install ShellCheck
|
||||
run: sudo apt-get install -y shellcheck
|
||||
|
||||
- name: Run ShellCheck
|
||||
run: shellcheck -x setup-openwhispr.sh
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Editor
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
.vscode/
|
||||
.idea/
|
||||
|
||||
# Temp
|
||||
/tmp/
|
||||
*.bak
|
||||
*.tmp
|
||||
@@ -0,0 +1,24 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/).
|
||||
|
||||
## [1.0.0] — 2026-03-25
|
||||
|
||||
### Added
|
||||
|
||||
- Single-file bash installer `setup-openwhispr.sh`
|
||||
- Automatic distro detection (Ubuntu, Debian, Mint, Fedora, openSUSE, Arch)
|
||||
- Architecture detection (amd64, arm64)
|
||||
- GitHub releases integration — fetches latest OpenWhispr automatically
|
||||
- Paste dependency detection (xdotool / kdotool / wtype / D-Bus)
|
||||
- STT provider selection: local Whisper/Parakeet, Groq, OpenAI Realtime
|
||||
- AI cleanup provider selection: OpenAI, Anthropic, Groq, Ollama, skip
|
||||
- Safe API key storage in `~/.bashrc` (never overwrites existing keys)
|
||||
- Idempotent — safe to re-run, skips already-installed versions
|
||||
- ShellCheck CI via GitHub Actions
|
||||
- README with cost table, privacy table, and one-command install
|
||||
- Issue templates for bug reports and distro compatibility reports
|
||||
- Pull request template
|
||||
@@ -0,0 +1,88 @@
|
||||
# GitHub Repository Setup Guide
|
||||
|
||||
Step-by-step instructions to publish this project on GitHub.
|
||||
|
||||
## 1. Create a new GitHub repository
|
||||
|
||||
Go to [github.com/new](https://github.com/new) and create a repository:
|
||||
|
||||
- **Name**: `openwhispr-easy-setup`
|
||||
- **Description**: `One-command installer for OpenWhispr on Linux — distro detection, dependencies, API keys, first-run setup`
|
||||
- **Visibility**: Public
|
||||
- **Do NOT** initialize with README, .gitignore, or license (we already have them)
|
||||
|
||||
## 2. Push local files
|
||||
|
||||
```bash
|
||||
cd openwhispr-easy-setup
|
||||
git init
|
||||
git add .
|
||||
git commit -m "Initial release v1.0.0"
|
||||
git branch -M main
|
||||
git remote add origin https://github.com/YOUR_USERNAME/openwhispr-easy-setup.git
|
||||
git push -u origin main
|
||||
```
|
||||
|
||||
## 3. Replace YOUR_USERNAME placeholder
|
||||
|
||||
Replace `YOUR_USERNAME` with your actual GitHub username in all files:
|
||||
|
||||
```bash
|
||||
sed -i 's/YOUR_USERNAME/your-actual-username/g' setup-openwhispr.sh README.md GITHUB_SETUP.md LICENSE
|
||||
```
|
||||
|
||||
Then commit:
|
||||
|
||||
```bash
|
||||
git add -A
|
||||
git commit -m "Replace YOUR_USERNAME with actual username"
|
||||
git push
|
||||
```
|
||||
|
||||
## 4. Tag the v1.0.0 release
|
||||
|
||||
```bash
|
||||
git tag -a v1.0.0 -m "v1.0.0 — Initial release"
|
||||
git push origin v1.0.0
|
||||
```
|
||||
|
||||
Then go to **Releases → Draft a new release**:
|
||||
- **Tag**: `v1.0.0`
|
||||
- **Title**: `v1.0.0 — Initial release`
|
||||
- **Description**: Copy the [CHANGELOG.md](CHANGELOG.md) entries for v1.0.0
|
||||
- Click **Publish release**
|
||||
|
||||
## 5. Add GitHub topics
|
||||
|
||||
Go to your repository page → click the gear icon next to **About** → add these topics:
|
||||
|
||||
```
|
||||
linux
|
||||
voice-dictation
|
||||
openwhispr
|
||||
speech-to-text
|
||||
whisper
|
||||
easy-install
|
||||
```
|
||||
|
||||
## 6. Set repository description
|
||||
|
||||
In the same **About** section, set the description to:
|
||||
|
||||
> One-command installer for OpenWhispr on Linux — distro detection, dependencies, API keys, first-run setup
|
||||
|
||||
## 7. Verify
|
||||
|
||||
- [ ] README renders correctly with badges
|
||||
- [ ] ShellCheck CI runs on push (check **Actions** tab)
|
||||
- [ ] Issue templates appear when creating new issues
|
||||
- [ ] PR template appears when opening a pull request
|
||||
- [ ] `bash <(curl -fsSL https://raw.githubusercontent.com/YOUR_USERNAME/openwhispr-easy-setup/main/setup-openwhispr.sh)` works
|
||||
|
||||
## Done!
|
||||
|
||||
Share the one-liner with anyone who wants to install OpenWhispr:
|
||||
|
||||
```bash
|
||||
bash <(curl -fsSL https://raw.githubusercontent.com/YOUR_USERNAME/openwhispr-easy-setup/main/setup-openwhispr.sh)
|
||||
```
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 YOUR_USERNAME
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,114 @@
|
||||
# OpenWhispr Easy Setup
|
||||
|
||||
[](LICENSE)
|
||||
[](https://github.com/YOUR_USERNAME/openwhispr-easy-setup/actions/workflows/lint.yml)
|
||||
|
||||
**One-command installer for [OpenWhispr](https://github.com/OpenWhispr/openwhispr) on Linux** — distro detection, dependencies, API keys, first-run setup.
|
||||
|
||||
> *Your voice stays on your machine. Always. The only question is whether you want AI to clean up the text after transcription.*
|
||||
|
||||
## Quick install
|
||||
|
||||
```bash
|
||||
bash <(curl -fsSL https://raw.githubusercontent.com/YOUR_USERNAME/openwhispr-easy-setup/main/setup-openwhispr.sh)
|
||||
```
|
||||
|
||||
Or clone and run:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/YOUR_USERNAME/openwhispr-easy-setup.git
|
||||
cd openwhispr-easy-setup
|
||||
bash setup-openwhispr.sh
|
||||
```
|
||||
|
||||
## What it does
|
||||
|
||||
| Step | Action | Details |
|
||||
|------|--------|---------|
|
||||
| 1/8 | **Intro** | Explains OpenWhispr and the privacy model |
|
||||
| 2/8 | **Detect distro + arch** | Ubuntu, Debian, Mint, Fedora, openSUSE, Arch — amd64/arm64 |
|
||||
| 3/8 | **Install OpenWhispr** | Fetches latest release from GitHub, picks correct package |
|
||||
| 4/8 | **Paste dependencies** | xdotool (X11), wtype (Wayland), kdotool (KDE), or D-Bus (GNOME) |
|
||||
| 5/8 | **STT provider** | Local Whisper/Parakeet, Groq, or OpenAI Realtime |
|
||||
| 6/8 | **AI cleanup** | OpenAI, Anthropic, Groq, Ollama, or skip |
|
||||
| 7/8 | **First launch** | Opens OpenWhispr for model download and hotkey setup |
|
||||
| 8/8 | **Summary** | Prints config, useful commands, and next steps |
|
||||
|
||||
## STT options
|
||||
|
||||
| Option | Method | Streaming | Cost | Audio sent to |
|
||||
|--------|--------|-----------|------|---------------|
|
||||
| 1 (default) | Local Whisper/Parakeet | No | Free | Nobody, ever |
|
||||
| 2 | Groq whisper-large-v3-turbo | No | $0.04/hr* | Groq servers |
|
||||
| 3 | OpenAI Realtime API | Yes | $0.06/min | OpenAI servers |
|
||||
|
||||
\*Groq free tier: ~2,000 audio seconds/day.
|
||||
|
||||
## AI cleanup options
|
||||
|
||||
| Provider | Model | Per use | 50/day/mo | 200/day/mo |
|
||||
|----------|-------|---------|-----------|------------|
|
||||
| OpenAI | gpt-4o-mini-2024-07-18 | $0.000058 | $0.09 | $0.35 |
|
||||
| Anthropic | claude-haiku-4-5 | $0.000450 | $0.68 | $2.70 |
|
||||
| Groq | llama-3.3-70b-versatile | Free* | Free* | Free* |
|
||||
| Ollama | Local (fully offline) | Free | Free | Free |
|
||||
| Skip | Raw transcription only | Free | Free | Free |
|
||||
|
||||
\*Groq free: 1,000 req/day, 100K tokens/day.
|
||||
|
||||
## Privacy
|
||||
|
||||
| Data | Where it goes | Condition |
|
||||
|------|---------------|-----------|
|
||||
| **Audio** | Stays on your machine | Default (local Whisper/Parakeet) |
|
||||
| **Audio** | Groq or OpenAI servers | Only if you choose cloud STT |
|
||||
| **Text** | AI cleanup provider | Only if you choose AI cleanup |
|
||||
| **API keys** | `~/.bashrc` on your machine | Never sent anywhere by this script |
|
||||
|
||||
No provider uses your data for training. Cloud data is deleted within 30 days.
|
||||
|
||||
## Useful commands
|
||||
|
||||
```bash
|
||||
openwhispr # Launch the app
|
||||
openwhispr --help # Show options
|
||||
source ~/.bashrc # Reload API keys in current shell
|
||||
```
|
||||
|
||||
In the app:
|
||||
- **Settings → Hotkey** — change your hotkey (default: backtick `` ` ``)
|
||||
- **Settings → Models** — download better speech models
|
||||
- **Settings → Processing** — change STT or AI provider
|
||||
|
||||
## Supported distros
|
||||
|
||||
| Distro | Package format | Status |
|
||||
|--------|---------------|--------|
|
||||
| Ubuntu / Debian / Mint | `.deb` | Supported |
|
||||
| Fedora | `.rpm` (dnf) | Supported |
|
||||
| openSUSE | `.rpm` (zypper) | Supported |
|
||||
| Arch / Manjaro / EndeavourOS | `.tar.gz` | Supported |
|
||||
|
||||
Architecture: `x86_64` (amd64) and `aarch64` (arm64).
|
||||
|
||||
## Requirements
|
||||
|
||||
- **bash 4+**
|
||||
- **curl** — for downloading
|
||||
- **jq** — for parsing GitHub API (script offers to install if missing)
|
||||
|
||||
## Safe to re-run
|
||||
|
||||
The script is idempotent:
|
||||
- Skips OpenWhispr install if the latest version is already present
|
||||
- Never overwrites existing API keys in `~/.bashrc`
|
||||
- Detects already-installed paste dependencies
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- [OpenWhispr Troubleshooting Guide](https://github.com/OpenWhispr/openwhispr/blob/main/TROUBLESHOOTING.md)
|
||||
- [Open an issue](https://github.com/YOUR_USERNAME/openwhispr-easy-setup/issues/new/choose)
|
||||
|
||||
## License
|
||||
|
||||
[MIT](LICENSE)
|
||||
Executable
+619
@@ -0,0 +1,619 @@
|
||||
#!/usr/bin/env bash
|
||||
# setup-openwhispr.sh — One-command installer for OpenWhispr on Linux
|
||||
# https://github.com/YOUR_USERNAME/openwhispr-easy-setup
|
||||
# License: MIT
|
||||
|
||||
# Do NOT use set -e — we handle errors explicitly
|
||||
|
||||
readonly SCRIPT_VERSION="1.0.0"
|
||||
readonly BASHRC="$HOME/.bashrc"
|
||||
readonly MARKER="# Added by openwhispr-easy-setup"
|
||||
readonly GITHUB_API="https://api.github.com/repos/OpenWhispr/openwhispr/releases/latest"
|
||||
readonly TMP_DIR="/tmp/openwhispr-install"
|
||||
|
||||
# ── Colours & formatting ─────────────────────────────────────────────────────
|
||||
|
||||
if [[ -t 1 ]]; then
|
||||
BOLD='\033[1m'
|
||||
DIM='\033[2m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[0;33m'
|
||||
RED='\033[0;31m'
|
||||
CYAN='\033[0;36m'
|
||||
RESET='\033[0m'
|
||||
else
|
||||
BOLD='' DIM='' GREEN='' YELLOW='' RED='' CYAN='' RESET=''
|
||||
fi
|
||||
|
||||
# ── Helper functions ──────────────────────────────────────────────────────────
|
||||
|
||||
# shellcheck disable=SC2059
|
||||
info() { printf "${GREEN}[✓]${RESET} %s\n" "$*"; }
|
||||
# shellcheck disable=SC2059
|
||||
warn() { printf "${YELLOW}[!]${RESET} %s\n" "$*"; }
|
||||
# shellcheck disable=SC2059
|
||||
err() { printf "${RED}[✗]${RESET} %s\n" "$*" >&2; }
|
||||
# shellcheck disable=SC2059
|
||||
step() { printf "\n${BOLD}${CYAN}[%s]${RESET} ${BOLD}%s${RESET}\n" "$1" "$2"; }
|
||||
# shellcheck disable=SC2059
|
||||
divider() { printf "${DIM}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}\n"; }
|
||||
|
||||
prompt_default() {
|
||||
# Usage: prompt_default "prompt text" "default" VARNAME
|
||||
local prompt="$1" default="$2" varname="$3" reply
|
||||
printf "%s [default: %s]: " "$prompt" "$default"
|
||||
read -r reply
|
||||
reply="${reply:-$default}"
|
||||
printf -v "$varname" '%s' "$reply"
|
||||
}
|
||||
|
||||
prompt_hidden() {
|
||||
# Usage: prompt_hidden "prompt text" VARNAME
|
||||
local prompt="$1" varname="$2" reply
|
||||
printf "%s: " "$prompt"
|
||||
read -rs reply
|
||||
printf "\n"
|
||||
printf -v "$varname" '%s' "$reply"
|
||||
}
|
||||
|
||||
store_api_key() {
|
||||
# Usage: store_api_key VARNAME value
|
||||
local keyname="$1" keyval="$2"
|
||||
if [[ -z "$keyval" ]]; then
|
||||
warn "Empty value for $keyname — skipping."
|
||||
return 1
|
||||
fi
|
||||
# Check if already present in bashrc
|
||||
if grep -q "^export ${keyname}=" "$BASHRC" 2>/dev/null; then
|
||||
info "$keyname already set in $BASHRC — not overwriting."
|
||||
else
|
||||
printf '\n%s\nexport %s="%s"\n' "$MARKER" "$keyname" "$keyval" >> "$BASHRC"
|
||||
info "$keyname stored in $BASHRC"
|
||||
fi
|
||||
# Also export for current session
|
||||
export "$keyname=$keyval"
|
||||
}
|
||||
|
||||
command_exists() { command -v "$1" >/dev/null 2>&1; }
|
||||
|
||||
# ── Variables set during the run ──────────────────────────────────────────────
|
||||
|
||||
DISTRO="" # debian, ubuntu, mint, fedora, arch, opensuse
|
||||
DISTRO_FAMILY="" # deb, rpm-dnf, rpm-zypper, arch
|
||||
ARCH="" # amd64, arm64
|
||||
SESSION_TYPE="" # x11, wayland
|
||||
DESKTOP="" # GNOME, KDE, etc.
|
||||
STT_CHOICE=""
|
||||
CLEANUP_CHOICE=""
|
||||
STT_LABEL=""
|
||||
CLEANUP_LABEL=""
|
||||
INSTALLED_VERSION=""
|
||||
LATEST_VERSION=""
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
# [1/8] Intro
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
step "1/8" "Welcome to OpenWhispr Easy Setup v${SCRIPT_VERSION}"
|
||||
divider
|
||||
cat <<'INTRO'
|
||||
OpenWhispr is an Electron-based voice dictation app for Linux.
|
||||
Press a hotkey, speak, and your words appear wherever the cursor is.
|
||||
|
||||
Your voice stays on your machine. Always.
|
||||
The only question is whether you want AI to clean up
|
||||
the text after transcription.
|
||||
|
||||
Speech-to-text (STT) is local Whisper/Parakeet by default.
|
||||
Audio never leaves your machine unless YOU explicitly choose a cloud option.
|
||||
|
||||
This script will:
|
||||
1. Detect your distro and architecture
|
||||
2. Install OpenWhispr (latest release)
|
||||
3. Install paste/typing dependencies
|
||||
4. Configure STT provider
|
||||
5. Configure AI text cleanup
|
||||
6. Launch OpenWhispr for first-run setup
|
||||
INTRO
|
||||
divider
|
||||
printf "\nPress Enter to continue or Ctrl-C to cancel... "
|
||||
read -r
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
# [2/8] Detect distro + arch
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
step "2/8" "Detecting distribution and architecture"
|
||||
|
||||
detect_distro() {
|
||||
if [[ -f /etc/os-release ]]; then
|
||||
# shellcheck source=/dev/null
|
||||
. /etc/os-release
|
||||
case "${ID,,}" in
|
||||
ubuntu) DISTRO="ubuntu"; DISTRO_FAMILY="deb" ;;
|
||||
debian) DISTRO="debian"; DISTRO_FAMILY="deb" ;;
|
||||
linuxmint) DISTRO="mint"; DISTRO_FAMILY="deb" ;;
|
||||
fedora) DISTRO="fedora"; DISTRO_FAMILY="rpm-dnf" ;;
|
||||
opensuse*|sles) DISTRO="opensuse"; DISTRO_FAMILY="rpm-zypper" ;;
|
||||
arch|manjaro|endeavouros) DISTRO="arch"; DISTRO_FAMILY="arch" ;;
|
||||
*)
|
||||
err "Unsupported distribution: ${ID} (${PRETTY_NAME:-unknown})"
|
||||
err "Supported: Ubuntu, Debian, Mint, Fedora, openSUSE, Arch"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
else
|
||||
err "Cannot detect distribution (/etc/os-release not found)."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
detect_arch() {
|
||||
local machine
|
||||
machine="$(uname -m)"
|
||||
case "$machine" in
|
||||
x86_64) ARCH="amd64" ;;
|
||||
aarch64) ARCH="arm64" ;;
|
||||
*)
|
||||
err "Unsupported architecture: $machine"
|
||||
err "Supported: x86_64 (amd64), aarch64 (arm64)"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
detect_distro
|
||||
detect_arch
|
||||
info "Distro: ${DISTRO} (${DISTRO_FAMILY}) | Arch: ${ARCH}"
|
||||
|
||||
# ── Check base dependencies ───────────────────────────────────────────────────
|
||||
|
||||
if ! command_exists curl; then
|
||||
err "curl is required but not installed."
|
||||
err "Install it with your package manager and re-run this script."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command_exists jq; then
|
||||
warn "jq is required but not installed."
|
||||
printf "Install jq now? [Y/n]: "
|
||||
read -r jq_reply
|
||||
jq_reply="${jq_reply:-Y}"
|
||||
if [[ "${jq_reply,,}" == "y" ]]; then
|
||||
case "$DISTRO_FAMILY" in
|
||||
deb) sudo apt update && sudo apt install -y jq ;;
|
||||
rpm-dnf) sudo dnf install -y jq ;;
|
||||
rpm-zypper) sudo zypper install -y jq ;;
|
||||
arch) sudo pacman -S --noconfirm jq ;;
|
||||
esac
|
||||
if ! command_exists jq; then
|
||||
err "Failed to install jq. Please install it manually and re-run."
|
||||
exit 1
|
||||
fi
|
||||
info "jq installed."
|
||||
else
|
||||
err "jq is required. Please install it and re-run."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
# [3/8] Install OpenWhispr
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
step "3/8" "Installing OpenWhispr"
|
||||
|
||||
# Check currently installed version
|
||||
get_installed_version() {
|
||||
if command_exists openwhispr; then
|
||||
openwhispr --version 2>/dev/null || true
|
||||
elif command_exists open-whispr; then
|
||||
open-whispr --version 2>/dev/null || true
|
||||
fi
|
||||
}
|
||||
|
||||
INSTALLED_VERSION="$(get_installed_version)"
|
||||
|
||||
# Fetch latest release info
|
||||
info "Fetching latest release info..."
|
||||
RELEASE_JSON="$(curl -sf "$GITHUB_API")" || {
|
||||
err "Failed to fetch release info from GitHub."
|
||||
err "Check your internet connection and try again."
|
||||
exit 1
|
||||
}
|
||||
|
||||
LATEST_VERSION="$(printf '%s' "$RELEASE_JSON" | jq -r '.tag_name // empty')"
|
||||
if [[ -z "$LATEST_VERSION" ]]; then
|
||||
err "Could not determine latest version from GitHub."
|
||||
exit 1
|
||||
fi
|
||||
info "Latest version: ${LATEST_VERSION}"
|
||||
|
||||
# Strip leading 'v' for comparison
|
||||
LATEST_CLEAN="${LATEST_VERSION#v}"
|
||||
INSTALLED_CLEAN="${INSTALLED_VERSION#v}"
|
||||
|
||||
if [[ -n "$INSTALLED_VERSION" && "$INSTALLED_CLEAN" == "$LATEST_CLEAN" ]]; then
|
||||
info "OpenWhispr ${LATEST_VERSION} is already installed — skipping."
|
||||
else
|
||||
if [[ -n "$INSTALLED_VERSION" ]]; then
|
||||
info "Upgrading from ${INSTALLED_VERSION} to ${LATEST_VERSION}..."
|
||||
fi
|
||||
|
||||
# Determine asset pattern
|
||||
case "$DISTRO_FAMILY" in
|
||||
deb) ASSET_EXT=".deb" ;;
|
||||
rpm-dnf|rpm-zypper) ASSET_EXT=".rpm" ;;
|
||||
arch) ASSET_EXT=".tar.gz" ;;
|
||||
esac
|
||||
|
||||
# Map arch names (assets may use different conventions)
|
||||
case "$ARCH" in
|
||||
amd64) ARCH_PATTERNS=("amd64" "x86_64" "x64") ;;
|
||||
arm64) ARCH_PATTERNS=("arm64" "aarch64") ;;
|
||||
esac
|
||||
|
||||
# Find matching asset URL
|
||||
ASSET_URL=""
|
||||
for arch_pat in "${ARCH_PATTERNS[@]}"; do
|
||||
ASSET_URL="$(printf '%s' "$RELEASE_JSON" | jq -r \
|
||||
--arg ext "$ASSET_EXT" --arg arch "$arch_pat" \
|
||||
'.assets[] | select(.name | (endswith($ext) and contains($arch))) | .browser_download_url' \
|
||||
| head -1)"
|
||||
[[ -n "$ASSET_URL" ]] && break
|
||||
done
|
||||
|
||||
if [[ -z "$ASSET_URL" ]]; then
|
||||
err "No ${ASSET_EXT} asset found for ${ARCH} in release ${LATEST_VERSION}."
|
||||
err "Check: https://github.com/OpenWhispr/openwhispr/releases/latest"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ASSET_NAME="$(basename "$ASSET_URL")"
|
||||
info "Downloading ${ASSET_NAME}..."
|
||||
|
||||
mkdir -p "$TMP_DIR"
|
||||
curl --progress-bar -fL -o "${TMP_DIR}/${ASSET_NAME}" "$ASSET_URL" || {
|
||||
err "Download failed."
|
||||
exit 1
|
||||
}
|
||||
|
||||
info "Installing..."
|
||||
case "$DISTRO_FAMILY" in
|
||||
deb)
|
||||
sudo apt install -y "${TMP_DIR}/${ASSET_NAME}" || {
|
||||
err "Installation failed. Try: sudo dpkg -i ${TMP_DIR}/${ASSET_NAME}"
|
||||
exit 1
|
||||
}
|
||||
;;
|
||||
rpm-dnf)
|
||||
sudo dnf install -y "${TMP_DIR}/${ASSET_NAME}" || {
|
||||
err "Installation failed."
|
||||
exit 1
|
||||
}
|
||||
;;
|
||||
rpm-zypper)
|
||||
sudo zypper install -y "${TMP_DIR}/${ASSET_NAME}" || {
|
||||
err "Installation failed."
|
||||
exit 1
|
||||
}
|
||||
;;
|
||||
arch)
|
||||
sudo mkdir -p /opt/openwhispr
|
||||
sudo tar -xzf "${TMP_DIR}/${ASSET_NAME}" -C /opt/openwhispr || {
|
||||
err "Extraction failed."
|
||||
exit 1
|
||||
}
|
||||
# Find the binary and symlink
|
||||
local_bin="$(find /opt/openwhispr -maxdepth 2 -name 'openwhispr' -o -name 'open-whispr' 2>/dev/null | head -1)"
|
||||
if [[ -n "$local_bin" ]]; then
|
||||
sudo ln -sf "$local_bin" /usr/local/bin/openwhispr
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
# Clean up
|
||||
rm -rf "$TMP_DIR"
|
||||
info "OpenWhispr ${LATEST_VERSION} installed successfully."
|
||||
fi
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
# [4/8] Install paste dependencies
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
step "4/8" "Installing paste/typing dependencies"
|
||||
|
||||
SESSION_TYPE="${XDG_SESSION_TYPE:-}"
|
||||
DESKTOP="${XDG_CURRENT_DESKTOP:-}"
|
||||
|
||||
if [[ -z "$SESSION_TYPE" ]]; then
|
||||
# Try to detect
|
||||
if [[ -n "${WAYLAND_DISPLAY:-}" ]]; then
|
||||
SESSION_TYPE="wayland"
|
||||
elif [[ -n "${DISPLAY:-}" ]]; then
|
||||
SESSION_TYPE="x11"
|
||||
else
|
||||
warn "Cannot detect session type (X11/Wayland). Skipping paste deps."
|
||||
fi
|
||||
fi
|
||||
|
||||
info "Session: ${SESSION_TYPE:-unknown} | Desktop: ${DESKTOP:-unknown}"
|
||||
|
||||
install_paste_dep() {
|
||||
local pkg="$1"
|
||||
if command_exists "$pkg"; then
|
||||
info "$pkg is already installed."
|
||||
return 0
|
||||
fi
|
||||
info "Installing $pkg..."
|
||||
case "$DISTRO_FAMILY" in
|
||||
deb) sudo apt install -y "$pkg" ;;
|
||||
rpm-dnf) sudo dnf install -y "$pkg" ;;
|
||||
rpm-zypper) sudo zypper install -y "$pkg" ;;
|
||||
arch) sudo pacman -S --noconfirm "$pkg" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
case "$SESSION_TYPE" in
|
||||
x11)
|
||||
install_paste_dep "xdotool"
|
||||
;;
|
||||
wayland)
|
||||
if [[ "$DESKTOP" == *"GNOME"* ]]; then
|
||||
info "GNOME on Wayland — OpenWhispr uses D-Bus natively. No extra deps."
|
||||
elif [[ "$DESKTOP" == *"KDE"* ]]; then
|
||||
install_paste_dep "kdotool"
|
||||
else
|
||||
install_paste_dep "wtype"
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
warn "Skipping paste dependency install (unknown session type)."
|
||||
;;
|
||||
esac
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
# [5/8] STT provider
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
step "5/8" "Speech-to-text (STT) provider"
|
||||
|
||||
cat <<'STT_MENU'
|
||||
|
||||
How should OpenWhispr transcribe your voice?
|
||||
|
||||
Option Method Streaming Cost Audio sent to
|
||||
─────────────────────────────────────────────────────────────────────────────
|
||||
1) Local Whisper/Parakeet (default) no free nobody, ever
|
||||
2) Groq whisper-large-v3-turbo no $0.04/hr* Groq servers
|
||||
3) OpenAI Realtime API YES $0.06/min OpenAI servers
|
||||
|
||||
*Groq free tier: ~2,000 audio seconds/day (~600 dictations/day free).
|
||||
NOT used for training. 30-day retention max.
|
||||
Verify: https://console.groq.com/docs/your-data
|
||||
|
||||
Streaming (option 3) = text appears word-by-word as you speak.
|
||||
Options 1 and 2 transcribe after you stop speaking.
|
||||
|
||||
All cloud options send audio to a server (like Siri/Google Assistant)
|
||||
but unlike Siri/Google: NOT tied to your identity, NOT used for
|
||||
training, deleted within 30 days.
|
||||
|
||||
Best free combo: option 1 (local) + Groq cleanup [step 6]
|
||||
Best free cloud combo: option 2 (Groq STT) + Groq cleanup [same key]
|
||||
Best streaming: option 3 (OpenAI Realtime) + any cleanup
|
||||
|
||||
STT_MENU
|
||||
|
||||
prompt_default "Enter 1-3" "1" STT_CHOICE
|
||||
|
||||
case "$STT_CHOICE" in
|
||||
1)
|
||||
STT_LABEL="Local Whisper/Parakeet (offline)"
|
||||
info "STT: Local — audio never leaves your machine."
|
||||
;;
|
||||
2)
|
||||
STT_LABEL="Groq whisper-large-v3-turbo (cloud)"
|
||||
info "STT: Groq cloud transcription."
|
||||
if [[ -n "${GROQ_API_KEY:-}" ]]; then
|
||||
info "GROQ_API_KEY already set — reusing."
|
||||
else
|
||||
prompt_hidden "Enter your Groq API key (from https://console.groq.com)" GROQ_KEY_INPUT
|
||||
store_api_key "GROQ_API_KEY" "$GROQ_KEY_INPUT"
|
||||
fi
|
||||
;;
|
||||
3)
|
||||
STT_LABEL="OpenAI Realtime API (cloud, streaming)"
|
||||
info "STT: OpenAI Realtime (streaming)."
|
||||
if [[ -n "${OPENAI_API_KEY:-}" ]]; then
|
||||
info "OPENAI_API_KEY already set — reusing."
|
||||
else
|
||||
prompt_hidden "Enter your OpenAI API key (from https://platform.openai.com/api-keys)" OPENAI_KEY_INPUT
|
||||
store_api_key "OPENAI_API_KEY" "$OPENAI_KEY_INPUT"
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
warn "Invalid choice '$STT_CHOICE' — defaulting to local."
|
||||
STT_CHOICE="1"
|
||||
STT_LABEL="Local Whisper/Parakeet (offline)"
|
||||
;;
|
||||
esac
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
# [6/8] AI cleanup provider
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
step "6/8" "AI text cleanup provider"
|
||||
|
||||
cat <<'CLEANUP_MENU'
|
||||
|
||||
After transcription, AI can fix grammar, spelling, and filler words.
|
||||
Only transcribed TEXT is sent — your voice stays local regardless.
|
||||
None of these providers use your text for training.
|
||||
|
||||
Provider Model Per use 50/day/mo 200/day/mo
|
||||
──────────────────────────────────────────────────────────────────────────
|
||||
1) OpenAI gpt-4o-mini-2024-07-18 $0.000058 $0.09 $0.35
|
||||
2) Anthropic claude-haiku-4-5 $0.000450 $0.68 $2.70
|
||||
3) Groq llama-3.3-70b-versatile free* free* free*
|
||||
4) Local Ollama (fully offline) free free free
|
||||
5) Skip raw transcription only free free free
|
||||
|
||||
*Groq free: 1,000 req/day, 100K tokens/day. NOT used for training.
|
||||
Verify: https://console.groq.com/docs/your-data
|
||||
|
||||
Pricing based on ~50 words per dictation (~125 input + 65 output tokens).
|
||||
Verify current pricing:
|
||||
OpenAI: https://openai.com/api/pricing
|
||||
Anthropic: https://www.anthropic.com/pricing
|
||||
Groq: https://groq.com/pricing
|
||||
|
||||
CLEANUP_MENU
|
||||
|
||||
prompt_default "Enter 1-5" "1" CLEANUP_CHOICE
|
||||
|
||||
case "$CLEANUP_CHOICE" in
|
||||
1)
|
||||
CLEANUP_LABEL="OpenAI gpt-4o-mini"
|
||||
info "Cleanup: OpenAI gpt-4o-mini."
|
||||
if [[ -n "${OPENAI_API_KEY:-}" ]]; then
|
||||
info "OPENAI_API_KEY already set — reusing."
|
||||
else
|
||||
prompt_hidden "Enter your OpenAI API key (from https://platform.openai.com/api-keys)" OPENAI_KEY_INPUT
|
||||
store_api_key "OPENAI_API_KEY" "$OPENAI_KEY_INPUT"
|
||||
fi
|
||||
;;
|
||||
2)
|
||||
CLEANUP_LABEL="Anthropic claude-haiku-4-5"
|
||||
info "Cleanup: Anthropic claude-haiku-4-5."
|
||||
if [[ -n "${ANTHROPIC_API_KEY:-}" ]]; then
|
||||
info "ANTHROPIC_API_KEY already set — reusing."
|
||||
else
|
||||
prompt_hidden "Enter your Anthropic API key (from https://console.anthropic.com)" ANTHROPIC_KEY_INPUT
|
||||
store_api_key "ANTHROPIC_API_KEY" "$ANTHROPIC_KEY_INPUT"
|
||||
fi
|
||||
;;
|
||||
3)
|
||||
CLEANUP_LABEL="Groq llama-3.3-70b (free)"
|
||||
info "Cleanup: Groq llama-3.3-70b."
|
||||
if [[ -n "${GROQ_API_KEY:-}" ]]; then
|
||||
info "GROQ_API_KEY already set — reusing."
|
||||
else
|
||||
prompt_hidden "Enter your Groq API key (from https://console.groq.com)" GROQ_KEY_INPUT
|
||||
store_api_key "GROQ_API_KEY" "$GROQ_KEY_INPUT"
|
||||
fi
|
||||
;;
|
||||
4)
|
||||
CLEANUP_LABEL="Local Ollama (offline)"
|
||||
info "Cleanup: Local Ollama."
|
||||
if command_exists ollama; then
|
||||
info "Ollama is already installed."
|
||||
else
|
||||
warn "Ollama is not installed."
|
||||
cat <<'OLLAMA_INFO'
|
||||
|
||||
To install Ollama:
|
||||
curl -fsSL https://ollama.ai/install.sh | sh
|
||||
|
||||
Then pull a model:
|
||||
ollama pull llama3.1
|
||||
|
||||
OLLAMA_INFO
|
||||
printf "Install Ollama now? [Y/n]: "
|
||||
read -r ollama_reply
|
||||
ollama_reply="${ollama_reply:-Y}"
|
||||
if [[ "${ollama_reply,,}" == "y" ]]; then
|
||||
curl -fsSL https://ollama.ai/install.sh | sh || {
|
||||
warn "Ollama install failed. Install manually later."
|
||||
}
|
||||
else
|
||||
warn "Skipping Ollama install — you can install it later."
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
5)
|
||||
CLEANUP_LABEL="None (raw transcription)"
|
||||
info "Cleanup: Skipped — raw transcription only."
|
||||
;;
|
||||
*)
|
||||
warn "Invalid choice '$CLEANUP_CHOICE' — defaulting to OpenAI."
|
||||
CLEANUP_CHOICE="1"
|
||||
CLEANUP_LABEL="OpenAI gpt-4o-mini"
|
||||
if [[ -n "${OPENAI_API_KEY:-}" ]]; then
|
||||
info "OPENAI_API_KEY already set — reusing."
|
||||
else
|
||||
prompt_hidden "Enter your OpenAI API key (from https://platform.openai.com/api-keys)" OPENAI_KEY_INPUT
|
||||
store_api_key "OPENAI_API_KEY" "$OPENAI_KEY_INPUT"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
# [7/8] First launch
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
step "7/8" "First launch"
|
||||
|
||||
cat <<'LAUNCH_INFO'
|
||||
|
||||
OpenWhispr will open now for first-run setup.
|
||||
|
||||
- It will download speech models (~75-500 MB depending on choice).
|
||||
- Set your hotkey in Settings (default: backtick `).
|
||||
- The app manages its own autostart.
|
||||
|
||||
LAUNCH_INFO
|
||||
|
||||
LAUNCHED=false
|
||||
if command_exists openwhispr; then
|
||||
openwhispr &>/dev/null &
|
||||
LAUNCHED=true
|
||||
elif command_exists open-whispr; then
|
||||
open-whispr &>/dev/null &
|
||||
LAUNCHED=true
|
||||
fi
|
||||
|
||||
if [[ "$LAUNCHED" == true ]]; then
|
||||
info "OpenWhispr launched in the background."
|
||||
else
|
||||
warn "Could not find openwhispr in PATH."
|
||||
warn "Try launching manually:"
|
||||
warn " openwhispr"
|
||||
warn " open-whispr"
|
||||
warn " /opt/openwhispr/openwhispr"
|
||||
fi
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
# [8/8] Summary
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
step "8/8" "Done!"
|
||||
|
||||
printf "\n"
|
||||
divider
|
||||
# shellcheck disable=SC2059
|
||||
printf "${BOLD} OpenWhispr is ready${RESET}\n"
|
||||
divider
|
||||
cat <<EOF
|
||||
|
||||
Hotkey: backtick (\`) — change in Settings
|
||||
STT: ${STT_LABEL}
|
||||
AI cleanup: ${CLEANUP_LABEL}
|
||||
|
||||
API keys stored in: ${BASHRC}
|
||||
Apply now: source ${BASHRC}
|
||||
|
||||
Useful commands:
|
||||
openwhispr launch the app
|
||||
openwhispr --help show options
|
||||
|
||||
Settings → Hotkey change your hotkey
|
||||
Settings → Models download better speech models
|
||||
Settings → Processing change STT or AI provider
|
||||
|
||||
Troubleshoot: https://github.com/OpenWhispr/openwhispr/blob/main/TROUBLESHOOTING.md
|
||||
This installer: https://github.com/YOUR_USERNAME/openwhispr-easy-setup
|
||||
|
||||
EOF
|
||||
divider
|
||||
printf "\n"
|
||||
Reference in New Issue
Block a user