- Python 97.7%
- Dockerfile 2.3%
|
|
||
|---|---|---|
| .forgejo/workflows | ||
| .vscode | ||
| .dockerignore | ||
| .env.example | ||
| .gitignore | ||
| app.py | ||
| cache.py | ||
| config.py | ||
| config.yaml | ||
| docker-compose.yml | ||
| Dockerfile | ||
| downloader.py | ||
| logger.py | ||
| mailer.py | ||
| models.py | ||
| pyproject.toml | ||
| README.md | ||
| search_provider.py | ||
| uv.lock | ||
BxPMLoader
Local or self-hosted web app for looking up and downloading Brillux Praxismerkblätter (technical product PDFs) by product code.
Built for a single user — no bulk crawling, no accounts required.
Features
- Search by product code (e.g.
3100) or paste a name containing the code - Direct URL probe for instant exact matches — no scraping for known codes
- Real product name + first-page thumbnail extracted from the PDF
- Local SQLite cache with configurable TTL (default 30 days)
- Download cart: add products, download individually, as ZIP, or send by e-mail
- SMTP e-mail sending + iOS Web Share (native share sheet / Mail app on iPad)
- Persistent PDF storage in
data/pdfs/— never auto-deleted
Local development
Requires Python 3.11+ and uv.
uv sync
cp .env.example .env # fill in SMTP credentials
uv run streamlit run app.py
Opens at http://localhost:8501.
Project structure
bxpmloader/
├── app.py # Streamlit UI
├── search_provider.py # SearchProvider interface + BrilluxSearchProvider
├── downloader.py # PDF download, name extraction, thumbnail rendering
├── mailer.py # SMTP e-mail sender
├── cache.py # SQLite cache with TTL
├── models.py # ProductMatch, DocumentInfo dataclasses
├── config.py # Config loaded from config.yaml
├── logger.py # Logging to console + data/logs/
├── config.yaml # User-editable settings
├── pyproject.toml # Dependencies (uv)
├── Dockerfile # Container image
├── docker-compose.yml # Production stack
├── .env.example # Secrets template
└── .forgejo/workflows/
└── deploy.yml # CI/CD pipeline (Forgejo Actions)
Configuration
config.yaml controls runtime behaviour:
cache_ttl_days: 30 # How long search results stay cached
data_dir: "data" # PDFs, cache DB, logs
request_delay: 1.0 # Seconds between requests
request_timeout: 15
max_retries: 2
SMTP credentials live in .env (see .env.example). Never commit the real .env.
Self-hosted deployment (Forgejo CI → Docker on OMV)
Overview
Push to main
│
▼
Forgejo Actions (runner LXC 106)
│ docker build + push
▼
Forgejo Container Registry
│ SSH → docker compose pull + up -d
▼
OMV Docker host (LXC 103 / 192.168.178.56)
│ reverse proxy
▼
nginx-proxy-manager → bxpmloader.teppris.duckdns.org
Step 1 — Create the Forgejo repository
- Log into Forgejo → New repository →
keno/bxpmloader - Push this code:
git remote add origin https://git.teppris.duckdns.org/keno/bxpmloader.git git push -u origin main
Step 2 — Enable the Forgejo container registry
Forgejo has a built-in OCI container registry — no extra setup needed.
Images are stored at git.teppris.duckdns.org/keno/bxpmloader.
Step 3 — Configure repository secrets
Go to the repo → Settings → Secrets and variables → Actions and add:
| Secret | Value |
|---|---|
REGISTRY_TOKEN |
A Forgejo API token with package:write scope(Profile → Settings → Applications → Generate token) |
REGISTRY_USER |
Your Forgejo username (keno) |
DEPLOY_HOST |
OMV IP: 192.168.178.56 |
DEPLOY_USER |
SSH user on OMV (e.g. root or a dedicated deploy user) |
DEPLOY_SSH_KEY |
Private SSH key whose public key is in ~/.ssh/authorized_keys on OMV |
Generating the deploy SSH key (do this once, on your desktop):
ssh-keygen -t ed25519 -C "forgejo-deploy" -f ~/.ssh/forgejo_deploy -N "" # Add the public key to OMV: ssh-copy-id -i ~/.ssh/forgejo_deploy.pub root@192.168.178.56 # Paste the private key (cat ~/.ssh/forgejo_deploy) into the DEPLOY_SSH_KEY secret
Step 4 — Prepare the server (OMV)
Run once on OMV:
mkdir -p /opt/bxpmloader
cp .env.example /opt/bxpmloader/.env
nano /opt/bxpmloader/.env # fill in real SMTP credentials
# docker-compose.yml is pulled from git and used by CI directly
# OR copy it manually once:
scp docker-compose.yml root@192.168.178.56:/opt/bxpmloader/
# Log in to the Forgejo registry so OMV can pull images:
docker login git.teppris.duckdns.org
Step 5 — First deployment
Push to main (or trigger manually in Forgejo Actions).
The workflow will:
- Build the Docker image on the runner
- Push to the Forgejo container registry
- SSH into OMV, pull the new image, restart the container
After ~2 minutes the app is live at http://192.168.178.56:8085.
Step 6 — Reverse proxy with nginx-proxy-manager
In Portainer / nginx-proxy-manager on OMV:
- Add Proxy Host
- Domain:
bxpmloader.teppris.duckdns.org - Scheme:
http - Forward hostname:
192.168.178.56(or the container namebxpmloader-web) - Forward port:
8085
- Domain:
- Enable SSL via Let's Encrypt (already configured for your duckdns domain)
The app is then reachable at https://bxpmloader.teppris.duckdns.org from anywhere on Tailscale or via your duckdns domain.
Updating the app
Any push to main triggers the full pipeline automatically — build, push, redeploy. No manual steps needed.
Adjusting the web search fallback
If the direct URL probe fails for a code that should exist:
- Open
search_provider.pyand find# ADJUST_ME - Visit
https://www.brillux.de, search for the product, inspect the HTML - Update
BRILLUX_SEARCH_URLand the CSS selectors in_parse_search_results()
The direct URL pattern (pm{code}.pdf) handles the vast majority of lookups.
TODO
- Add other document types (Sicherheitsdatenblatt, Technisches Merkblatt) from product detail pages
- Tune web search selectors once Brillux page structure is confirmed
- Multi-product batch search (paste a list of codes)
- k8s manifests for when k3s is ready (Phase 2 of homelab modernisation)
Compliance notice
This tool performs only user-triggered, single-product lookups on publicly accessible pages. It does not bulk-crawl or scrape brillux.de.
Verify that your usage is permitted under Brillux's terms of service before operational use.