Skip to content

CaptainPimpJr/beo_radio_scrobbler

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

69 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

BeoRadio Scrobbler 🎡

Problem Statement

When Spotify released Wrapped for the first time, I was beaten in minutes listened to music by far by my friends.

But, I was actually listening to music on the radio all day long.

That left me wondering: How do I track my music listening record besides Spotify? I liked the idea of analyzing my listening behaviour, but I had heard of scrobblers and never felt the urge to connect to Last.fm.

Every year since, when a new Wrapped is published, I have to think again about my "problem".


Timeline πŸ—“οΈ

timeline
    title Project Development Journey
    
    section Initial Idea
        Spotify Wrapped Released : Idea born
                                  : Get radio tracks to Spotify?
                                  : Run songs on headless system?
                                  : ...what a waste of energy πŸ’‘
    
    section December 2025
        Dec 23rd : Lying in bed googling stuff
                 : Wrapped haunting me again
                 : Musical age: 66 years
                 : Behind friends in minutes
                 : Losing street credibility 😒
        
        Dec 24th : Starting tests on Christmas Eve
                 : Working better than expected! πŸŽ„
                 : API understood
                 : Getting artist and title info
                 : First scrobble with pylast
                 : All work done by pylast library
        
        Dec 25th : Kids playing with new toys
                 : Scrobbling works! 🎁
                 : Minor flaws fixed
                 : Exception handling added
                 : Local work complete
                 : Next: Docker, then Unraid
        
        Dec 26th-30th : Dockerfile creation
                      : Publishing to Docker Hub
                      : GitHub Actions setup
                      : Much harder than the code! 🐳
Loading

Alternative Timeline View:

  • 🌟 Initial Idea β†’ When was Wrapped released the first time!? Idea was born: Get Radio tracks to Spotify, run the Songs on a headless system...what a waste of energy.
  • πŸ“… Dec 23, 2025 β†’ Lying in bed googling stuff...Wrapped is asking me again, since my "musical age" is 66. I am again far behind my friends in minutes spent with music, which is unfair, because I am listening to super good stuff all the time, but it is unrecognized, unrecorded...I am losing street credibility.
  • πŸŽ„ Dec 24, 2025 β†’ Starting with some tests while everyone is getting ready for Christmas Eve, working better than expected! API is understood and workflow is getting artist and title information. First scrobble is placed with pylast library. No efforts for me, all the work done by pylast. Time saved.
  • 🎁 Dec 25, 2025 β†’ While the kids are playing with their new toys, scrobbling works with some minor (?) flaws. Code needs to wait here for a second and adding exceptions there is doing the trick. Locally my work is done, next step docker, then unraid.
  • 🐳 Dec 26-30, 2025 β†’ Dockerfile, publishing to Docker Hub, using DHI, getting GitHub Actions working properly, makes me forget how easy the first part was.

Installation

This installation guide is provided to you by github copilot:

Installation Guide - BEO Radio Scrobbler

This guide covers two methods to install and run the BEO Radio Scrobbler: using uv (modern Python package manager) or running as a Docker container.

Prerequisites

  • Python 3.11 or higher (for uv installation)
  • Docker (for Docker installation)
  • Last.fm account with API credentials
  • Bang & Olufsen device on your network

Method 1: Installation with uv

uv is a fast Python package installer and resolver.

Step 1: Install uv

# On macOS and Linux
curl -LsSf https://astral.sh/uv/install.sh | sh

# On Windows
powershell -c "irm https://astral.sh/uv/install.ps1 | iex"

Step 2: Clone the Repository

git clone https://github.com/CaptainPimpJr/beo_radio_scrobbler.git
cd beo_radio_scrobbler

Step 3: Create Environment Variables

Create a .env file in appdata/config/:

# . env file
LASTFM_API_KEY=your_lastfm_api_key
LASTFM_API_SECRET=your_lastfm_api_secret
LASTFM_USERNAME=your_lastfm_username
LASTFM_PASSWORD=your_lastfm_password
LOCAL_TIMEZONE=Europe/Berlin
RUN_MODE=production
BEO_IP=192.168.1.100
LOGLEVEL=INFO

Environment Variables Explained:

  • LASTFM_API_KEY: Your Last.fm API key (get from https://www.last.fm/api/account/create)
  • LASTFM_API_SECRET: Your Last.fm API secret
  • LASTFM_USERNAME: Your Last.fm username
  • LASTFM_PASSWORD: Your Last.fm password
  • LOCAL_TIMEZONE: Your timezone (e.g., UTC, Europe/Berlin, America/New_York)
  • RUN_MODE: Operation mode (production, detect, detect_smpl, or notify_me)
  • BEO_IP: IP address of your Bang & Olufsen device
  • LOGLEVEL: Logging level (DEBUG, INFO, WARNING, ERROR)

Step 4: Sync Dependencies

uv sync

This will create a virtual environment and install all dependencies.

Step 5: Run the Application

# Activate the virtual environment
source .venv/bin/activate  # On Linux/macOS
# or
. venv\Scripts\activate  # On Windows

# Run the scrobbler
python -m beo_radio_scrobbler

# Or use the CLI entry point
beo-radio-scrobbler

Step 6: Run as a Background Service (Optional)

For Linux systems with systemd, create a service file:

sudo nano /etc/systemd/system/beo-radio-scrobbler.service

Add the following content (adjust paths):

[Unit]
Description=BEO Radio Scrobbler
After=network.target

[Service]
Type=simple
User=your_username
WorkingDirectory=/path/to/beo_radio_scrobbler
Environment="PATH=/path/to/beo_radio_scrobbler/. venv/bin"
ExecStart=/path/to/beo_radio_scrobbler/.venv/bin/python -m beo_radio_scrobbler
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target

Enable and start the service:

sudo systemctl daemon-reload
sudo systemctl enable beo-radio-scrobbler
sudo systemctl start beo-radio-scrobbler
sudo systemctl status beo-radio-scrobbler

Method 2: Docker Installation

Step 1: Clone the Repository

git clone https://github.com/CaptainPimpJr/beo_radio_scrobbler.git
cd beo_radio_scrobbler

Step 2: Build the Docker Image

docker build -t beo-radio-scrobbler .

Step 3: Run the Container

Option A: Using Environment Variables

docker run -d \
  --name beo-radio-scrobbler \
  --restart unless-stopped \
  -e LASTFM_API_KEY=your_lastfm_api_key \
  -e LASTFM_API_SECRET=your_lastfm_api_secret \
  -e LASTFM_USERNAME=your_lastfm_username \
  -e LASTFM_PASSWORD=your_lastfm_password \
  -e LOCAL_TIMEZONE=Europe/Berlin \
  -e RUN_MODE=production \
  -e BEO_IP=192.168.1.100 \
  -e LOGLEVEL=INFO \
  -v $(pwd)/appdata:/app/appdata \
  beo-radio-scrobbler

Option B: Using Docker Compose

Create a docker-compose.yml file:

version: '3.8'

services:
  beo-radio-scrobbler:
    build: .
    container_name: beo-radio-scrobbler
    restart: unless-stopped
    environment:
      - LASTFM_API_KEY=your_lastfm_api_key
      - LASTFM_API_SECRET=your_lastfm_api_secret
      - LASTFM_USERNAME=your_lastfm_username
      - LASTFM_PASSWORD=your_lastfm_password
      - LOCAL_TIMEZONE=Europe/Berlin
      - RUN_MODE=production
      - BEO_IP=192.168.1.100
      - LOGLEVEL=INFO
    volumes: 
      - ./appdata:/app/appdata
    network_mode: host  # Required to access local network devices

Option A+ & B+: .env. in config folder

Create a .env file (instructions above) and place it inside appdata/config/. Where appdata/ is defined by '-v' of 'OPTION A' or volumes of 'OPTION B'

Then run:

docker-compose up -d

Step 4: View Logs

# Docker run
docker logs -f beo-radio-scrobbler

# Docker Compose
docker-compose logs -f

Or check logs folder.

Step 5: Stop the Container

# Docker run
docker stop beo-radio-scrobbler

# Docker Compose
docker-compose down

Run Modes

The RUN_MODE environment variable controls how the application behaves:

  • production: Continuously scrobbles tracks to Last.fm (recommended for 24/7 operation)
  • detect: Detects one track and exits (useful for testing)
  • notify_me: Notification mode for debugging

Log Files

Logs are stored in the appdata/logs/ directory:

  • log_radio_scrobbler.log: Main application log (rotated daily, kept for 7 days)
  • log_scrobbles.log: Scrobble history (production mode, kept for 5 years)
  • log_detections.log: Detection log (detect modes, kept for 1 week)
  • log_notifications.log: Notification log (notify_me mode, kept for 7 days)

Troubleshooting

Can't Find Bang & Olufsen Device

  • Ensure your device is powered on and connected to the network
  • Verify the IP address in your environment variables
  • Check that your Docker container has network_mode: host (for Docker)

Authentication Errors with Last.fm

Application Exits Immediately

  • Check the logs in appdata/logs/log_radio_scrobbler.log
  • Ensure all required environment variables are set
  • Verify your RUN_MODE setting (use production for continuous operation)

Unraid Installation (Coming Soon)

Docker installation on Unraid will be supported through Community Applications once the project is published.


Documentation πŸ“š

For more detailed information, check out these documentation files:

  • Run Modes - Different operation modes (production, detect, detect_smpl, notify_me)
  • Feature Requests - Planned features and how to contribute
  • How-Tos - Guides and tutorials for common tasks

Credits & Acknowledgments

  • Initial proof of work written using requests and pylast

Third Party Libraries used:

  • pylast - Python library for Last.fm scrobbling

  • BeoNetRemote Client API - Bang & Olufsen's API for third-party integration

  • uv - An extremely fast Python package installer and resolver

  • Astral UV Docker Example - Dockerfile template and basic setup

  • python-dotenv - loading .env file

  • Arrow (Apache 2.0) - Better dates and times for Python

  • Loguru (MIT) - Python logging made simple

  • Pydantic (MIT) - Data validation using Python type hints

  • Requests (Apache 2.0) - HTTP library for Python

  • PyYAML (MIT) - YAML parser and emitter for Python

  • AI Assistance:

    • Static if-else cases improved with Claude Sonnet 4.5
    • Project structure modernized with GitHub Copilot
    • This README enhanced with GitHub Copilot πŸ€–

Supported Devices

Copied from B&O documentation (B&O 3rd Party Integration):

Bang & Olufsen Audio Systems: Beosound Stage Beosound 35 Beosound Essence 2nd Gen Beosound Core Beosound Shape Beosound Edge Beosound 1 1st / 2nd Gen Beosound 2 1st / 2nd Gen Beolink Converter NL/ML Beoplay A9 2nd / 3rd / 4th Gen Beoplay A6 Beoplay M5 Beoplay M3

Bang & Olufsen Active Loudspeakers: BeoLab 90 BeoLab 50

Mozart API - recent Bang & Olufsen Models

I do not think I want to add a "Buy me a coffee new B&O Speaker"-Button.
But if you insist on supporting with a replacement for my Beosound 1, open an issue in github...
I will add Mozart API as soon as I have replaced my Beosound 1 (which is not planned), or I have access to a Mozart API supporting device.

did something stupid and corrupted the Docker Image with latest tag. Push to Main

About

Scrobble Tracks while listening to radio on a B&O device supporting BeoNetRemote Client API.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors