The bridge to everything else
v0.5.1.1It's as simple as:
# Get current track info (just open in browser)
http://localhost:8080/nowplaying
# Get album artwork (returns image)
http://localhost:8080/nowplaying/artwork
# Browse all albums in library
http://localhost:8080/library/albums
# Skip to next track
curl -X POST http://localhost:8080/player/next
Try it now: /nowplaying • /nowplaying/artwork • /library/albums
Web-based remote control. All actions use POST-Redirect-GET pattern. Supports keyboard shortcuts: Space (play/pause), ←/→ (prev/next), ↑/↓ (volume ±1%), Shift+↑/↓ (volume ±5%), M (mute).
Full web dashboard with now playing, controls, volume, ratings, shuffle/autodj/repeat toggles. Panel visibility, order, and collapsible grouping are configurable via dashboardLayout in settings.
volup/voldown - Adjust volume by 5%.
volup1/voldown1 - Adjust volume by 1% (fine control).
shuffle=enable, shuffle-off=disable both, autodj=start AutoDJ, repeat=cycle mode
love - Toggle love tag on current track.
rate/0 - Toggle bomb (don't play). Click on → click off.
rate/1-5 - Toggle star rating. Click to set, click same to clear.
ban - Ban track from shuffle, skip to next (undo: skip back within 10s).
setban - Set ban flag without skipping.
mood - Set AutoQ mood channel (form: mood=Energetic).
react/fire|heart|like|dislike|ban - Submit reaction. Fire triggers queue refresh.
refresh-queue - Refresh vibe list and queue tracks.
influence/artist|genre/up|down - Thumbs up/down for artist or genre.
MBXHub can serve custom HTML pages from a configurable directory. This enables building custom web UIs that use the REST API.
/pages/ with correct Content-Type headers.
Default pages are extracted on first run and can be customized without losing changes on updates.
Set pagesPath in mbxhub.json to customize the pages directory:
{
"pagesPath": "C:\\MyCustomPages"
}
Default location: %APPDATA%\MusicBee\MBXHub\pages\
Set defaultPage to change the root URL redirect:
{
"defaultPage": "/pages/player.html"
}
Options: /dashboard (default), /pages/player.html, /pages/partymode/, /pages/nowplaying.html, or any custom page.
Kiosk Mode: Lock the display to a single page/app. All navigation redirects to the default page.
{
"defaultPage": "/pages/partymode/",
"kioskMode": true
}
Multi-page apps work: sub-paths are allowed (e.g., /pages/partymode/ allows /pages/partymode/guest.html). API calls and resources still work. Only editable in mbxhub.json (not exposed via API).
Serves index.html from the pages directory
Serves any file from the pages directory (HTML, CSS, JS, images, fonts)
/pages/index.html - Landing page listing available views/pages/player.html - Full-featured desktop player with:
/pages/nowplaying.html - Focused now-playing view with artwork, lyrics, and queue. Auto-stacks below 800px with accordion sections (Now Playing, Queue, Lyrics) — expanding one collapses the others/pages/browse.html - Library browser with 7 responsive tabs (Albums/Artists/Genres/Playlists/Podcasts/Radio/Moods), drilldown navigation, diacritic and punctuation normalized fuzzy search, album art grid, batch queue, and playlist picker/pages/config.html - Settings and configuration for dashboard layout and AutoQ parameters/pages/autoq.html - AutoQ Tuning Console with mixer-style sliders for scoring weights and normalization ranges/pages/phantom.html - Devialet Phantom speaker control: transport, volume, source selectionTo customize pages:
player.html or create new HTML filesMBXHub serves /llms.txt - an AI-friendly API reference. Use it with Claude or any AI to generate custom pages:
/nowplaying, /player/play) that work on any MBXHub instance%APPDATA%\MusicBee\MBXHub\pages\http://localhost:8080/pages/partyon.htmlThe generated code uses relative URLs, so it works on your local MBXHub without modification.
MBXHub advertises itself on the local network via three protocols so clients can find it automatically.
DnsServiceRegister API.
Requires Windows 10 1809+; gracefully skipped on older systems. Registered as MBXHub (Name)._http._tcp.local with TXT records for path and version.
UPnP device description XML. Contains device info, service URLs, and presentation URL.
WS-Discovery metadata exchange endpoint. Windows sends a SOAP GetMetadata request after discovering MBXHub via UDP Probe; response includes PresentationUrl pointing to /dashboard.
| Field | Description |
|---|---|
| deviceType | urn:halrad-com:device:MBXHub:1 |
| friendlyName | MBXHub instance identifier |
| presentationURL | Dashboard URL (/dashboard) |
| controlURL | REST API base (/api) |
| eventSubURL | WebSocket endpoint (/ws) |
| Port | Protocol | Purpose |
|---|---|---|
| 8080 | TCP | REST API + WebSocket |
| 1900 | UDP | SSDP (UPnP discovery) |
| 3702 | UDP | WS-Discovery (Windows Network folder) |
| 5353 | UDP | mDNS/DNS-SD (managed by OS) |
Use the Settings → Firewall panel or CLI: MBXHub.exe firewall add --name MBXHub --tcp 8080 --udp 1900,3702 --urlacl 8080. mDNS port 5353 is managed by the Windows DNS client service.
discoveryEnabled (default: true), discoveryName (default: empty = machine name).
The discovery name is used as the friendly name across all protocols and in GET /system/version.
Returns system status and enabled modules
Returns API version information. Includes host field with the configured discovery name (or machine name if not set).
Health check endpoint (alias: /system/ping)
Returns enabled feature flags: banlist, ratings, loved, reactions, streaming, autoq. Clients use these to show/hide UI elements.
Get or update hub configuration (ports, enabled modules)
Returns schema for all configurable settings. Each entry includes:
| Field | Description |
|---|---|
key | Dotted key path (e.g. autoQ.batchSize) |
type | bool, int, double, string, enum |
category | Grouping: General, AutoQ, Scoring, Dashboard, API, etc. |
tier | Standard, Advanced, or Expert |
description | Human-readable explanation |
default | Default value |
current | Current live value |
min, max, step | Range constraints (numeric types only) |
options | Valid values (enum types only) |
requiresRestart | Whether a restart is needed for the change to take effect |
Security: blocked when disableRemoteConfig is true (403) or during party mode (403). The /pages/settings.html page consumes this endpoint.
Update configurable settings via dotted key paths. Only properties decorated with [ConfigSetting] can be modified.
Body: JSON object with dotted keys:
{"autoQ.batchSize": 10, "apiReadOnlyMode": true}
Security: blocked by apiReadOnlyMode (403), disableRemoteConfig (403), and party mode (403). POST is an alias for PUT.
Get or set the default redirect page (body: {"defaultPage":"/pages/player.html"})
Generate QR code PNG image for the MBXHub base URL. Optional ?url= for custom target.
Returns current track info with full metadata
// Response:
{
"success": true,
"data": {
"playing": true,
"url": "file:///C:/Music/Artist/Album/track.mp3",
"title": "Love Don't Live Here",
"artist": "Breaking Rust",
"album": "Greatest Hits",
"albumArtist": "Breaking Rust",
"year": "2024",
"genre": "Rock",
"trackNo": "3",
"discNo": "1",
"rating": "4.5",
"love": true,
"duration": 234000,
"position": 45000
}
}
Tag fields: TrackTitle, Artist, Album, AlbumArtist, Year, Genre, Rating, RatingLove, Comment, Composer, Conductor, TrackNo, DiscNo, Lyrics, Publisher
Properties: Bitrate, SampleRate, Channels, Duration, Size, DateAdded, DateModified, PlayCount, SkipCount, LastPlayed
Examples:
GET /nowplaying/tag?field=Artist → {"value": "Breaking Rust"}
GET /nowplaying/tag?field=Album → {"value": "Love Don't Live Here"}
GET /nowplaying/tag?field=Genre → {"value": "Rock"}
GET /nowplaying/tag?field=TrackNo → {"value": "3"}
GET /nowplaying/property?type=Bitrate → {"value": "320"}
GET /nowplaying/property?type=Duration → {"value": "234000"} (ms)
GET /nowplaying/property?type=PlayCount → {"value": "42"}
Returns album artwork as binary image or URL
FFT spectrum and waveform data for visualizations
Returns the now playing list. Supports ?offset=0&limit=50
// Response:
{
"success": true,
"data": {
"currentIndex": 5,
"total": 150,
"offset": 0,
"limit": 50,
"tracks": [
{
"index": 0,
"url": "file:///C:/Music/track1.mp3",
"title": "First Track",
"artist": "Artist Name",
"album": "Album Name",
"duration": 234000
},
// ... more tracks
]
}
}
// POST /queue/add - Add tracks to queue (position: "next" or "last")
{"urls": ["file:///C:/Music/song1.mp3", "file:///C:/Music/song2.mp3"], "position": "last"}
// or single track:
{"url": "file:///C:/Music/song.mp3", "position": "next"}
// Response: {"success": true, "data": {"result": true, "added": 2, "position": "last"}}
// POST /queue/playnow - Play track immediately
{"url": "file:///C:/Music/song.mp3"}
// POST /queue/play - Play track at index
{"index": 3}
// POST /queue/move - Move track in queue
{"from": 5, "to": 2}
Query library with ?query=, ?artist=, ?albumArtist=, ?album=, ?genre=, ?sort=
// GET /library/files?artist=Breaking%20Rust&limit=10
{
"success": true,
"data": {
"total": 42,
"offset": 0,
"limit": 10,
"tracks": [
{
"url": "file:///C:/Music/Breaking Rust/track.mp3",
"title": "Love Don't Live Here",
"artist": "Breaking Rust",
"album": "Greatest Hits",
"duration": 234000
},
// ... more tracks
]
}
}
Full-text search: ?q=search+term&sort=alpha (searches title, artist, album; diacritic and punctuation normalized so "cafe" matches "Café" and "acdc" matches "AC/DC")
/library/albums/detailed returns albums with firstTrackUrl, year, and dateAdded for artwork lookups and sorting. Eliminates per-album /library/files?limit=1 round-trips. Params: ?offset=&limit=
/library/album-artists returns distinct album artists. Params: ?offset=&limit=
Albums for an album artist with year, track count, and firstTrackUrl for artwork. Params: ?albumArtist= (required), ?sort=alpha|year|year-asc
// GET /library/albums/by-artist?albumArtist=Pink%20Floyd&sort=year
{
"success": true,
"data": {
"albumArtist": "Pink Floyd",
"total": 3,
"sort": "year",
"albums": [
{
"name": "The Dark Side of the Moon",
"year": "1973",
"count": 10,
"firstTrackUrl": "file:///C:/Music/Artist/Album/track.mp3"
}
]
}
}
Note: URL-encode the file path in the URL (e.g., /library/file/file%3A%2F%2F%2FC%3A%2FMusic%2Ftrack.mp3)
// GET /library/file/{url} - Extended track metadata (includes playCount, lastPlayed, etc.)
{
"success": true,
"data": {
"url": "file:///C:/Music/Artist/Album/track.mp3",
"title": "Love Don't Live Here",
"artist": "Breaking Rust",
"album": "Greatest Hits",
"duration": 234000,
"albumArtist": "Breaking Rust",
"genre": "Rock",
"year": "2024",
"trackNo": "3",
"discNo": "1",
"rating": "4.5",
"composer": "J. Smith",
"bitrate": "320",
"format": "MPEG Audio",
"sampleRate": "44100",
"playCount": 42,
"dateAdded": "2024-01-15",
"lastPlayed": "2024-01-20"
}
}
// PUT /library/file/{url} - Update metadata (fields: title, artist, album, albumArtist, genre, year, trackNo, discNo, composer, comment, rating)
{"rating": "5", "comment": "Great track!"}
// Response: {"success": true, "data": {"result": true, "updated": ["rating", "comment"]}}
// POST /library/artwork/batch - Batch artwork fetch (max 50 URLs per request)
// Request: {"urls": ["D:\\Music\\track1.mp3", "D:\\Music\\track2.flac"]}
// Response: {"success": true, "data": {"D:\\Music\\track1.mp3": "data:image/jpeg;base64,/9j/...", "D:\\Music\\track2.flac": null}}
// GET /playlists - List all playlists
{
"success": true,
"data": {
"total": 5,
"playlists": [
{"url": "playlist://Favorites", "name": "Favorites", "trackCount": 120},
{"url": "playlist://Workout", "name": "Workout", "trackCount": 45},
// ... more playlists
]
}
}
// POST /playlists - Create new playlist
{"name": "New Playlist", "folder": "", "files": ["file:///C:/Music/song1.mp3"]}
// Response: {"success": true, "data": {"url": "playlist://New Playlist", "name": "New Playlist", "trackCount": 1}}
// PUT /playlists/{url} - Replace playlist contents
{"files": ["file:///C:/Music/song1.mp3", "file:///C:/Music/song2.mp3"]}
// GET /playlists/{url}/files - Get playlist tracks (supports pagination: ?offset=0&limit=50)
{
"success": true,
"data": {
"total": 120,
"offset": 0,
"limit": 50,
"tracks": [
{"index": 0, "url": "file:///...", "title": "Song", "artist": "Artist", "album": "Album", "duration": 234000},
// ... more tracks
]
}
}
// POST /playlists/{url}/files - Add tracks to playlist
{"urls": ["C:\\Music\\song.mp3"]}
List podcast subscriptions. Query: ?query=
Get MusicBee settings overview
Gracefully close MusicBee. Requires allowRemoteExit=true in settings.
Optional body to schedule a restart via Windows Task Scheduler before closing:
// Request (optional):
{
"restart": true, // schedule restart before closing (default: false)
"delay": 22 // seconds to wait before restarting (default: 22, range: 1-300)
}
// Response:
{
"success": true,
"data": {
"message": "MusicBee restarting in 10s",
"restart": true,
"delay": 22
}
}
Restart MusicBee. Convenience alias for /app/exit with restart=true. Requires allowRemoteExit=true.
// Request (optional):
{
"delay": 22 // seconds to wait before restarting (default: 22, range: 1-300)
}
Turn MusicBee into a social jukebox. Guests scan a QR code, enter a PIN, and request songs from their phone. The DJ controls playback while a TV display shows artwork, lyrics, and a live feed of requests and joins.
Roles: Guest (browse/request), DJ (full control), Display (TV mode)
Get current party state (active, request count).
Start a party session.
// Request body:
{"guestPin": "1234", "djPin": "5678"}
// djPin is optional - defaults to guestPin
End the current party session.
Validate PIN and get role. If nickname provided, announces join in feed.
// Response:
{"success": true, "data": {"valid": true, "role": "guest"}}
// role: "guest" or "dj"
Verify if a PIN grants DJ access. Used by DJ page login.
// Request body:
{"pin": "5678"}
// Response:
{"success": true, "data": {"valid": true}}
// valid is true only if PIN matches DJ PIN
Submit a vote (thumbs up/down) with guest attribution. Records in feed and forwards to influences if AutoQ available.
// Request body:
{"type": "++", "target": "Artist", "value": "Daft Punk", "nickname": "Haro"}
// type: "++"=more, "--"=less; target: "Artist" or "Genre"
Submit a song request (adds to queue).
// Request body:
{"url": "C:\\Music\\song.mp3", "nickname": "Haro"}
// Response includes requestId, title, artist
Get party feed (joins, requests, votes, reactions - newest first).
// Response:
{"success": true, "data": {
"items": [
{"type": "join", "nickname": "Haro", "timestamp": "..."},
{"type": "request", "nickname": "Haro", "title": "Song", "artist": "Artist", "timestamp": "..."},
{"type": "voteup", "nickname": "Haro", "artist": "Daft Punk", "timestamp": "..."},
{"type": "votedown", "nickname": "Haro", "artist": "Nickelback", "timestamp": "..."},
{"type": "reaction", "nickname": "Haro", "emoji": "🔥", "title": "Song", "artist": "Artist", "timestamp": "..."}
]
}}
Get recent song requests only (for DJ page).
Generate QR code PNG image. Auto-includes PIN if party is active.
Returns the caller's role based on IP: host (loopback), dj (verified DJ PIN), or guest.
Party Mode settings are configured in MusicBee via Settings → Network → Party Mode...
| Setting | Default | Description |
|---|---|---|
protectMetadata | true | Block metadata writes (love, rate, tag edits) for ALL users including DJ |
rateLimitEnabled | true | Enable per-IP rate limiting on party endpoints |
rateLimitRequestsPerMinute | 5 | Max song requests per minute per IP (when rate limiting enabled) |
rateLimitVotesPerMinute | 5 | Max votes per minute per IP (when rate limiting enabled) |
rateLimitPinAttemptsPerMinute | 5 | Max PIN validation attempts per minute per IP |
trustForwardedFor | false | Trust X-Forwarded-For header for client IP. Enable only behind a reverse proxy. |
Protect Metadata: When enabled, blocks PUT /track/{}/love, PUT /track/{}/rating,
and PUT /track/{}/tags for everyone during the party. Use this to prevent guests from permanently modifying
your library ratings. The setting also blocks the DJ—toggle it off temporarily if you need to make edits.
Built-in pages at /pages/partymode/:
index.html - PIN + nickname entry (guest join page)guest.html - Browse 7 tabs (Albums, Artists, Genres, Playlists, Podcasts, Radio, Moods), fuzzy search, request songs, vote on vibesdj.html - Start/stop party, set PINs, manage queue, see requests & vibesdisplay.html - TV mode: artwork, lyrics, request feed, QR code, floating reactionsleaderboard.html - Party stats: guest activity, top tracks, reaction countsIntelligent queue system. AutoQ combines TrueShuffle rules, mood analysis, reactions, and influences to automatically queue tracks that match the room's energy.
TrueShuffle manages the shuffle cycle — play rules, cycle tracking, and queue constraints.
Returns 503 SERVICE_UNAVAILABLE if TrueShuffle/AutoQ not enabled.
Get shuffle cycle status
// Response:
{
"success": true,
"data": {
"enabled": true,
"totalTracks": 1000,
"playedCount": 250,
"remainingCount": 750,
"percentComplete": 25.0,
"cycleStarted": "2024-01-01T00:00:00Z"
}
}
Reset the shuffle cycle. All tracks become unplayed.
Tracks played/remaining in shuffle cycle. Query: ?offset=&limit=
Permanently excluded tracks. Banned tracks are never queued by AutoQ.
Returns 503 SERVICE_UNAVAILABLE if TrueShuffle/AutoQ not enabled.
Get list of banned tracks. Query: ?offset=&limit=
// Response:
{
"success": true,
"data": {
"total": 5,
"offset": 0,
"limit": 50,
"tracks": [
{
"url": "file:///C:/Music/corrupted.mp3",
"reason": "Audio corruption detected at 2:30",
"bannedAt": "2024-01-01T12:00:00Z"
}
]
}
}
// POST /banlist - Ban a track
{"url": "file:///C:/Music/track.mp3", "reason": "Corrupted audio at 2:30"}
// DELETE /banlist/{url} - Unban a track (URL-encode the file path)
Influence rules shape AutoQ scoring — Pandora-style thumbs up/down on artists and genres.
Unlike bans (permanent, track-specific), influences are resettable metadata preferences.
Returns 503 if TrueShuffle/AutoQ not enabled.
Negative (--): Hard exclude matching tracks. Positive (++): Preference boost (future).
Get list of all influences. Query: ?offset=&limit=
// Response:
{
"success": true,
"data": {
"total": 2,
"offset": 0,
"limit": 50,
"influences": [
{
"type": "--",
"target": "Genre",
"value": "Audiobook",
"timestamp": "2024-01-15T10:30:00Z"
},
{
"type": "++",
"target": "Artist",
"value": "The Beatles",
"timestamp": "2024-01-15T10:35:00Z"
}
]
}
}
Get current track's genre/artist and any matching influences (for UI state).
// Response:
{
"success": true,
"data": {
"genre": "Rock",
"artist": "Pink Floyd",
"genreInfluence": null,
"artistInfluence": "++"
}
}
// POST /influences - Add an influence
{"type": "--", "target": "Genre", "value": "Audiobook"}
// type: "++" (more) or "--" (less/exclude)
// target: "Genre" or "Artist"
// DELETE /influences/Genre/Audiobook - Remove specific influence
// POST /influences/clear - Clear all influences (reset preferences)
Get AutoQ status and configuration.
// Response:
{
"success": true,
"data": {
"enabled": true,
"mode": "autopilot",
"soloMode": false,
"vibeListCount": 10,
"vibeListPreview": [
{ "title": "Uptown Funk", "artist": "Mark Ronson", "score": 12.5 }
]
}
}
Start AutoQ. Begins monitoring queue and adding tracks when needed.
// Optional request body:
{ "mode": "autopilot" }
// Modes: "autopilot" (default), "djassist" (aliases: "dj", "assist", "auto")
Stop AutoQ. Queue continues playing but no new tracks are added automatically.
Reset AutoQ session state (clears reactions, taste vector, ban list). DJ-only in party mode.
Force refresh vibe list. Returns updated track count.
// Response:
{
"success": true,
"data": { "message": "Vibe list refreshed", "count": 100 }
}
Pick the next track from the vibe list without queueing it.
// Response:
{
"success": true,
"data": { "url": "C:\\Music\\track.mp3", "title": "Uptown Funk", "artist": "Mark Ronson" }
}
Unban a track, allowing it back into the vibe list.
// Request:
{ "url": "C:\\Music\\track.mp3" }
// Response:
{
"success": true,
"data": { "result": true, "url": "C:\\Music\\track.mp3" }
}
Check if the currently playing track is banned.
// Response:
{
"success": true,
"data": { "url": "C:\\Music\\track.mp3", "isBanned": false }
}
Get available mood channels with arousal/valence coordinates. Channels are customizable via autoQ.moodChannels in mbxhub.json.
// Response:
{
"success": true,
"data": {
"currentMood": "Energetic",
"channels": [
{ "name": "Energetic", "emoji": "🔥", "arousal": 0.90, "valence": 0.80 },
{ "name": "Chill", "emoji": "😌", "arousal": 0.35, "valence": 0.65 }
]
}
}
Browse tracks matching a mood channel. Returns scored tracks sorted by mood similarity.
channel | Mood channel name (e.g. "Energetic", "Chill") |
limit | Max results (default: 200, max: 500) |
// GET /autoq/moods/browse?channel=Energetic&limit=50
// Response:
{
"success": true,
"data": {
"channel": "Energetic",
"emoji": "🔥",
"total": 50,
"tracks": [
{ "url": "...", "title": "...", "artist": "...", "album": "...", "trackNo": "3", "moodMatch": 0.92 }
]
}
}
Raw mood data for the current track (or any track via ?url=). Returns Essentia features, percentile ranks, computed valence/arousal, and best mood channel match.
// Response (Essentia-analyzed track):
{
"success": true,
"data": {
"valence": 0.5003,
"arousal": 0.5794,
"source": "essentia",
"hasEssentiaData": true,
"moodChannel": "Upbeat",
"moodEmoji": "😊",
"raw": {
"bpm": 119.95,
"mode": "major",
"loudness": -10.23,
"spectralCentroid": 1284.56,
"spectralFlux": 0.000342,
"spectralRms": 0.001245,
"spectralFlatness": 0.000089,
"danceability": 1.45,
"onsetRate": 2.87,
"zeroCrossingRate": 0.058,
"dissonance": 0.4123,
"pitchSalience": 0.3856,
"chordsChangesRate": 0.0312,
"mfcc": 142.56
},
"percentiles": {
"bpm": 0.52, "loudness": 0.61, "centroid": 0.48,
"flux": 0.35, "dance": 0.67, "onset": 0.55,
"zcr": 0.42, "rms": 0.58, "dissonance": 0.44,
"salience": 0.39, "chords": 0.51, "mfcc": 0.63
},
"confidence": 0.82,
"confidenceLabel": "high",
"genreProfile": "electronic"
}
}
// Response (fallback — no Essentia data):
{
"success": true,
"data": {
"valence": 0.65,
"arousal": 0.70,
"source": "fallback",
"hasEssentiaData": false,
"moodChannel": "Energetic",
"moodEmoji": "🔥",
"confidence": 0.45,
"confidenceLabel": "medium",
"genreProfile": null
}
}
Set target mood for Mood mode. Pass empty channel to disable mood mode.
// Request:
{ "channel": "Energetic" }
// Response:
{
"success": true,
"data": {
"message": "Mood set to Energetic",
"mode": "mood",
"mood": { "name": "Energetic", "emoji": "🔥", "arousal": 0.90, "valence": 0.80 }
}
}
Reload the mood channel cache from disk (re-reads Essentia data).
Bulk write mood tags to a MusicBee custom field. Only processes Essentia-analyzed tracks. Requires autoQ.moodTagField to be configured (e.g. "Custom1").
// Response:
{
"success": true,
"data": { "updated": 342 }
}
Get current vibe list (candidate tracks with scores). Query: ?count=50
// Response:
{
"success": true,
"data": {
"count": 50,
"tracks": [
{
"url": "C:\\Music\\track.mp3",
"title": "Uptown Funk",
"artist": "Mark Ronson",
"genre": "Funk",
"score": 12.5
}
]
}
}
Discover tracks adjacent to the current taste profile, grouped by category. Query: ?limit=100&groupBy=auto|genre|artist|mood
// Response:
{
"success": true,
"data": {
"profile": {
"topGenres": [{ "name": "Rock", "weight": 1.0 }],
"topArtists": [{ "name": "Foo Fighters", "weight": 0.85 }],
"bpmRange": [90, 160],
"mood": "Energetic",
"influenceCount": 3,
"reactionCount": 12
},
"groupBy": "genre",
"groups": [{
"label": "Rock",
"reason": "genre",
"count": 15,
"tracks": [{
"url": "C:\\Music\\track.mp3",
"title": "Everlong",
"artist": "Foo Fighters",
"genre": "Rock",
"score": 8.2,
"reasons": ["genre match", "artist match"]
}]
}],
"total": 45
}
}
Find tracks similar to a seed track by metadata and feature distance. Query: ?url={trackUrl}&limit=20
// Response:
{
"success": true,
"data": {
"seed": {
"url": "C:\\Music\\seed.mp3",
"title": "Everlong",
"artist": "Foo Fighters",
"genre": "Rock"
},
"tracks": [{
"url": "C:\\Music\\similar.mp3",
"title": "Learn to Fly",
"artist": "Foo Fighters",
"genre": "Rock",
"similarity": 0.6,
"reasons": ["same genre", "same artist", "similar BPM"]
}]
}
}
Tiered reactions for the now playing track. Each emoji has a different score weight.
| Emoji | Name | Score | Description |
|---|---|---|---|
| 🔥 | fire | +3 | This track is fire! (triggers queue refresh) |
| ❤️ | heart | +2 | Love this song |
| 👍 | like | +1 | Good choice |
| 👎 | dislike | -1 | Not feeling it |
| 🚫 | ban | -100 | Skip and exclude from queue |
Submit a reaction for a track. Reactions appear as floating emojis on the Display page.
// Request body (always reacts to currently playing track):
{
"emoji": "🔥",
"nickname": "Mike"
}
// emoji: "🔥", "❤️", "👍", "👎", "🚫" or "fire", "heart", "like", "dislike", "ban"
// Response:
{
"success": true,
"data": {
"recorded": true,
"emoji": "🔥",
"trackUrl": "C:\\Music\\track.mp3",
"trackTitle": "Uptown Funk",
"trackArtist": "Mark Ronson",
"nickname": "Mike"
}
}
Get reaction history. Query: ?trackUrl= for specific track, ?limit=100
// Response:
{
"success": true,
"data": {
"count": 25,
"trackUrl": null,
"reactions": [
{
"emoji": "🔥",
"type": "fire",
"trackUrl": "C:\\Music\\track.mp3",
"trackTitle": "Uptown Funk",
"trackArtist": "Mark Ronson",
"nickname": "Haro",
"timestamp": "2026-02-01T20:45:00Z"
}
]
}
}
Get party leaderboard data: guest activity, top tracks, reaction breakdown.
// Response:
{
"success": true,
"data": {
"totalReactions": 156,
"topTracks": [
{ "url": "...", "title": "Uptown Funk", "artist": "Mark Ronson", "score": 15 }
],
"guests": [
{ "nickname": "Haro", "totalReactions": 42, "fire": 10, "heart": 15, "like": 12, "dislike": 3, "ban": 2 }
]
}
}
Get all tunable AutoQ parameters: scoring weights, reaction scores, influence scores, estimation weights, and normalization ranges. Use with Tuning Console.
Partial update of AutoQ parameters. Only provided fields are changed. Changes take effect on the next scoring pass — no restart needed. Nested objects are merged, not replaced.
// Example: change just the BPM arousal weight
PUT /autoq/settings
{"estimation": {"arousalWeightBpm": 0.30}}
AutoQ settings in mbxhub.json under the autoQ section:
| Setting | Default | Description |
|---|---|---|
enabled | false | Enable AutoQ feature |
pickMode | "weighted" | Track selection mode: off (disabled, shuffle fill only), favorites (always pick highest-scored), weighted (score-proportional random from top candidates), random (uniform random, diversity caps still apply) |
queueThreshold | 3 | Add tracks when queue drops below this |
batchSize | 5 | Tracks to add per batch |
vibeListSize | 100 | Size of candidate track pool |
moodMatchWeight | 0.4 | Weight for mood matching in scoring (0-1) |
recencyDecayLambda | 0.1 | Decay rate for recency boost on reacted tracks |
recencyPenaltyDecay | 0.1 | Decay rate for recently-played penalty |
minReplayMinutes | 30 | Minimum minutes before a track can be replayed |
diversityWindowSize | 10 | Recent tracks considered for diversity calculations |
minSessionEntropy | 0.5 | Entropy threshold before boosting diversity (0-5) |
vibeListRefreshMinutes | 30 | Minutes between automatic vibe list refreshes |
moodChannels | null | Custom mood channels (array). Uses defaults if null. |
genreQuota | 3 | Max consecutive same-genre tracks (0=disabled) |
artistQuota | 2 | Max tracks from same artist in batch (0=disabled) |
moodTagField | "Custom1" | MusicBee custom tag field for mood labels (null=disabled) |
moodTagFieldName | "AutoQ Mood" | Expected display name for the tag field (must match MusicBee config) |
Scoring weights under autoQ.scoringWeights: featureSimilarity (0.5), trackSentiment (0.25), artistSentiment (0.15), recencyPenalty (0.3), diversityPenalty (0.6), explorationBonus (0.1), influenceWeight (0.3).
Reaction scores under autoQ.reactionScores: fire (3), heart (2), like (1), dislike (-1), ban (-100).
Estimation settings under autoQ.estimation: usePercentileNormalization (true) — ranks each feature against the library for automatic 0-1 spread. When false, uses absolute ranges below. useGenreAdjustment (true) — applies genre-specific weight multipliers to V/A computation (e.g., BPM matters less for EDM, chord changes matter more for jazz). moodMatchMinSimilarity (0.5), moodComboMaxResults (3), moodComboMinScore (0.85), bpmMin (80), bpmMax (170), ratingScale (5.0), yearMin (1950), yearMax (2030), playCountLogDivisor (4.0).
Confidence scoring under autoQ.estimation: confidenceEssentiaBase (0.9), confidenceFallbackGenre (0.45), confidenceFallbackNone (0.2) — base confidence by data source. confidenceMinForTag (0.3) — skip writing mood tag when confidence is below this threshold. Confidence = sourceBase × channelProximity (how close V/A lands to nearest mood channel).
Genre profiles under autoQ.genreProfiles: dictionary of genre name → weight multiplier profile. Each profile has 14 multipliers (8 arousal: bpm, loudness, flux, centroid, dance, onsetRate, zcr, rms; 6 valence: mode, valDance, dissonance, pitchSalience, chords, mfcc). Values are multiplied against base weights then renormalized. Built-in profiles cover electronic, metal, jazz, classical, hip-hop, folk, pop/rock, and R&B. Set to null to use built-in defaults.
Valence weights (positivity, 6 active inputs) under autoQ.estimation: valenceWeightMode (0.35), valenceWeightCentroid (0.0 — centroid is an arousal feature), valenceWeightDance (0.05), valenceWeightFlatness (0.0), valenceWeightDissonance (0.25), valenceWeightPitchSalience (0.10), valenceWeightChords (0.15), valenceWeightMfcc (0.10). Mode scoring: modeScoreMajor (0.85), modeScoreMinor (0.4).
Arousal weights (energy/intensity, 8 inputs) under autoQ.estimation: arousalWeightBpm (0.25), arousalWeightLoudness (0.20), arousalWeightFlux (0.15), arousalWeightCentroid (0.05), arousalWeightDance (0.02), arousalWeightOnsetRate (0.15), arousalWeightZcr (0.08), arousalWeightRms (0.10).
Normalization ranges (used when percentile off) under autoQ.estimation: centroidMin/Max (500/2300 Hz), loudnessMin/Max (-23/-5 dB), onsetRateMax (5.5), zcrMax (0.12), rmsMax (0.008), chordsRateMax (0.2), mfccMin/Max (70/220).
Mood Quadrants — Arousal (energy) is the vertical axis, valence (positivity) is the horizontal. Each mood channel targets a point in this space.
| Quadrant | Profile | Typical Genres | Acoustic Traits |
|---|---|---|---|
| High arousal + high valence | Energetic, upbeat | EDM, pop, funk | Fast tempo, bright timbre, strong beats |
| High arousal + low valence | Tense, aggressive | Metal, hard rock, industrial | Distortion, high energy, dissonance |
| Low arousal + low valence | Sad, subdued | Ambient drone, slow blues, lo-fi | Slow tempo, dark timbre, soft dynamics |
| Low arousal + high valence | Calm, pleasant | Chillhop, acoustic folk, soft jazz | Warm timbre, consonance, smooth textures |
Simulate keyboard and mouse input to wake or control the host PC. Useful for remote wake scenarios. Full ARiA Documentation →
ariaEnabled: false). Returns 403 ARIA_DISABLED when disabled.
Check if ARiA input simulation is enabled
Quick wake: move mouse + send Shift key to wake sleeping/locked PC
Send keyboard input. Body: {"keys": "^a"} (Ctrl+A). Optional: {"keys": "%{F4}", "window": "Notepad"} to focus window first.
SendKeys format: ^=Ctrl, %=Alt, +=Shift. Special keys: {ENTER}, {TAB}, {ESC}, {F1}-{F12}, {UP}, {DOWN}, etc.
DuckyScript format: CTRL ALT V, SHIFT F1, ALT TAB. Modifiers: CTRL, ALT, SHIFT. Special: WIN/GUI (opens Start Menu, standalone only - not a modifier).
Focus a window by title. Body: {"window": "Notepad"} (partial match, case-insensitive)
Move: {"x":100,"y":100} (absolute) or {"dx":10,"dy":0} (relative)
Click: {"button":"left"} or {"x":500,"y":300,"button":"right"}
List available presets
Execute a preset by name (e.g., /aria/preset/RIA3)
%APPDATA%\MusicBee\mb_MBXHub.json to add/modify presets:
"ariaPresets": [
{"name": "RIA1", "script": "sndkeys(^%a)", "icon": "1"},
{"name": "DuckyDemo", "script": "sndkeys(CTRL ALT V)", "icon": "D"},
{"name": "Notify", "script": "toast(MBXHub,Hello World!)", "icon": "N"}
]
Script commands:sndkeys(keys) - SendKeys or DuckyScript: sndkeys(^a) or sndkeys(CTRL A)delay(ms) - Wait milliseconds (max 30000)click(x,y[,button]) - Mouse click: click(100,200) or click(100,200,right)volume(action) - Volume control: up, down, mute, or steps like +5/-3run(path[,args]) - Launch program: run(notepad.exe) or run(cmd.exe,/c dir)webhook(url[,method,body]) - HTTP request: webhook(http://example.com) or webhook(!http://...,POST,{}) (prefix ! for fire-and-forget)toast(msg) or toast(title,msg) - Show notificationrestart(target) - Restart: mb (MusicBee), system, or shutdownsndkeys(^a);delay(100);sndkeys(^c)
Publish MusicBee as a Windows RemoteApp, allowing the full desktop UI to be accessed from other machines via RDP. Requires Windows Pro, Enterprise, or Server — Home edition is not supported.
Check RemoteApp status. Always accessible.
Response:
{
"configured": false,
"supported": true,
"rdpEnabled": true,
"edition": "Professional",
"enabled": false,
"apiDisabled": false,
"message": "RemoteApp not configured. Run 'MBXHub.exe remoteapp setup' to configure."
}
Download a .rdp file for connecting to MusicBee as a RemoteApp. Blocked when remoteAppApiDisabled is true.
Query parameters:
hostname - Override the hostname in the .rdp file (defaults to request Host header)audioqualitymode=0, redirectprinters=1)Response: application/x-rdp file download (MusicBee.rdp)
403 when remoteAppApiDisabled is true.
Visibility: Dashboard footer link requires remoteAppEnabled and remoteAppApiDisabled: false. Footer links are configurable via dashboardFooterLinks in settings.
App program: On Windows Client (Pro/Enterprise), the .rdp file uses the full executable path from the registry.
On Windows Server, it uses the ||AppName alias for RDS published app lookup.
MBXHub.exe remoteapp setup --path "C:\MusicBee\MusicBee.exe" (requires elevation)MBXHub.exe remoteapp setup --detect (auto-detect MusicBee)MBXHub.exe remoteapp remove (remove configuration)MBXHub.exe remoteapp status (check current state)
Install-WindowsFeature RDS-Connection-Broker, RDS-Web-Access, RDS-RD-Server -IncludeManagementTools
Generic HTTP proxy for controlling LAN devices (speakers, receivers, home automation) from the browser.
Browsers enforce CORS on all cross-origin requests, and LAN devices don’t serve CORS headers —
direct fetch() from the dashboard to a device IP will silently fail. The proxy solves this by
forwarding requests server-side.
Forward an HTTP request to a LAN device. Only private IPs are allowed (RFC 1918).
Request body:
{
"method": "GET",
"url": "http://192.168.10.100/ipcontrol/v1/systems/current/sources",
"body": {}
}
method — HTTP method to use: GET, POST, or PUTurl — Full URL of the target device endpoint. Must be a private IP address.body — Optional JSON body to forward with POST/PUT requests.Response: The target device’s response is passed through verbatim (status code and body).
Errors:
POST /api/proxy → MBXHub → LAN deviceThe proxy can be disabled in Settings → API → Feature Toggles (apiDisableProxy).
When disabled, POST /api/proxy returns 404 and charm webapps that depend on it will not be able to reach LAN devices.
Serves audio files from the MusicBee library over HTTP with Range support.
Enables “Listen Here” mode in the player — the browser plays audio locally
via <audio> while MusicBee acts as the library manager.
Stream an audio file. The path must be URL-encoded and must be a file in the MusicBee library.
Path parameter: URL-encoded absolute file path (e.g. /stream/C%3A%5CMusic%5Csong.mp3).
Range support: Send Range: bytes=N-M header for partial content (required for seeking).
The server responds with 206 Partial Content and Content-Range header.
Supported formats: mp3, flac, m4a, mp4, ogg, oga, wav, opus, aac, wma, aiff, aif. Actual browser playback depends on codec support — FLAC works in Firefox/Chrome/Edge, WMA is not supported by any browser.
Security:
..) is blockedErrors:
Streaming can be disabled in Settings → API → Feature Toggles (disableStreaming).
When disabled, GET /stream/* returns 404 and the Listen Here button is hidden in the player.
Charms are configurable action buttons on the dashboard. They can open webapps, fire HTTP requests to LAN devices, or call MBXHub endpoints. The charm bar appears as a dashboard section and can be reordered/hidden like any other panel.
Each charm is a .json file in the charms/ folder inside the MBXHub data directory.
MBXHub seeds a built-in phantom.json on first run.
Simple charm (single button):
{
"id": "my-charm",
"icon": "\uD83D\uDD0A",
"label": "My Charm",
"action": "webapp /pages/my-charm.html",
"display": "both",
"msg": "My Charm"
}
Expand charm (grouped buttons, e.g. Phantom):
{
"id": "phantom",
"label": "Phantom",
"expand": [
{ "icon": "\u2699\uFE0F", "label": "Settings", "action": "webapp /pages/phantom.html", "display": "inline" },
{ "icon": "+", "label": "Volume Up", "action": "iframe-cmd volumeUp", "msg": "Vol +" },
{ "icon": "\u2013", "label": "Volume Down", "action": "iframe-cmd volumeDown", "msg": "Vol \u2013" },
{ "icon": "\uD83D\uDD07", "label": "Mute", "action": "iframe-cmd toggleMute", "msg": "Mute" }
]
}
id — Unique identifier (matches filename without extension).icon — Emoji or character displayed on the button.label — Tooltip and display name.action — What happens on click:
webapp /path — Opens an HTML page (standalone tab or inline iframe).iframe-cmd <command> — Sends a postMessage command to the charm iframe. The iframe webapp listens for { charmCmd: "command" } messages. If the iframe isn’t loaded yet, the dashboard auto-loads it from the sibling webapp action.http://... — Fires an HTTP request. Same-origin requests go direct; cross-origin LAN requests are routed through /api/proxy automatically.display — How webapp charms open:
standalone — Always opens in a new tab.inline — Always opens in an iframe below the charm bar.both — Click opens inline; Shift+click opens standalone.msg — Status message shown after execution.context — Optional. library-only or stream-only to restrict when the charm appears.expand — Optional array of sub-actions. Each sub-action has icon, label, action, display, and msg. Renders as a grouped button row with a connection status indicator.Charm bar order and visibility are stored in charmBar in mbxhub.json:
{
"charmBar": {
"order": ["phantom"],
"hidden": []
}
}
order — Charm IDs in display order. New charms not in the list are appended.hidden — Charm IDs to hide from the dashboard.The charm bar also appears as “Charm Bar” in the Dashboard Layout panel ordering, so it can be repositioned or collapsed like any other section.
Direct access to all 137 MusicBee API methods. Use this for operations not exposed via REST endpoints or for scripting/automation.
Invoke any MusicBee API method by name
POST /rpc/Library_GetFileTag
Content-Type: application/json
{
"fileUrl": "C:\\Music\\song.mp3",
"field": "TrackTitle"
}
// Response:
{
"success": true,
"data": {
"method": "Library_GetFileTag",
"result": "Song Title"
}
}
| Method | Parameters | Returns |
|---|---|---|
| Player_PlayPause | - | boolean |
| Player_Stop | - | boolean |
| Player_StopAfterCurrent | - | boolean |
| Player_PlayNextTrack | - | boolean |
| Player_PlayPreviousTrack | - | boolean |
| Player_PlayNextAlbum | - | boolean |
| Player_PlayPreviousAlbum | - | boolean |
| Player_StartAutoDj | - | boolean |
| Player_EndAutoDj | - | boolean |
| Player_GetPosition | - | int (ms) |
| Player_SetPosition | position: int | boolean |
| Player_GetVolume | - | float (0-1) |
| Player_SetVolume | volume: float | boolean |
| Player_GetMute | - | boolean |
| Player_SetMute | mute: boolean | boolean |
| Player_GetShuffle | - | boolean |
| Player_SetShuffle | shuffle: boolean | boolean |
| Player_GetRepeat | - | RepeatMode |
| Player_SetRepeat | mode: RepeatMode | boolean |
| Player_GetPlayState | - | PlayState |
| Player_GetEqualiserEnabled | - | boolean |
| Player_SetEqualiserEnabled | enabled: boolean | boolean |
| Player_GetDspEnabled | - | boolean |
| Player_SetDspEnabled | enabled: boolean | boolean |
| Player_GetCrossfade | - | boolean |
| Player_SetCrossfade | enabled: boolean | boolean |
| Player_GetReplayGainMode | - | ReplayGainMode |
| Player_SetReplayGainMode | mode: ReplayGainMode | boolean |
| Player_GetScrobbleEnabled | - | boolean |
| Player_SetScrobbleEnabled | enabled: boolean | boolean |
| Player_QueueRandomTracks | count: int | int |
| Player_GetOutputDevices | - | {devices, activeDevice} |
| Player_SetOutputDevice | deviceName: string | boolean |
| Method | Parameters | Returns |
|---|---|---|
| NowPlaying_GetFileUrl | - | string |
| NowPlaying_GetDuration | - | int (ms) |
| NowPlaying_GetFileTag | field: MetaDataType | string |
| NowPlaying_GetFileTags | fields: MetaDataType[] | string[] |
| NowPlaying_GetFileProperty | type: FilePropertyType | string |
| NowPlaying_GetArtwork | - | string (base64/path) |
| NowPlaying_GetArtworkUrl | - | string |
| NowPlaying_GetLyrics | - | string |
| NowPlaying_GetDownloadedLyrics | - | string |
| NowPlaying_GetArtistPicture | fadingPercent: int | string |
| NowPlaying_GetArtistPictureThumb | - | string |
| NowPlaying_GetArtistPictureUrls | localOnly: boolean | string[] |
| NowPlaying_IsSoundtrack | - | boolean |
| NowPlaying_GetSpectrumData | - | float[] |
| NowPlaying_GetSoundGraph | - | float[] |
| Method | Parameters | Returns |
|---|---|---|
| NowPlayingList_GetCurrentIndex | - | int |
| NowPlayingList_GetNextIndex | offset: int | int |
| NowPlayingList_IsAnyPriorTracks | - | boolean |
| NowPlayingList_IsAnyFollowingTracks | - | boolean |
| NowPlayingList_GetListFileUrl | index: int | string |
| NowPlayingList_GetFileTag | index: int, field: MetaDataType | string |
| NowPlayingList_GetFileTags | index: int, fields: MetaDataType[] | string[] |
| NowPlayingList_GetFileProperty | index: int, type: FilePropertyType | string |
| NowPlayingList_Clear | - | boolean |
| NowPlayingList_PlayNow | fileUrl: string | boolean |
| NowPlayingList_QueueNext | fileUrl: string | boolean |
| NowPlayingList_QueueLast | fileUrl: string | boolean |
| NowPlayingList_QueueFilesNext | fileUrls: string[] | boolean |
| NowPlayingList_QueueFilesLast | fileUrls: string[] | boolean |
| NowPlayingList_RemoveAt | index: int | boolean |
| NowPlayingList_MoveFiles | fromIndices: int[], toIndex: int | boolean |
| NowPlayingList_PlayLibraryShuffled | - | boolean |
| NowPlayingList_QueryFilesEx | query: string | string[] |
| Method | Parameters | Returns |
|---|---|---|
| Library_GetFileTag | fileUrl: string, field: MetaDataType | string |
| Library_GetFileTags | fileUrl: string, fields: MetaDataType[] | string[] |
| Library_GetFileProperty | fileUrl: string, type: FilePropertyType | string |
| Library_SetFileTag | fileUrl: string, field: MetaDataType, value: string | boolean |
| Library_CommitTagsToFile | fileUrl: string | boolean |
| Library_GetLyrics | fileUrl: string, type: LyricsType | string |
| Library_GetArtwork | fileUrl: string, index: int | string |
| Library_GetArtworkUrl | fileUrl: string, index: int | string |
| Library_GetArtistPicture | artistName: string, fadingPercent: int | string |
| Library_GetArtistPictureThumb | artistName: string | string |
| Library_GetArtistPictureUrls | artistName: string, localOnly: boolean | string[] |
| Library_QueryFilesEx | query: string | string[] |
| Library_QuerySimilarArtists | artistName: string, minimumSimilarity: double | string |
| Library_AddFileToLibrary | fileUrl: string, category: LibraryCategory | string |
| Method | Parameters | Returns |
|---|---|---|
| Playlist_QueryPlaylists | - | boolean |
| Playlist_QueryGetNextPlaylist | - | string |
| Playlist_GetName | playlistUrl: string | string |
| Playlist_GetType | playlistUrl: string | PlaylistFormat |
| Playlist_IsInList | playlistUrl: string, filename: string | boolean |
| Playlist_QueryFilesEx | playlistUrl: string | string[] |
| Playlist_CreatePlaylist | folderName: string, playlistName: string, filenames: string[] | string |
| Playlist_DeletePlaylist | playlistUrl: string | boolean |
| Playlist_SetFiles | playlistUrl: string, filenames: string[] | boolean |
| Playlist_AppendFiles | playlistUrl: string, filenames: string[] | boolean |
| Playlist_RemoveAt | playlistUrl: string, index: int | boolean |
| Playlist_MoveFiles | playlistUrl: string, fromIndices: int[], toIndex: int | boolean |
| Playlist_PlayNow | playlistUrl: string | boolean |
| Method | Parameters | Returns |
|---|---|---|
| Podcasts_QuerySubscriptions | query: string | string[] |
| Podcasts_GetSubscription | id: string | string[] |
| Podcasts_GetSubscriptionArtwork | id: string, index: int | string (base64) |
| Podcasts_GetSubscriptionEpisodes | id: string | string[] |
| Podcasts_GetSubscriptionEpisode | id: string, index: int | string[] |
| Method | Parameters | Returns |
|---|---|---|
| Setting_GetPersistentStoragePath | - | string |
| Setting_GetSkin | - | string |
| Setting_GetSkinElementColour | element: SkinElement, state: ElementState, component: ElementComponent | int |
| Setting_IsWindowBordersSkinned | - | boolean |
| Setting_GetFieldName | field: MetaDataType | string |
| Setting_GetDataType | field: MetaDataType | string |
| Setting_GetLastFmUserId | - | string |
| Setting_GetWebProxy | - | string |
| Setting_GetValue | settingId: SettingId | object |
| Method | Parameters | Returns |
|---|---|---|
| MB_GetWindowHandle | - | long |
| MB_RefreshPanels | - | true |
| MB_GetLocalisation | id: string, defaultText: string | string |
| MB_ShowNowPlayingAssistant | - | boolean |
| MB_InvokeCommand | command: Command, parameter: object | boolean |
| MB_SetWindowSize | width: int, height: int | boolean |
| MB_GetVisualiserInformation | - | {visualiserNames, defaultState, currentState} |
| MB_ShowVisualiser | visualiserName: string, state: WindowState | boolean |
"rpcEnabled": false
Real-time event streaming. Connect once, receive updates automatically - no polling.
ws://localhost:8080/| Step | Description |
|---|---|
| 1. Connect | Open WebSocket to ws://localhost:8080/ |
| 2. Receive | Immediately starts receiving ALL events (default behavior) |
| 3. Subscribe (optional) | Send subscribe message to filter to specific events only |
| 4. Disconnect | Close the WebSocket connection when done |
Note: New clients receive ALL events by default. Once you send a subscribe message, you only receive those specific events. Use unsubscribe to stop receiving events without disconnecting.
| Event | Description | Frequency |
|---|---|---|
| TrackChanged | New track started playing | On track change |
| PlayStateChanged | Play/pause/stop state changed | On state change |
| VolumeChanged | Volume level or mute state changed | On volume change |
| PositionChanged | Playback position update (milliseconds) | ~1 per second while playing |
| QueueChanged | Now playing list modified (add/remove/clear) | On queue change |
| ShuffleChanged | Shuffle mode toggled on/off | On shuffle change |
| RepeatChanged | Repeat mode changed (none/all/one) | On repeat change |
| MetadataChanged | Rating or love tag changed on current track | On tag/rating change |
| Reaction | User reacted to now playing track (emoji, nickname, track info) | On reaction submit |
// Subscribe to specific events (filters to only these events)
{"subscribe": ["TrackChanged", "PlayStateChanged"]}
// Unsubscribe from events (stop receiving them)
{"unsubscribe": ["PositionChanged"]}
// Subscribe to all events (equivalent to no subscriptions)
{"subscribe": ["TrackChanged", "PlayStateChanged", "VolumeChanged",
"PositionChanged", "QueueChanged", "ShuffleChanged",
"RepeatChanged", "MetadataChanged", "Reaction",
"TasteChanged"]}
// TrackChanged
{
"event": "TrackChanged",
"timestamp": "2024-01-03T12:00:00.000Z",
"data": {
"fileUrl": "C:\\Music\\song.mp3",
"title": "Track Title",
"artist": "Artist Name",
"album": "Album Name",
"duration": 245000,
"artworkUrl": "/nowplaying/artwork"
}
}
// PlayStateChanged
{
"event": "PlayStateChanged",
"timestamp": "2024-01-03T12:00:00.000Z",
"data": {
"state": "playing" // "playing", "paused", "stopped"
}
}
// VolumeChanged
{
"event": "VolumeChanged",
"timestamp": "2024-01-03T12:00:00.000Z",
"data": {
"volume": 75, // 0 to 100
"muted": false
}
}
// PositionChanged
{
"event": "PositionChanged",
"timestamp": "2024-01-03T12:00:00.000Z",
"data": {
"position": 45000, // Current position in milliseconds
"duration": 245000 // Total duration in milliseconds
}
}
// QueueChanged
{
"event": "QueueChanged",
"timestamp": "2024-01-03T12:00:00.000Z",
"data": {
"action": "add", // "add", "remove", "clear", "move"
"index": 5,
"totalTracks": 42
}
}
// ShuffleChanged
{
"event": "ShuffleChanged",
"timestamp": "2024-01-03T12:00:00.000Z",
"data": {
"enabled": true
}
}
// RepeatChanged
{
"event": "RepeatChanged",
"timestamp": "2024-01-03T12:00:00.000Z",
"data": {
"mode": "all" // "none", "all", "one"
}
}
// MetadataChanged
{
"event": "MetadataChanged",
"timestamp": "2024-01-03T12:00:00.000Z",
"data": {
"fileUrl": "C:\\Music\\song.mp3",
"rating": 3, // -1 (unrated) to 5
"love": "L" // "L" (loved), "B" (banned), or "" (neither)
}
}
// Reaction
{
"event": "Reaction",
"timestamp": "2024-01-03T12:00:00.000Z",
"data": {
"emoji": "fire",
"type": "fire", // fire, heart, like, dislike, ban
"nickname": "Guest",
"trackTitle": "Song Name",
"trackArtist": "Artist"
}
}
// TasteChanged (debounced, fires after reactions/influences/mood changes)
{
"event": "TasteChanged",
"timestamp": "2024-01-03T12:00:05.000Z",
"data": {
"profile": {
"topGenres": [{ "name": "Rock", "weight": 1.0 }],
"topArtists": [{ "name": "Foo Fighters", "weight": 0.85 }],
"bpmRange": [90, 160],
"mood": "Energetic",
"moodConfidence": 0.88,
"influenceCount": 3,
"reactionCount": 12
}
}
}
// Connect to WebSocket
const ws = new WebSocket('ws://localhost:8080/');
ws.onopen = function() {
console.log('Connected to MBXHub');
// Optional: Subscribe to specific events only
// Without this, you receive ALL events
ws.send(JSON.stringify({
subscribe: ['TrackChanged', 'PlayStateChanged', 'PositionChanged']
}));
};
ws.onmessage = function(event) {
const msg = JSON.parse(event.data);
switch (msg.event) {
case 'TrackChanged':
console.log('Now playing:', msg.data.title, '-', msg.data.artist);
break;
case 'PlayStateChanged':
console.log('State:', msg.data.state);
break;
case 'PositionChanged':
const pct = (msg.data.position / msg.data.duration * 100).toFixed(1);
console.log('Position:', pct + '%');
break;
}
};
ws.onclose = function() {
console.log('Disconnected from MBXHub');
};
ws.onerror = function(err) {
console.error('WebSocket error:', err);
};
// Later: Unsubscribe from position updates (too frequent)
ws.send(JSON.stringify({ unsubscribe: ['PositionChanged'] }));
// Clean disconnect
ws.close();
{
"success": false,
"error": {
"code": "ERROR_CODE",
"message": "Human-readable description"
}
}
| Code | HTTP | Description |
|---|---|---|
| NOT_FOUND | 404 | Endpoint or resource not found |
| INVALID_REQUEST | 400 | Invalid parameters or malformed request |
| ARIA_DISABLED | 403 | ARiA input simulation is disabled |
| FORBIDDEN | 403 | Operation not allowed (e.g., RPC disabled) |
| METHOD_NOT_ALLOWED | 405 | Wrong HTTP method |
| SERVICE_UNAVAILABLE | 503 | Required service not available (e.g., TrueShuffle/AutoQ) |
| INTERNAL_ERROR | 500 | Server error |
Common enum values used in API parameters and responses.
| Value | Code | Description |
|---|---|---|
| undefined | 0 | Unknown state |
| loading | 1 | Track is loading |
| playing | 3 | Playing |
| paused | 6 | Paused |
| stopped | 7 | Stopped |
| Value | Code | Description |
|---|---|---|
| none | 0 | No repeat |
| all | 1 | Repeat all tracks |
| one | 2 | Repeat current track |
| Value | Code | Description |
|---|---|---|
| off | 0 | Disabled |
| track | 1 | Track-based gain |
| album | 2 | Album-based gain |
| smart | 3 | Automatic selection |
| Field | Code | Description |
|---|---|---|
| TrackTitle | 65 | Track title |
| Album | 30 | Album name |
| AlbumArtist | 31 | Album artist |
| Artist | 32 | Track artist |
| Composer | 43 | Composer |
| Genre | 59 | Genre |
| Rating | 75 | Star rating (0-5) |
| RatingLove | 76 | Love rating |
| TrackNo | 86 | Track number |
| DiscNo | 52 | Disc number |
| Year | 88 | Year |
| Lyrics | 114 | Lyrics text |
| Comment | 44 | Comment |
| Publisher | 73 | Publisher/Label |
| Conductor | 45 | Conductor |
| Property | Code | Description |
|---|---|---|
| Url | 2 | File path/URL |
| Kind | 4 | File type (Music, Video, etc.) |
| Format | 5 | Audio format (MP3, FLAC, etc.) |
| Size | 7 | File size in bytes |
| Channels | 8 | Audio channels |
| SampleRate | 9 | Sample rate (Hz) |
| Bitrate | 10 | Bitrate (kbps) |
| Duration | 16 | Duration (ms) |
| PlayCount | 14 | Play count |
| SkipCount | 15 | Skip count |
| LastPlayed | 13 | Last played date |
| DateAdded | 12 | Date added to library |
| DateModified | 11 | File modification date |
| Format | Code | Description |
|---|---|---|
| Unknown | 0 | Unknown format |
| M3u | 1 | M3U playlist |
| Xspf | 2 | XSPF (XML Shareable Playlist) |
| Asx | 3 | ASX (Windows Media) |
| Wpl | 4 | WPL (Windows Media) |
| Pls | 5 | PLS playlist |
| Auto | 7 | Auto-detect format |
| Type | Code | Description |
|---|---|---|
| NotSpecified | 0 | Any lyrics type |
| Synchronised | 1 | Time-synced lyrics (LRC) |
| UnSynchronised | 2 | Plain text lyrics |
| Category | Code | Description |
|---|---|---|
| Music | 0 | Music files |
| Audiobook | 1 | Audiobooks |
| Video | 2 | Video files |
| Inbox | 4 | Inbox (new files) |
MBXHub includes comprehensive logging for debugging and monitoring. Logs are written using NLog to a log file in the MBXHub folder.
Logging is controlled by two settings in mbxhub.json:
| Setting | Type | Description |
|---|---|---|
debugMode | boolean | Master switch - enables/disables all logging |
logLevel | string | Minimum log level when debug mode is on |
| Level | What Gets Logged |
|---|---|
| Trace | Everything including request/response bodies, WebSocket message content. Very verbose. |
| Debug | Route matching, handler selection, subscription changes, internal decisions. |
| Info | Startup/shutdown, HTTP requests (method/path/status/timing), connections, track changes. |
| Warning | Recoverable errors, timeouts, retries, unexpected but handled situations. |
| Error | Failures, exceptions, service unavailable. Always logged even with debug mode off. |
Log files are stored in the MBXHub subfolder of MusicBee's persistent storage:
%AppData%\MusicBee\MBXHub\mbxhub.log
mbxhub.1.log, mbxhub.2.log, etc.To enable debug logging:
// In mbxhub.json:
{
"debugMode": true,
"logLevel": "Trace" // or "Debug", "Info", "Warning", "Error"
}
HTTP requests are logged at Info level with timing:
2025-01-25 14:32:15 [INFO ] [REST] HTTP GET /player/status -> 200 (12ms)
2025-01-25 14:32:16 [INFO ] [REST] HTTP POST /player/playpause -> 200 (8ms)
2025-01-25 14:32:17 [INFO ] [Plugin] Track changed: Artist Name - Track Title
2025-01-25 14:32:17 [INFO ] [WebSocket] WS client abc12345 connected from 192.168.1.50:54321
MBXHub operates on a trusted local network model with multiple security layers.
PartyMode uses PIN-based authentication with three roles:
| Role | Access | Authentication |
|---|---|---|
DJ | Full control: player, queue, start/stop party | DJ PIN via X-Party-PIN header |
Guest | Browse library, request songs, vote | Guest PIN via X-Party-PIN header |
Anonymous | Read-only: now playing, artwork, status | No PIN required |
PartyMode endpoints authenticate via the X-Party-PIN header:
# Guest request example
curl -X POST http://localhost:8080/partymode/request \
-H "X-Party-PIN: 1234" \
-H "Content-Type: application/json" \
-d '{"url":"C:\\Music\\Track.mp3","nickname":"Haro"}'
# Validate PIN and get role
GET /partymode/validate?pin=1234&nickname=Haro
Invalid or missing PIN returns 401 Unauthorized.
MBXHub supports three protection levels for different deployment scenarios:
| Level | Description | Use Case |
|---|---|---|
| Default | Full API access, no restrictions | Personal use, trusted networks |
| Kiosk | All requests redirect to defaultPage | Party displays, public screens |
| Restricted | Read-only mode with granular controls | Shared access, limited control |
Configure via kioskMode and apiReadOnlyMode in settings.
PartyMode includes built-in limits and optional per-IP rate limiting:
Built-in limits (always active):
Per-IP rate limiting (configurable via Settings → Party Mode...):
Returns 429 Too Many Requests when limits exceeded.
| Origin | Access |
|---|---|
localhost, 127.0.0.1 | Always allowed |
192.168.x.x | Allowed when allowRemoteConnections is enabled |
10.x.x.x | Allowed when allowRemoteConnections is enabled |
172.16.x.x - 172.31.x.x | Allowed when allowRemoteConnections is enabled |
| External origins | Blocked (prevents cross-site attacks) |
MBXHub can restrict write operations via settings with granular per-operation controls:
Restriction Hierarchy:
Settings cascade: master OR category OR operation = blocked
Granular Operations:
| Category | Operation | Endpoints Affected |
|---|---|---|
| Library | Tag edits | PUT /library/file/*, POST /library/commit |
| File deletion | DELETE /library/file/* | |
| Queue | Add tracks | POST /queue/add, /queue/playnow, /queue/play |
| Remove tracks | DELETE /queue/* | |
| Reorder tracks | POST /queue/move | |
| Player | Playback | POST /player/play, /pause, /stop, /next, /previous |
| Volume | POST /player/volume, /mute | |
| Seek | POST /player/position | |
| Playlists | Create | POST /playlists |
| Delete | DELETE /playlists/* | |
| Modify | PUT /playlists/*, POST /playlists/*/files |
Default Settings:
The philosophy: player and queue are open, destructive operations are locked. Playlists, tag edits, and file deletion default to read-only.
| Setting | Default | Effect |
|---|---|---|
apiReadOnlyMode | false | Master switch — off |
apiReadOnlyPlayer | false | Player controls allowed |
apiReadOnlyQueue | false | Queue modifications allowed |
apiReadOnlyLibrary | false | Library allowed (but see granular) |
apiReadOnlyPlaylists | true | Playlists blocked by default |
apiReadOnlyLibraryTags | true | Tag edits blocked by default |
apiReadOnlyLibraryDelete | true | File deletion blocked by default |
apiReadOnlyPlayerPlayback | false | Playback allowed |
apiReadOnlyPlayerVolume | false | Volume allowed |
apiReadOnlyPlayerSeek | false | Seek allowed |
apiReadOnlyQueueAdd | false | Queue add allowed |
apiReadOnlyQueueRemove | false | Queue remove allowed |
apiReadOnlyQueueReorder | false | Queue reorder allowed |
apiReadOnlyPlaylistsCreate | false | (moot — parent is true) |
apiReadOnlyPlaylistsDelete | false | (moot — parent is true) |
apiReadOnlyPlaylistsModify | false | (moot — parent is true) |
Settings cascade: master OR category OR operation = blocked. The granular playlist settings default to false but are moot because their parent apiReadOnlyPlaylists is true.
Always Permitted (exempt from restrictions):
Blocked requests return 403 Forbidden:
{"success":false,"error":{"code":"READ_ONLY","message":"API is in read-only mode"}}
allowRemoteConnections disabled unless neededrpcEnabled: false) if not needed, as it exposes all API methodsariaEnabled: false) unless specifically needed for PC wake scenarios
All file URLs are Windows paths. URL-encode when passing in path parameters:
// Original: C:\Music\Artist\Track.mp3
// Encoded: C%3A%5CMusic%5CArtist%5CTrack.mp3
// Example: GET /library/file/C%3A%5CMusic%5CArtist%5CTrack.mp3
Most list endpoints support offset and limit query parameters:
GET /library/files?offset=100&limit=50Library endpoints support ?sort= parameter for server-side sorting:
alpha (default) - Alphabetical by title+artistartist - By artist name, then titlealbum - By album name, then track numbertitle - By title onlydate - By date added (newest first)track - By disc number, then track number (natural album order)name - By display nameyear-asc - By year ascending (chronological, oldest first)Example: GET /library/files?artist=Pink+Floyd&sort=album
Shuffle, banlist, and influence endpoints require TrueShuffle or AutoQ to be enabled. Check availability:
GET /shuffle/status
// Returns 503 SERVICE_UNAVAILABLE if TrueShuffle/AutoQ not enabled
For real-time updates (track changes, state changes), use WebSocket instead of polling:
By default, MBXHub only accepts localhost connections. For network access:
allowRemoteConnections in settingshttp://192.168.1.100:8080)MBXHub automatically detects CUE-backed audio files and resolves per-track metadata across all surfaces:
/player/status) - Overlays title, artist, trackNo, album from CUE sheet/nowplaying/position) - Includes cueTrack and cueTitle/nowplaying/tag) - Returns CUE track data for TrackTitle, Artist, Album, TrackNotrack events include cueTrack field when CUE activeEncoding detection: BOM check → UTF-8 validation → Windows-1252 fallback. Optional CueSharp.dll in Plugins folder enables enhanced parsing via reflection.
| Use REST when... | Use RPC when... |
|---|---|
| Building a client app | Writing automation scripts |
| Need clean, discoverable URLs | Need direct MusicBee API access |
| Want resource-oriented design | Familiar with MusicBee plugin API |
| Working with standard HTTP clients | Need parameter flexibility |