KansaiGaijin 679453eecc
All checks were successful
Build and Publish Docker Image / docker (push) Successful in 22s
Add README with service documentation
2026-07-10 23:59:55 +12:00
2026-01-21 06:59:28 +00:00
2026-01-21 06:59:28 +00:00
2026-01-21 06:59:28 +00:00
2026-01-21 06:59:28 +00:00
2026-01-21 06:59:28 +00:00
2026-01-21 06:59:28 +00:00
2026-01-21 06:59:28 +00:00

MBi Poller — Maloja to ListenBrainz Importer

A lightweight Python service that polls a Maloja music scrobbling server and forwards scrobbled tracks to ListenBrainz.

Runs as a one-shot Docker container — designed to be triggered on a schedule via cron, systemd timer, or another orchestrator.

How It Works

  1. Validates the ListenBrainz API token
  2. Fetches recently scrobbled tracks from the Maloja API
  3. Transforms and sanitizes track data (artist, title, album)
  4. Submits listens to ListenBrainz in batches of up to 100
  5. Rate-limits to 5 requests/second to respect ListenBrainz API limits
  6. Logs all activity to stdout and ./logs/scrobble_import.log

Quick Start

cp .env.example .env
# Edit .env with your real tokens
docker compose up

Configuration

Environment Variables

Variable Required Description
LISTENBRAINZ_TOKEN Yes ListenBrainz API token (format: lbp_xxxxxxxxxxxxxxxxx)
SOURCE_API_KEY Yes API key for authenticating with the Maloja instance
SOURCE_API_URL Yes Maloja API endpoint (default: https://maloja.example.com/api/trackscrobble)
LOG_DIR No Log directory inside the container (default: /app/logs)

.env.example

LISTENBRAINZ_TOKEN=lbp_xxxxxxxxxxxxxxxxx
SOURCE_API_KEY=xxxxxxxxxxxxxxxxx

Copy this to .env and fill in your values. Set SOURCE_API_URL in the docker-compose.yaml to point at your Maloja instance.

Command-Line Flags

Flag Description
--mode api Use the Maloja API to fetch scrobbles (default, used in compose)
--dry-run Parse and validate data without submitting to ListenBrainz

Docker Compose

services:
  maloja-listenbrainz-importer:
    image: yourname/maloja-listenbrainz-importer:latest
    container_name: maloja-listenbrainz-importer
    environment:
      LISTENBRAINZ_TOKEN: "${LISTENBRAINZ_TOKEN}"
      SOURCE_API_URL: "https://maloja.example.com/api/trackscrobble"
      SOURCE_API_KEY: "${SOURCE_API_KEY}"
      LOG_DIR: "/app/logs"
    volumes:
      - ./logs:/app/logs
    command: ["--mode", "api"]
    restart: "no"

Key Details

  • Restart policy: no — container runs once and exits
  • Ports: None exposed (outbound HTTPS only)
  • Volumes: ./logs persists log output across runs
  • Networks: Uses the default bridge network; no custom network required

Scheduling

This service does not run continuously. Trigger it periodically with a host-level scheduler:

Example crontab entry (every 6 hours):

0 */6 * * * cd /path/to/mbi-poller && docker compose up --remove-orphans

Building

docker compose build

Or pull the pre-built image from the Gitea registry:

docker pull gitea.kansaigaijin.com/Jamie/maloja-listenbrainz-importer:latest

CI/CD

A Gitea Actions workflow (.gitea/workflows/docker.yml) automatically builds and pushes the Docker image on pushes to main or version tags (v*).

Logs

Logs are written to:

  • Stdout — visible via docker compose logs
  • File./logs/scrobble_import.log (persisted on the host)

Project Structure

mbi-poller/
├── docker-compose.yaml    # Service definition
├── Dockerfile             # Python 3.12-slim image build
├── importer.py            # Core application logic
├── requirements.txt       # Python dependencies (requests)
├── .env.example           # Environment variable template
├── .dockerignore          # Build context exclusions
├── .gitea/workflows/      # CI/CD pipeline
└── logs/                  # Persisted log output
Description
No description provided
Readme 32 KiB
Languages
Python 94.9%
Dockerfile 5.1%