Extract and export worship song lyrics from ProPresenter 7
View the Project on GitHub adamswbrown/propresenterlyricexport
| ← Back to Home | User Guide | Proxy App Guide (GUI) |
Prefer a GUI? The Web Proxy App is a macOS menu bar application that manages the web server and tunnel for you — no terminal required. This guide covers the manual, terminal-based setup.
Access ProPresenter Lyrics Export from any device — phone, tablet, or remote computer — through a secure web interface exposed via Cloudflare Tunnel.
The web proxy lets remote users access your ProPresenter installation through a browser. The architecture is:
User's browser → Cloudflare Tunnel → Web proxy (your machine) → ProPresenter API
Key features:
Google OAuth lets your users sign in with their Google account. Only emails you approve can access the app.
| Field | What to enter |
|---|---|
| App name | ProPresenter Web (or your preferred name) |
| User support email | Your email address |
| Developer contact email | Your email address |
openid.../auth/userinfo.email.../auth/userinfo.profileThese are all non-sensitive scopes — no Google verification is required, even for production use.
https://YOUR-DOMAIN.example.com/auth/google/callback
Replace YOUR-DOMAIN.example.com with your actual tunnel hostname (configured in Step 3).
A dialog shows your Client ID and Client Secret.
Copy both values immediately — the Client Secret is only visible at creation time (since June 2025). You can also click Download JSON to save them.
Store these values securely. You’ll use them as environment variables in Step 4.
For a self-hosted app using only openid, email, and profile scopes:
Cloudflare Tunnel (cloudflared) creates a secure outbound connection from your machine to Cloudflare’s edge. No port-forwarding required.
brew install cloudflared
winget install --id Cloudflare.cloudflared
# Add Cloudflare GPG key and repository
sudo mkdir -p --mode=0755 /usr/share/keyrings
curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg \
| sudo tee /usr/share/keyrings/cloudflare-main.gpg >/dev/null
echo "deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared any main" \
| sudo tee /etc/apt/sources.list.d/cloudflared.list
sudo apt-get update && sudo apt-get install cloudflared
curl -fsSl https://pkg.cloudflare.com/cloudflared.repo \
| sudo tee /etc/yum.repos.d/cloudflared.repo
sudo yum update && sudo yum install cloudflared
cloudflared --version
cloudflared tunnel login
This opens your browser. Select the Cloudflare zone (domain) to authorize. A certificate is saved to ~/.cloudflared/cert.pem.
cloudflared tunnel create propresenter-web
Note the tunnel UUID printed (e.g., 6ff42ae2-765d-4adf-8112-31c55c1551ef). A credentials file is created at ~/.cloudflared/<UUID>.json.
cloudflared tunnel route dns propresenter-web pp.standrewsbangor.church
Replace pp.example.com with your desired hostname. This creates a CNAME record in Cloudflare DNS.
Create ~/.cloudflared/config.yml:
tunnel: <TUNNEL-UUID>
credentials-file: /home/<USER>/.cloudflared/<TUNNEL-UUID>.json
ingress:
- hostname: pp.example.com
service: http://localhost:3100
- service: http_status:404
Replace <TUNNEL-UUID> with your actual UUID and <USER> with your system username.
cloudflared tunnel ingress validate
Create a .env file or export these variables before starting the server:
# Required — Google OAuth credentials (from Step 1e)
export GOOGLE_CLIENT_ID="123456789-abcdef.apps.googleusercontent.com"
export GOOGLE_CLIENT_SECRET="GOCSPX-xxxxxxxxxxxxxxxx"
# Required — your tunnel's public URL
export TUNNEL_URL="https://pp.example.com"
# Optional — ProPresenter connection (defaults shown)
export PROPRESENTER_HOST="192.168.68.58"
export PROPRESENTER_PORT="61166"
# Optional — server settings (defaults shown)
export WEB_PORT="3100"
export WEB_HOST="0.0.0.0"
export LOG_RETENTION_DAYS="14"
# 1. Install dependencies
npm ci
# 2. Build the server and web UI
npm run build
npm run web:build:ui
# 3. Start the server
npm run web:start
The startup banner shows connection details, auth status, and paths:
ProPresenter Lyrics Export — Web Proxy
======================================
Server: http://0.0.0.0:3100
Health: http://0.0.0.0:3100/health
Tunnel: https://pp.example.com
Tunnel config: OK
Google OAuth: ENABLED
Callback URL: https://pp.example.com/auth/google/callback
Allowed users: (none — add emails to allowlist)
Bearer token: a1b2c3d4-e5f6-...
(fallback auth for API access / SSE)
Sessions: /home/user/.propresenter-words/sessions
Logs: /home/user/.propresenter-words/logs
cloudflared tunnel run propresenter-web
Or run both together:
npm run web:start:tunnel
Only emails in the allowlist can sign in. Add the first user (yourself) via CLI:
# Add yourself as an admin
npm start -- users add you@gmail.com --admin
# Add other users
npm start -- users add colleague@gmail.com
# List all users
npm start -- users list
Once signed in as an admin, you can also manage users from the web UI via the Users button.
For persistent operation (survives reboots), install cloudflared as a system service.
# Install cloudflared as a service
sudo cloudflared --config /home/<USER>/.cloudflared/config.yml service install
sudo systemctl enable cloudflared
sudo systemctl start cloudflared
For the web proxy itself, create a systemd unit:
# /etc/systemd/system/propresenter-web.service
[Unit]
Description=ProPresenter Lyrics Export Web Proxy
After=network.target
[Service]
Type=simple
User=<USER>
WorkingDirectory=/path/to/propresenterlyricexport
ExecStart=/usr/bin/node dist/server/index.js
Restart=on-failure
RestartSec=5
Environment=GOOGLE_CLIENT_ID=your-client-id
Environment=GOOGLE_CLIENT_SECRET=your-client-secret
Environment=TUNNEL_URL=https://pp.example.com
Environment=NODE_ENV=production
[Install]
WantedBy=multi-user.target
sudo systemctl enable propresenter-web
sudo systemctl start propresenter-web
# Install cloudflared as a service
sudo cloudflared service install
For the web proxy, create a launchd plist at ~/Library/LaunchAgents/com.propresenter.web.plist or use a process manager like pm2.
cloudflared.exe service install
sc start cloudflared
For the web proxy, use a tool like NSSM or run as a scheduled task on login.
For quick testing without a Cloudflare account or domain:
# Terminal 1: Start the web server
npm run web:start
# Terminal 2: Quick tunnel (random URL)
cloudflared tunnel --url http://localhost:3100
This gives you a temporary *.trycloudflare.com URL. Limitations:
Use the bearer token (printed on server start) to authenticate — paste it on the login page.
The server didn’t find GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET. Make sure they’re exported in your environment before starting the server.
The redirect URI in Google Cloud Console must match exactly:
https://pp.example.com/auth/google/callback
This shouldn’t happen with the file-based session store. Check that ~/.propresenter-words/sessions/ exists and is writable.
When behind a tunnel, cookies require secure: true. Make sure:
TUNNEL_URL is set (this enables secure cookies automatically)http://localhost# Test tunnel health
curl https://pp.example.com/health?check=tunnel
# Check cloudflared is running
cloudflared tunnel info propresenter-web
Request logs are written to ~/.propresenter-words/logs/ as daily JSON-lines files:
# View today's log
cat ~/.propresenter-words/logs/web-$(date +%Y-%m-%d).log | head -20
# Watch live
tail -f ~/.propresenter-words/logs/web-$(date +%Y-%m-%d).log
Logs are automatically pruned after 14 days (configurable via LOG_RETENTION_DAYS).
All traffic between users and your server is encrypted end-to-end by Cloudflare Tunnel. You don’t need to configure TLS certificates — Cloudflare handles this automatically.
The connection flow:
Browser ←—HTTPS—→ Cloudflare Edge ←—encrypted tunnel—→ cloudflared ←—HTTP—→ localhost:3100
httpOnly, secure, sameSite=lax, 6-hour expiry.All configuration is stored locally at ~/.propresenter-words/:
| File | Contents |
|---|---|
web-auth.json |
Bearer token + session secret (chmod 600) |
web-users.json |
Email allowlist + admin list |
sessions/ |
Session files (auto-pruned) |
logs/ |
Request logs (auto-pruned after 14 days) |
aliases.json |
Song alias mappings |
Auth endpoints are rate-limited to 20 attempts per 15 minutes per IP (uses CF-Connecting-IP when behind Cloudflare).
| Variable | Required | Default | Description |
|---|---|---|---|
GOOGLE_CLIENT_ID |
For OAuth | — | Google OAuth client ID |
GOOGLE_CLIENT_SECRET |
For OAuth | — | Google OAuth client secret |
TUNNEL_URL |
For production | — | Public tunnel URL (e.g., https://pp.example.com) |
WEB_PORT |
No | 3100 |
Server listen port |
WEB_HOST |
No | 0.0.0.0 |
Server listen host |
PROPRESENTER_HOST |
No | 127.0.0.1 |
ProPresenter host |
PROPRESENTER_PORT |
No | 1025 |
ProPresenter API port |
CORS_ORIGIN |
No | * |
Comma-separated allowed origins |
NODE_ENV |
No | — | Set to production for secure cookies without TUNNEL_URL |
LOG_RETENTION_DAYS |
No | 14 |
Days to keep log files |