A website that stores and scans Kometa log files, presents recommendations, and creates opaque shareable result URLs. Each result has a separate deletion token. The included Red cog detects log attachments, asks the author for confirmation, uploads the file, and returns its result/deletion URL.
- Python 3.13
- Approximately 1 GB RAM is recommended for 100 MB logs
- Windows, Linux, or macOS
Open PowerShell in the extracted kometa-logscan-web directory:
py -3.13 -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install --upgrade pip
python -m pip install -r requirements.txt
python -m logscan_web.appOpen http://127.0.0.1:5000.
If PowerShell blocks virtual-environment activation, run this once in the current PowerShell window:
Set-ExecutionPolicy -Scope Process Bypasspython3.13 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install -r requirements.txt
python -m logscan_web.appOpen http://127.0.0.1:5000.
Do not use Flask's development server for a public deployment.
Linux production command:
gunicorn --workers 1 --threads 1 --timeout 300 --bind 0.0.0.0:8000 logscan_web.app:appWindows production command:
waitress-serve --listen=0.0.0.0:8000 --threads=1 logscan_web.app:appOne worker/thread is intentional because the inherited recommendation engine has process-global divider state. It also avoids allowing simultaneous 100 MB scans to exhaust memory. Put a reverse proxy and rate limiting in front of a public instance.
docker build -t kometa-logscan-web .
docker run --rm -p 8000:8000 \
-e LOGSCAN_API_KEY=replace-me \
-e TMDB_API_KEY=your-tmdb-api-key \
-v logscan-data:/data \
kometa-logscan-webOpen http://127.0.0.1:8000.
The included compose.saltbox.yml follows Saltbox's Traefik template and
publishes the app at https://logscan.kometa.team.
sudo mkdir -p /opt/logscan/data
sudo chown -R 10001:10001 /opt/logscan/data
cd /opt/logscan
cp .env.example .env
openssl rand -hex 32
# Put that value in .env, then:
docker compose -f compose.saltbox.yml up -d --buildCreate an A/AAAA record for logscan.kometa.team pointing to the Saltbox
server (or use Saltbox DDNS/wildcard DNS). The compose file expects the
external saltbox Docker network and Saltbox's standard Traefik middlewares.
If the domain is not managed through the Cloudflare account configured in
Saltbox, change cfdns to the certificate resolver used by your installation.
Logs and result JSON are stored below /opt/logscan/data, so all persistent
application data remains inside /opt/logscan on the host. Scans are
automatically deleted 48 hours after upload. The service checks immediately at
startup and hourly thereafter.
Add this repository to Red's Downloader and install logscan_cog:
[p]repo add logscan <repository-clone-url>
[p]cog install logscan logscan_cog
[p]load logscan_cog
[p]logscanset url https://logscan.kometa.team
[p]logscanset apikey <the same value from /opt/logscan/.env>
The settings commands are bot-owner only. The API-key command attempts to delete the invoking Discord message. Prefer running it in a private channel. The cog requires permission to read messages, send messages, attach buttons, and read attachment content.
The returned link contains the deletion token after #delete=. URL fragments
are not included in HTTP requests, but anyone who receives the complete link
can view and permanently delete that log. A result URL without the fragment is
view-only. All links expire when their scan is automatically deleted after 48
hours.
When a scan encounters a missing People Poster, the website records the person
in the persistent /people backlog. Set TMDB_API_KEY in the web service
environment so the service can resolve a TMDb person ID and load their profile
images. A person remains in the backlog until someone marks them complete.
The cog sends one non-embedding notification per detected person to channel
1539665929330499664, including the scan, original Discord message (where
available), TMDb-image status, and the person page.
For website uploads, set DISCORD_PEOPLE_WEBHOOK_URL to a webhook created in
the destination Discord channel. A non-embedding notice is posted only when a
person is first added to the backlog, preventing repeat notifications for the
same person.
The application accepts uploads up to 100 MiB. Your reverse proxy must accept a slightly larger HTTP request because multipart uploads add overhead.
For Nginx, include this in the applicable server or location block:
client_max_body_size 110M;
proxy_read_timeout 300s;
proxy_send_timeout 300s;
proxy_pass http://127.0.0.1:8000;If Cloudflare proxies the hostname, its plan-specific request-body limit also applies. A nominal 100 MB Cloudflare limit may reject a 100 MiB file plus form overhead. Use a smaller application limit, a higher-limit plan, or DNS-only routing if full-size uploads must work.
python -m unittest discover -s tests -vlogscan_web/app.py— Flask routes and upload handlinglogscan_web/storage.py— filesystem persistence and deletion tokenslogscan_web/scanner.py— input validation and result normalizationlogscan_web/models.pyandlogscan_web/rules/— structured scan data and modular recommendation ruleslogscan_web/templates/— HTML interfacelogscan_web/static/— styles and browser behaviortests/— scanner and API regression testsrequirements.txt— complete Python dependenciesDockerfile— container deploymentcompose.saltbox.yml— Saltbox/Traefik deploymentlogscan_cog/— Red bot cog