BBB-Recorder: Aufnahme-Bot fuer BigBlueButton-Meetings mit Transkript & KI-Zusammenfassung.
2.5K
Recording bot for BigBlueButton meetings โ including rooms fronted by the Nextcloud BBB integration: the bot joins through the ready-made room URL (no BBB API or checksums needed), records the meeting and offers playback, a transcript and an AI summary in the web UI. Existing audio/video files can be analysed by upload, without a bot.
Frontend (React) and backend (Spring Boot, Java 21, Playwright) run together in one container; PostgreSQL runs separately.
.md or as .doc, which Word and LibreOffice open directly โ and can save as DOCX or PDF from therelatest โ current state<git-hash> โ a specific commit (reproducible deployments)v3.0.0 where applicable/data/recordings โ recordings, transcripts, summaries, MP4s, attached documents| Variable | Meaning |
|---|---|
DB_URL, DB_USER, DB_PASSWORD | PostgreSQL connection (required) |
JWT_SECRET | Signing key for sessions (at least 32 random characters) |
JWT_TTL_HOURS | Sign-in session lifetime in hours (default 168 = 7 days) |
ADMIN_USERNAME, ADMIN_INITIAL_PASSWORD | Local admin account on first start (a password change is enforced) |
STORAGE_DIR | Recording directory (default /data/recordings) |
MAX_UPLOAD_SIZE | Maximum size for file uploads (default 4GB) |
MAX_CONCURRENT_BOTS | Maximum concurrent bots (default 5) |
INSECURE_TLS | Accept self-signed certificates on an intranet |
SERVER_PORT | HTTP port inside the container (default 8080) |
Everything else (Whisper, LLM, time window, bot behaviour, screen recording, retention, LDAP) is configured at runtime in the admin area and stored in the database โ no container restarts needed.
The container speaks HTTP; TLS termination is usually handled by a reverse proxy
in front of it. HTTPS is mandatory for screen recording โ without a secure
context the browser does not expose getDisplayMedia at all. The proxy should
set X-Forwarded-Proto/-Host (the app evaluates them) and must not buffer the
request body (proxy_request_buffering off), so that the incrementally uploaded
recording passes straight through. Ready-made nginx and Apache configurations:
docs/SCREEN_CAPTURE.mdโ .
services:
db:
image: postgres:16-alpine
environment:
POSTGRES_DB: bbbbot
POSTGRES_USER: bbbbot
POSTGRES_PASSWORD: please-change-me
volumes:
- pgdata:/var/lib/postgresql/data
app:
image: posiuke/bbb-recorder:latest
depends_on:
- db
environment:
DB_URL: jdbc:postgresql://db:5432/bbbbot
DB_USER: bbbbot
DB_PASSWORD: please-change-me
JWT_SECRET: please-enter-a-long-random-value
ADMIN_USERNAME: admin
ADMIN_INITIAL_PASSWORD: please-change-me
STORAGE_DIR: /data/recordings
ports:
- "8090:8080"
volumes:
- ./data/recordings:/data/recordings
shm_size: "1gb"
volumes:
pgdata:
Sign in with ADMIN_USERNAME / ADMIN_INITIAL_PASSWORD. A new password has to
be set on the first sign-in. After that you can optionally enable and test
LDAP/AD under Admin โ Authentication.
Recording and playback work even without these services configured โ only the transcript and the summary need Whisper and an LLM. Both are configured under Admin โ Settings; a click on "Test connection" checks the stored configuration right away (reachability, API key and model name).
| Setting | Value |
|---|---|
whisper.provider | local |
whisper.url | e.g. http://whisper:9000/asr (onerahmet/openai-whisper-asr-webserviceโ ; with ASR_ENGINE=whisperx speaker separation is possible, then enable whisper.diarize) |
llm.baseUrl | e.g. http://vllm:8000/v1 (vLLM, Ollama or any other OpenAI-compatible server) |
llm.model | Name of the loaded model |
The data stays entirely inside your own network.
Transcription (OpenAI audio API format):
| Setting | Value |
|---|---|
whisper.provider | openai |
whisper.openaiUrl | https://api.openai.com/v1/audio/transcriptions (compatible providers such as Groq work the same way) |
whisper.openaiApiKey | The provider's API key |
whisper.openaiModel | e.g. whisper-1 (with timestamps) or gpt-4o-mini-transcribe |
Summary (any OpenAI-compatible chat endpoint):
| Provider | llm.baseUrl | llm.model (example) |
|---|---|---|
| OpenAI | https://api.openai.com/v1 | gpt-4o-mini |
| Anthropic | https://api.anthropic.com/v1 | claude-sonnet-5 |
| Google Gemini | https://generativelanguage.googleapis.com/v1beta/openai | gemini-2.5-flash |
| Groq | https://api.groq.com/openai/v1 | llama-3.3-70b-versatile |
| Mistral | https://api.mistral.ai/v1 | mistral-large-latest |
Set llm.apiKey alongside each of them.
โ ๏ธ Data protection: with cloud APIs, audio data (Whisper) and the transcript plus chat log (LLM) leave your own network. Speaker separation (diarisation) is not supported by the OpenAI audio API and is only available with your own WhisperX server.
Between speech recognition and analysis, the LLM smooths the raw transcript. The
summary uses the smoothed version; the Whisper original stays stored and can be
viewed in the transcript tab via Corrected / Original (transcript.md and
transcript_original.md).
Smoothing works sentence by sentence and in several steps: Whisper lines often end mid-sentence, so they are joined into whole sentences; a sentence is never cut across two steps, and a change of speaker always ends a sentence. Timestamps and speaker labels are not left to the model: it only receives the numbered sentences, and the backend puts the structure back in front of them. If smoothing fails, the analysis carries on unaffected with the original.
Under Glossary there are two lists: your own and a shared glossary for the installation, maintained by admins and readable by everyone. For a recording both feed into the smoothing prompt โ the shared one and the personal one of its owner; if a term appears in both, the personal entry wins.
| Setting | Default | Meaning |
|---|---|---|
correction.enabled | true | Smoothing on/off |
correction.systemPrompt | (built-in default prompt) | Instruction to the LLM; keep the number | sentence response format |
correction.chunkChars | 3000 | Characters per smoothing step (also determines the response token budget) |
correction.maxSentenceChars | 500 | Where to split when the transcript contains no punctuation |
correction.glossaryMaxChars | 12000 | How much glossary goes into the prompt (0 = unlimited) |
The owner assigns tags on the detail page (max. 40 characters, 20 per recording; different spellings are merged); everyone with read access sees and filters by them. Above the list, a search field covers title/room name, meeting URL and tags โ with the checkbox "Also search transcript and summary" the contents as well. There is nothing to configure; the search only ever returns your own and shared recordings.
The list loads page by page ("Load more") and can be narrowed by period, source (bot in the meeting, upload, screen recording) and owner, sorted by date or title. Slicing, filtering and sorting happen in the database, so the browser only ever holds what it displays.
The content search uses LIKE without a full-text index โ unproblematic for a
few thousand recordings; for very large holdings a Postgres full-text index
would be the next step.
Users record their screen via Recordings โ Record screen directly in the browser; the running recording is uploaded in chunks, so a crash costs at most the last few seconds. HTTPS and Chrome/Edge are required (Firefox does not capture system audio).
| Setting | Default | Meaning |
|---|---|---|
capture.enabled | true | Feature available to users |
capture.maxMegabytes | 8192 | Upper limit per recording (rule of thumb: 0.5 GB per hour at standard quality, about 60 MB without video) |
capture.staleMinutes | 5 | After this long without data a recording counts as aborted; the server then finalises it with the data it has |
Setup, reverse proxy configuration and troubleshooting: docs/SCREEN_CAPTURE.mdโ .
In a recording's Documents tab you can attach whatever was discussed in the meeting โ the agenda, slides, an offer. Their text feeds into the next AI analysis, so the summary knows the subject and not just what was said.
Text and Markdown files are read by the app itself. PDF, Office files and
scans need an Apache Tika server; OCR of scanned pages is done by Tika using
tesseract. To set that up, enable the commented-out tika service in
docker-compose.ymlโ (apache/tika:latest-full ships with tesseract) and set
tikaUrl to http://tika:9998 in the admin area under Attached documents. If
you already run a Tika server, just enter its address there. Without it, PDFs
fail with a clear message โ and the tab says so beforehand.
The documents section feeds into every analysis step; how much of it is
limited by documents.maxCharsPerDocument (per file) and
documents.promptMaxChars (in total). With a cloud API the documents therefore
leave your own network as well. They do not appear in a share view.
On the recording detail page the owner can use "Customise analysis" to set their own analysis prompt (with templates for talks, interviews and voice notes), a maximum length and the summary language โ handy for uploaded files that are not meetings. The language of the recording for speech recognition (including "detect automatically") lives there too; it can also be picked in the upload dialog, in the bot form and when starting a screen recording, because a wrong language hint damages the transcript from the very beginning. The settings take effect on the next analysis ("Analyse now", "Analyse again" or "Recreate transcript"). The administrator's default can be pulled into the field with "Load default" and edited there, instead of having to replace it entirely.
Model and temperature can be overridden per template and per recording
(llm.model/llm.temperature remain the default). That makes it possible to
compare two models on the same recording: switch, "Analyse again", read both
versions side by side.
A repeated analysis does not replace the summary; it adds another version
next to it โ labelled with template, model and timestamp, along with the prompt
it was created from. In the Summary tab you can switch between versions;
exactly one is the current one and is used in the download, the API, the share
view and summary.md. An older version can be brought back with "Make
current", and older ones can be deleted individually. Nothing is cleaned up
automatically โ hand-edited versions stay.
Summary and smoothed transcript are rendered as GitHub Markdown โ tables and task
lists appear as such. Code blocks with the language mermaid are drawn as a
diagram; you can ask for that in the analysis prompt ("โฆ additionally as a
Mermaid flowchart").
Both can be downloaded: the transcript in the transcript tab (always the
version currently shown โ corrected or original), the summary via the button in
the recording header. Besides Markdown there is a Word version (.doc) of
each, which Word and LibreOffice open directly and from which DOCX or PDF can be
saved.
With one GPU, a nightly time window and several bots, the queue is where it is decided whether everything is finished by morning. The Admin โ Processing tab is the page to open in the morning:
Also available through the API: GET /api/admin/processing and
POST /api/admin/processing/jobs/{jobId}/retry (both admin only).
Via Share โ Link for sharing the owner of a recording creates a URL
(https://<host>/share/<token>) and chooses the access:
The chat log and session log remain reserved for the signed-in view. The lifetime is either unlimited (until revoked) or 7/30/90 days; a revocation takes effect immediately. The dialog shows the kind, the number of views and the last access for each link. If the recording is deleted, its links go with it.
If accesses must stay attributable as a matter of policy, switch off the admin
setting sharing.publicLinks: then all share links require a sign-in, including
ones created earlier.
A note on downloads: whether the browser asks for a target folder when downloading is a browser setting (Chrome/Edge: Settings โ Downloads โ "Ask where to save each file before downloading"). The application cannot force it.
The source code is public on GitHub: Posiuke/Meeting-Recorderโ .
Bug reports and feature requests are welcome โ from anyone, straight as an issue: report a bugโ ยท request a featureโ ยท all issuesโ .
Please strip credentials, API keys, internal host names and meeting content from logs first โ issues are publicly readable.
Content type
Image
Digest
sha256:ece96021dโฆ
Size
1.6 GB
Last updated
9 days ago
docker pull posiuke/bbb-recorder