186 lines
6.5 KiB
Markdown
186 lines
6.5 KiB
Markdown
# MPS — Martinhal Presentation System
|
||||
|
|
|
|||
|
|
A self-hosted app that turns any presentation into a link and a QR code. Upload a
|
|||
|
|
**video, PowerPoint, Word doc, Excel sheet, PDF, or image**; the app converts it to a
|
|||
|
|
browser-friendly format and generates a QR code. Anyone who scans it watches along on
|
|||
|
|
their own phone — **live and in sync with the presenter**, or browsing at their own pace.
|
|||
|
|
|
|||
|
|
Everything runs in a single Docker container. No cloud, no third-party services except
|
|||
|
|
the mail account you choose to connect for email sharing.
|
|||
|
|
|
|||
|
|
**Version 1.3** · © 2026 Martinhal IT — Joao Vaz
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## What's new in 1.3
|
|||
|
|
|
|||
|
|
- **First-run setup.** The very first time you open the app it asks you to **create the
|
|||
|
|
administrator account** — no default password to remember or change.
|
|||
|
|
- **Rebranded** as MPS — Martinhal Presentation System, with the MPS logo across the app.
|
|||
|
|
- **QR Code button** on every ready presentation opens the code on its own page in
|
|||
|
|
**large format** — ideal for putting on a projector. It has copy-link, fullscreen and
|
|||
|
|
print actions.
|
|||
|
|
- **Bigger presentation cards** on the host dashboard (twice as wide, 50% taller) so the
|
|||
|
|
QR preview and controls are easier to use.
|
|||
|
|
|
|||
|
|
### From 1.2
|
|||
|
|
|
|||
|
|
Login page and user accounts, admin-only user management, mail settings and activity logs,
|
|||
|
|
email sharing, PurelyMail SMTP+IMAP with a copy saved to **Sent**, and First/Last slide
|
|||
|
|
controls.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Quick start (Docker Compose)
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# Point QR codes at an address phones can actually reach (LAN IP or public URL).
|
|||
|
|
export PUBLIC_BASE_URL=http://192.168.1.50:3000
|
|||
|
|
|
|||
|
|
docker compose up --build -d
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Open `http://<host>:3000`. On first launch you'll be asked to **create the admin
|
|||
|
|
account**. After that, sign in, upload a file, and hit **Present**. Show the QR code
|
|||
|
|
(**Share / QR**, or the **QR Code** button for a full-screen page) and the room scans to
|
|||
|
|
join.
|
|||
|
|
|
|||
|
|
> **Unattended installs:** if you'd rather not use the setup screen, set `ADMIN_USERNAME`
|
|||
|
|
> and `ADMIN_PASSWORD` before the first start and an admin will be pre-created from those
|
|||
|
|
> values instead. They're ignored once an account exists.
|
|||
|
|
|
|||
|
|
### Plain Docker
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
docker build -t mps .
|
|||
|
|
docker run -d --name mps \
|
|||
|
|
-p 3000:3000 \
|
|||
|
|
-e PUBLIC_BASE_URL=http://192.168.1.50:3000 \
|
|||
|
|
-v qr_data:/data \
|
|||
|
|
mps
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Without Docker (Node 18+)
|
|||
|
|
|
|||
|
|
Requires `libreoffice`, `ffmpeg`, and fonts on the host.
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
npm install
|
|||
|
|
npm run fetch-vendor # vendors PDF.js for offline use
|
|||
|
|
npm start
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Updating (no data loss)
|
|||
|
|
|
|||
|
|
All state — user accounts, mail settings, logs, uploaded decks — lives under the `/data`
|
|||
|
|
volume, never inside the app code. To update:
|
|||
|
|
|
|||
|
|
1. Unzip the new version over your existing project folder (code files are replaced;
|
|||
|
|
your `data/` and the Docker `qr_data` volume are left untouched).
|
|||
|
|
2. Rebuild and restart:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
docker compose up --build -d
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Your admin account, users, presentations, and mail configuration carry straight over, and
|
|||
|
|
the first-run setup screen does **not** reappear.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Users & roles
|
|||
|
|
|
|||
|
|
| Role | Can do |
|
|||
|
|
|---|---|
|
|||
|
|
| **admin** | Everything: manage users, configure mail, view logs, see and manage **all** presentations. |
|
|||
|
|
| **user** | Sign in, upload and present **their own** decks, share them (link, QR, email). |
|
|||
|
|
|
|||
|
|
The first admin is created during setup. Manage further accounts at **/users** (admins
|
|||
|
|
only). The app prevents removing or demoting the last remaining admin, and you can't
|
|||
|
|
delete your own account.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Sharing a presentation
|
|||
|
|
|
|||
|
|
Each ready presentation offers several ways to get people in:
|
|||
|
|
|
|||
|
|
- **QR Code** — opens a dedicated full-screen page with a large, scannable code (plus
|
|||
|
|
copy-link, fullscreen, and print). Great for a projector or printed handout.
|
|||
|
|
- **Share / QR** (in the presenter view) — a quick pop-over with the code and link.
|
|||
|
|
- **Copy link** — the plain audience URL.
|
|||
|
|
- **Email** — sends the link and QR straight to recipients (requires mail set up).
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Email sharing (PurelyMail)
|
|||
|
|
|
|||
|
|
1. Sign in as an admin → **Manage users → Mail**.
|
|||
|
|
2. Tick **Enable email sending**, enter the PurelyMail **account email** and **password**.
|
|||
|
|
Servers are prefilled and match PurelyMail's settings:
|
|||
|
|
|
|||
|
|
| | Host | Port | Security |
|
|||
|
|
|---|---|---|---|
|
|||
|
|
| SMTP (send) | `smtp.purelymail.com` | `465` | SSL/TLS |
|
|||
|
|
| IMAP (Sent copy) | `imap.purelymail.com` | `993` | SSL/TLS |
|
|||
|
|
|
|||
|
|
3. Save, then **Send test** to confirm it works.
|
|||
|
|
|
|||
|
|
Every message is delivered over SMTP and a copy is appended to the **Sent** folder over
|
|||
|
|
IMAP, so it appears in your normal mail client. The stored mail password lives only in
|
|||
|
|
`/data/config.json` on your server and is never sent back to the browser.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Presenting
|
|||
|
|
|
|||
|
|
- **Slides:** `« First` · `‹ Prev` · `Next ›` · `Last »`, or the keys `←` `→` `Home` `End`.
|
|||
|
|
- **Synced ↔ Free browse:** in Synced mode the audience follows you; in Free browse they
|
|||
|
|
move on their own. Audience members who wander off get a one-tap "Rejoin presenter".
|
|||
|
|
- **Video:** play / pause / seek are broadcast to everyone. Viewers tap once to allow
|
|||
|
|
sound (browsers block autoplay audio until then).
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Supported formats
|
|||
|
|
|
|||
|
|
| Input | Handled as | Tool |
|
|||
|
|
|---|---|---|
|
|||
|
|
| `.pdf` | paged deck | served directly |
|
|||
|
|
| `.pptx .ppt .odp .pps .ppsx` | paged deck | LibreOffice → PDF |
|
|||
|
|
| `.docx .doc .odt .rtf .txt .md` | paged deck | LibreOffice → PDF |
|
|||
|
|
| `.xlsx .xls .ods .csv .tsv` | paged deck | LibreOffice → PDF |
|
|||
|
|
| `.mp4 .webm .ogg` | synced video | served directly |
|
|||
|
|
| `.mov .avi .mkv .wmv .flv .3gp .mpeg .ts` | synced video | ffmpeg → MP4 |
|
|||
|
|
| `.png .jpg .jpeg .gif .webp .svg .bmp .avif` | image | served directly |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Configuration
|
|||
|
|
|
|||
|
|
| Variable | Default | Purpose |
|
|||
|
|
|---|---|---|
|
|||
|
|
| `PUBLIC_BASE_URL` | derived from request | Absolute URL encoded in QR codes / links. **Set this.** |
|
|||
|
|
| `ADMIN_USERNAME` | `admin` | Only for unattended pre-seed (see Quick start). |
|
|||
|
|
| `ADMIN_PASSWORD` | *(unset)* | If set on first run, pre-creates an admin instead of showing setup. |
|
|||
|
|
| `ADMIN_EMAIL` | *(empty)* | Email for a pre-seeded admin. |
|
|||
|
|
| `MAX_UPLOAD_MB` | `500` | Maximum upload size. |
|
|||
|
|
| `PORT` | `3000` | Listen port. |
|
|||
|
|
| `DATA_DIR` | `/data` (Docker) | Where all state is stored. Mount as a volume. |
|
|||
|
|
| `CONVERT_TIMEOUT_MS` | `240000` | Per-file conversion timeout. |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Security notes
|
|||
|
|
|
|||
|
|
- Put a reverse proxy with **HTTPS** in front for anything beyond a trusted LAN, and set
|
|||
|
|
`PUBLIC_BASE_URL` to the https address.
|
|||
|
|
- Passwords are stored as salted scrypt hashes. Login sessions are HttpOnly cookies.
|
|||
|
|
- The container runs as a non-root user; all state stays on the mounted volume.
|
|||
|
|
|
|||
|
|
## License
|
|||
|
|
|
|||
|
|
MIT.
|