Skip to content
Konstantinos Kyriakopoulos edited this page Feb 25, 2026 · 16 revisions

Foo Scrobbler for Mac

Introduction

Foo Scrobbler for Mac (foo_scrobbler_mac) is a native Last.fm scrobbling component for foobar2000 on macOS.

It submits playback metadata from foobar2000 to your Last.fm account, including artist, track title, album, and duration. Submitted data becomes part of your Last.fm listening history, where it is used to generate long-term statistics, charts, and listening trends accessible through the Last.fm web interface.

The component is implemented as a proper foobar2000 component, not a wrapper or external helper process. Its design follows foobar2000’s event model, lifetime rules, and threading constraints, with explicit attention to macOS and Apple Silicon behaviour.

Foo Scrobbler provides reliable submission of:

  • Now Playing updates for the currently playing track
  • Scrobbles that meet Last.fm eligibility rules, without background services, polling loops, or uncontrolled thread creation

Design priorities are deliberately conservative, with a strong emphasis on lightweight execution and predictable resource usage:

  • Deterministic behaviour over implicit automation
    All actions are triggered by explicit playback or user events. There is no background polling, speculative work, or hidden automation.

  • Explicit state transitions over heuristics
    Internal state changes are driven by well-defined playback boundaries, authentication events, and user actions.

  • Failure recovery over silent retry loops
    Errors are handled explicitly, with bounded retry logic and backoff to prevent runaway network activity or thread churn.

  • Minimal interaction with the playback pipeline
    Playback is observed without interfering with decoding, DSP, or output stages. Metadata handling and network submission are fully decoupled from the audio path.

All network activity is bounded, state-driven, and recoverable. Internal state is persistent, ensuring that pending scrobbles survive crashes, restarts, or forced termination without duplication or loss.

When disabled or suspended, the component becomes effectively inert.

This project targets users who prefer a predictable, inspectable scrobbling implementation that behaves as a stable subsystem of foobar2000 rather than an autonomous background agent.


Defaults and Limits

Advanced Preferences Defaults

Preferences are exposed under:

Preferences → Advanced → Tools → Foo Scrobbler

Default values:

  • Disable Now Playing updates: off
  • Submit only tracks from Media Library: off
  • Console log level: Basic
  • Dynamic sources: NP & Scrobbling

Cache and Drain Defaults

  • Cache draining is enabled by default
  • Drain cooldown: 360 seconds (disabled when cache size is less than 50)
  • Dispatch batch size: 10
  • Linear retry backoff with a maximum delay of 1 hour
  • Default daily submission budget: 2600 scrobbles

Eligibility Constants

Scrobble eligibility is governed by fixed, non-configurable rule constants:

  • Minimum track duration: 30 seconds
    Tracks shorter than this are ignored entirely.

  • Eligibility threshold: 50% of track duration
    A track must be played for at least half of its total duration.

  • Maximum eligibility cap: 240 seconds (4 minutes)
    For long tracks, eligibility is capped to avoid excessive wait times.

  • Long track classification: 480 seconds (8 minutes)
    Tracks longer than this are treated uniformly using the capped threshold.

These values are applied consistently to all tracks and are not affected by user preferences or runtime conditions.


Main Functionality

Foo Scrobbler provides Last.fm scrobbling for foobar2000 on macOS using a strictly event-driven and state-aware design. All behaviour is derived from playback events, explicit user actions, and well-defined runtime states. There is no background polling and no autonomous activity.

Playback Integration

The component listens to playback state changes such as track start, track change, pause, resume, and stop. These events are used only to update internal playback state and eligibility tracking.

Playback callbacks never perform network operations. All communication with Last.fm is deferred and handled asynchronously to ensure that audio playback remains unaffected.

Scrobble Eligibility and Submission

A track is considered for scrobbling only if all of the following conditions are met:

  • Mandatory metadata (artist and title) is present and stable
  • Track duration is known and at least 30 seconds
  • Playback progresses past the computed eligibility threshold
  • Scrobbling is enabled and the component is in an active runtime state

The eligibility threshold is computed as: threshold = min(trackDuration × 0.5, 240 seconds)

Playback time is measured using real elapsed playback time.

If playback ends before the threshold is reached, the track is discarded and never queued.
If the threshold is reached while submission is temporarily blocked, eligibility is remembered and resolved at the next natural playback boundary.

Scrobble submission is persistent and stateful. If a scrobble cannot be submitted immediately due to temporary conditions, it is cached locally and submitted later when conditions allow. Successfully submitted scrobbles are never duplicated.

For dynamic sources like radio stations, the eligibility threshold is 30 seconds of effective listening time. These scrobbles are never persistently queued, but are temporarily cached locally to be submitted during the first available slot (stream song change, stop, or start).

Now Playing Behaviour

Now Playing updates are handled independently from scrobble submission.

They are best-effort and non-persistent, and are sent only when:

  • Authentication is valid
  • Scrobbling is not suspended
  • Now Playing updates are enabled in preferences
  • Playback metadata is stable

Now Playing updates are:

  • Never cached
  • Never retried
  • Immediately suppressed on authentication failure or suspension

Disabling Now Playing updates does not affect scrobble eligibility or submission.

Media Library Filtering

When Submit only tracks from Media Library is enabled, scrobbling is restricted strictly to tracks that belong to the foobar2000 Media Library.

Tracks outside the Media Library:

  • Do not generate Now Playing updates
  • Are not tracked for scrobble eligibility
  • Are never cached or submitted

This filtering is applied early and prevents further processing for excluded tracks.

Tag Handling Behaviour

Foo Scrobbler treats track tags strictly as input data. It does not rewrite, normalize, split, merge, or infer metadata.

The following fields are required:

  • Artist
  • Title

If either is missing or invalid after validation, the track is ignored and not submitted.

Each core field can be mapped to alternative tag sources:

  • Artist source: ARTIST, ALBUM ARTIST, PERFORMER, COMPOSER, CONDUCTOR
  • Album Artist source: ALBUM ARTIST, ARTIST, PERFORMER, COMPOSER, CONDUCTOR
  • Title source: TITLE, WORK, MOVEMENT, PART, SUBTITLE
  • Album source: ALBUM, RELEASE, WORK, ALBUMTITLE, DISCNAME

If a selected source is empty, an optional fallback can restore ARTIST or ALBUM as appropriate.

An optional rule allows treating "Various Artists" (Album Artist only) as empty, enabling cleaner submissions for compilation releases.

Before use, tag values are trimmed and validated. Empty values, placeholder text (e.g. "unknown"), malformed data, or non-textual garbage are rejected. Metadata is never repaired or guessed.

Once a track becomes eligible, its captured metadata and duration are treated as immutable. Cached retries always submit the originally captured values, even if tags are edited later.

Foo Scrobbler passes validated metadata exactly as provided by foobar2000.

Menu Commands

Foo Scrobbler integrates into the foobar2000 menu under:

Playback → Last.fm

Menu entries are state-aware and appear only when applicable.

  • Authenticate
    Initiates or completes the Last.fm authentication process. The command is hidden once authentication is valid.

  • Suspend Scrobbling
    Immediately disables all Now Playing updates and scrobble submission without clearing cached data.

  • Resume Scrobbling
    Appears only when scrobbling is suspended. Restores normal operation and allows cached scrobbles to be submitted according to internal rules.

Menu commands are non-blocking and never perform synchronous network operations.


Lightweight, Efficient and Non-Intrusive Behaviour

Foo Scrobbler is designed to remain effectively invisible during normal operation. It does not alter playback behavior, timing, or metadata flow beyond what is strictly required for scrobbling.

There are no background services, helper applications, or persistent UI elements. The component exists entirely within foobar2000’s process and follows its lifecycle exactly.

Playback Safety

All playback-related callbacks are treated as read-only observation points.

  • No network requests are performed from playback callbacks
  • No blocking operations are executed on playback-related threads
  • No additional latency is introduced into the audio pipeline

All time-consuming or failure-prone operations are deferred and handled asynchronously.

Threading Discipline

The component uses a bounded and predictable threading model.

  • No detached or fire-and-forget threads
  • No unbounded thread creation
  • No busy-waiting or polling loops

Background work is scheduled only in response to explicit events. When idle, the component consumes no CPU time.

Network Restraint

Network activity is strictly demand-driven.

  • No periodic polling
  • No keep-alive traffic
  • No speculative retries

Requests are issued only when a submission is eligible and permitted. On failure, retries are deferred and rate-limited according to internal backoff rules.

Shutdown and Failure Behaviour

On application shutdown or component unload:

  • Pending work is safely terminated
  • No blocking shutdown hooks are executed
  • No background threads outlive the component

Unexpected termination does not corrupt cached data or cause duplicate submissions on restart.


Abstract - Modular Design

Foo Scrobbler Architecture

Foo Scrobbler is built from a small number of clearly separated parts, each responsible for one job only. This keeps behavior predictable and makes problems easier to isolate and recover from when something goes wrong.

Rather than acting as a single monolithic system, the component behaves as a set of cooperating pieces with clear boundaries between playback tracking, user interaction, network communication, and data storage.

Clear Separation of Responsibilities

Different aspects of scrobbling are handled independently:

  • Playback is observed and tracked without affecting audio output
  • User commands and preferences control behavior explicitly
  • Communication with Last.fm is isolated from playback logic
  • Pending scrobbles are stored safely until submission succeeds
  • Background work and retries are handled in a controlled way

Because each part has a single responsibility, changes or failures in one area do not cascade into others.

One-Way, Predictable Data Flow

Foo Scrobbler Data Flow

Information moves through the component in a single, well-defined direction:

  1. Playback events update internal tracking state
  2. Tracks that become eligible are recorded as submission entries
  3. These entries are submitted to Last.fm
  4. Local storage is updated only after successful submission

Network or storage state never feeds back into playback behavior.

Failures Stay Contained

Errors are handled where they occur and do not leak into unrelated parts of the system:

  • Network issues do not interrupt playback tracking
  • Authentication problems do not damage cached data
  • Storage issues do not block playback or menu interaction

Each part can pause, fail, or recover independently without forcing a full reset or requiring user intervention.

Predictable and Inspectable Behavior

All internal state changes are explicit and event-driven.

State transitions occur only in response to:

  • Playback boundaries
  • User commands
  • Explicit network responses

There are no hidden mode switches or timer-based behaviour.


Network Error Handling

Foo Scrobbler Network Errors

Network interaction is treated as an unreliable external dependency.
Failures are expected, classified, and handled explicitly without affecting playback, UI responsiveness, or internal state consistency.

Network errors never alter playback tracking, timing, or eligibility logic.

Authentication Failures

Authentication errors are treated as a hard stop.

When credentials are invalid, expired, or revoked:

  • All scrobble submission halts immediately
  • Cached scrobbles are preserved without modification
  • Now Playing updates are suppressed entirely
  • No retry attempts are made using invalid credentials

The component does not attempt to refresh or repair credentials automatically.
Recovery occurs only after the user successfully completes re-authentication via the menu.

Transport and Network Failures

Transient network failures such as connection errors, timeouts, or unreachable hosts are handled conservatively:

  • The current submission attempt is aborted
  • The affected scrobble remains cached
  • No further submissions are attempted in the same cycle

Playback tracking continues uninterrupted while network activity is paused.

Unclassified API responses (bounded retry, then discard)

  • Some Last.fm responses contain valid JSON and an API error code that is not mapped to a specific handling category (anything other than the known handled cases like invalid session and selected temporary conditions); these are treated as “unclassified” results (internally treated as OTHER_ERROR).
  • For cached scrobbles, retries follow the normal backoff policy, but are bounded by a small fixed ceiling:
    • The queue entry is discarded after 5 consecutive unclassified responses for that same scrobble.
    • This prevents endless retry loops when Last.fm repeatedly returns the same unclassified response, or when API changes / undocumented conditions are encountered.
  • If a later attempt returns a temporary or transport-level failure, the “unclassified” streak is reset (so transient network issues do not push an entry toward discard).
  • Applies to persistently queued scrobbles only; Now Playing remains best-effort and non-persistent.
  • Logs indicate discard after repeated unclassified responses, including the counter.

Backoff and Retry Discipline

Retries are bounded, state-driven, and rate-aware.

  • Retry attempts use linear backoff with an upper time cap
  • Only one submission attempt is active at a time
  • Failures immediately pause further drain activity
  • Retries are additionally bounded when Last.fm repeatedly returns unclassified API responses for a specific queued scrobble.

There are no aggressive retry loops, retry storms, or parallel submission attempts.

Recovery Conditions

Submission resumes only in response to explicit recovery events:

  • Successful re-authentication
  • Explicit user action (resuming scrobbling)
  • Restoration of network availability during a scheduled drain opportunity

There are no background probes, heartbeat timers, or speculative retries.

Failure Isolation Guarantees

Network and authentication failures are isolated from the rest of the component:

  • Playback behavior is unchanged
  • UI state remains stable and responsive
  • Cached data is not corrupted or reordered

Failures do not cascade into unrelated subsystems and do not require application restart or manual cleanup.

Foo Scrobbler (foo_scrobbler_mac) Wiki

Clone this wiki locally