A native macOS and iOS client for nugs.net, written in Swift/SwiftUI. Fast search, a real queue, gapless playback, video, offline listening on iPhone, a Winamp-style artist library, and keyboard control over your own nugs.net subscription.
Important
Unofficial and unaffiliated. AppleNugs is an independent client. It is not affiliated with, authorized, sponsored, or endorsed by nugs.net. It is for personal use against your own nugs.net subscription only: it plays the same content the official apps do, keeps any offline copies private to the app's own container on your own device (the official apps offer offline listening too), redistributes nothing, and circumvents no DRM. You are responsible for complying with the nugs.net Terms of Service.
- Sign in your way. Browser-based SSO (Apple / Google / Facebook / SiriusXM)
via
ASWebAuthenticationSessionwith OAuth2 Authorization Code + PKCE, or classic email + password. Tokens persist locally and refresh automatically. - Gapless audio. The next track is resolved and parked in an
AVQueuePlayerwhile the current one plays, so live-show segues flow. ALAC is preferred (lossless in MP4); FLAC, MQA, AAC, and HLS all play, with automatic fallthrough if a format fails. - Video. Continue Watching with resume positions, Live & Upcoming, Recent Exclusives (just-ended livestream replays, last-chance style), a paged on-demand grid, chapters, and a quality cap — audio and video share one playback arbiter so they never talk over each other.
- Artist library. Each artist opens as a Winamp-style expandable outline — Albums, Videos, and Shows as collapsible nodes (Videos and Shows grouped by year) rendered as dense, scannable rows under a VU + LCD-style header, instead of a wall of posters. Rows page in on scroll and lazily build per year, so a catalog of hundreds stays fast.
- iPhone app. A full iOS counterpart sharing the same core: five-tab shell, background audio with lock-screen transport, themed full-screen now-playing (tap the mini player), portrait-locked UI with rotatable fullscreen video and automatic Picture-in-Picture.
- Offline listening (iOS). Download whole shows in the best lossless format offered; they land in a Library ▸ Downloads segment, play with no network (gapless included), and the player prefers local files automatically. An airplane-mode launch offers "Listen Offline" directly.
- Favorites. Follow artists and save shows and videos; they surface on a Home landing strip and a dedicated Favorites view (Library tab on iOS).
- Themes. Five runtime-swappable looks (Tape Room, Soundboard, Shoebox, The Receiver, Click Wheel) with album-art-driven accent washes, per-theme transport signatures, and touch-first now-playing variants on iPhone — including The Receiver's VU faceplate and Click Wheel's circular pad.
- System integration. Media keys, Control Center, AirPods, and lock-screen
transport via
MPRemoteCommandCenter; cover art and now-playing in the system widget; AirPlay from the now-playing screens. - Live quality dashboard. Real format, platform tier, sample rate, bit depth, channels, and buffer-ahead — read from the decoder, not guessed.
- Auto-update. Built-in Sparkle updater — the app checks for new releases and installs them in place with one click; "Check for Updates…" lives under the app menu.
Two nugs.net platform constraints rule out a browser-based client: CORS, and
the audio CDN's required Referer/User-Agent headers that browsers won't let
JavaScript set. A native app has neither problem — AVURLAsset carries the
headers directly, so no proxy tier is needed:
┌──────────────────────────────┐ TLS ┌──────────┐
│ AppleNugs (macOS / iOS) │ ────────► │ nugs.net │
│ SwiftUI · AVFoundation │ └──────────┘
│ tokens in the Keychain │
└──────────────────────────────┘
Requires Xcode 16+ and XcodeGen
(brew install xcodegen). The .xcodeproj is generated, not committed:
xcodegen generate
open AppleNugs.xcodeproj # then ⌘Ror headless:
xcodebuild -project AppleNugs.xcodeproj -scheme AppleNugs -configuration Debug buildThe committed project is ad-hoc signed so it builds in CI and on any machine with no Apple Developer team. Producing a signed, notarized build for the App Store or for direct download is covered in DISTRIBUTION.md.
Sign in with browser SSO or your nugs.net email and password. Access and refresh
tokens are stored in the macOS Keychain (the source of truth), with a
chmod 600 file fallback inside the app's sandbox container
(~/Library/Containers/com.timvbs.applenugs/…) for unsigned/ad-hoc builds that
have no Keychain entitlement; a legacy session.json is migrated into the
Keychain on first launch and then removed. Tokens refresh ~60s before expiry.
The repo also carries an AppleNugs-iOS target (iPhone, iOS 17+) sharing the
same core. It is personal-install only — build it yourself and run it on
your own device; there is no App Store or TestFlight distribution:
xcodegen generate
open AppleNugs.xcodeproj # select the AppleNugs-iOS scheme,
# set your team under Signing & Capabilities, ⌘RA team selected in Xcode is wiped by the next xcodegen generate. To make it
stick, export your team ID before generating (e.g. in ~/.zshrc):
export APPLENUGS_TEAM_ID=YOUR_TEAM_ID # then: xcodegen generateor headless against the simulator:
xcodebuild -project AppleNugs.xcodeproj -scheme AppleNugs-iOS \
-configuration Debug -destination 'generic/platform=iOS Simulator' \
CODE_SIGNING_ALLOWED=NO buildAudio keeps playing in the background (lock screen / Control Center transport
included), and downloaded shows play fully offline. Sparkle auto-update is
macOS-only; update the iOS install by rebuilding from source. For cable-free
updates after the first install, xcodebuild against the device destination
plus xcrun devicectl device install app does the whole cycle headlessly.
Pure-logic unit tests live in a host-free bundle (no app launch, no
@testable):
xcodebuild test -project AppleNugs.xcodeproj -scheme AppleNugsTests| key | action |
|---|---|
/ |
Focus search |
space |
Play / pause |
n / p |
Next / previous track |
← / → |
Seek −10s / +10s (⇧ for ∓30s) |
0 |
Seek to start |
1–9 |
Seek to 10%–90% of the track |
Esc |
Blur a focused input |
⌃⌘→ / ⌃⌘← |
Next / previous (menu) |
⌘⇧F |
Focus search (menu) |
⌥⌘I |
Toggle the dashboard panel |
Plain-letter keys are handled by a window-level event monitor and pass through untouched while a text field has focus. On iPhone, transport lives on screen and on the lock screen instead.
- Target layout: everything under
AppleNugs/is shared by both apps exceptAppleNugs/macOS/(Sparkle glue, keyboard monitor, split-view shell, desktop faceplate) andAppleNugs/iOS/(app entry, tab shell, now-playing screens, orientation gate).project.ymlexcludes each platform directory from the other target; small in-file divergences use#if os(...). - UI-test harness: launch with
-UITESTfor a stubbed logged-in state (no network/Keychain). Extra launch args:-UITestSeedQueueparks a fake queue so transport UI renders,-UITestTab artists|search|favorites|videosselects the starting tab,-UITestTheme <ThemeID>forces a theme, and-UITestShowNowPlayingopens the full-screen player (iOS). - Offline downloads live in
Application Support/AppleNugs/Downloads/<containerID>/with amanifest.jsonindex, excluded from backups; only direct-file picks (never HLS) are downloadable. - The unofficial API surface is documented by Sorrow446/Nugs-Downloader and Dniel97/orpheusdl-nugs; check those when an endpoint or shape stops working.
- nugs's catalog JSON uses inconsistent casing (
artistIDvsArtistID) and pluralization. All shape-dependent digging lives inCore/JSON.swiftandCore/Catalog.swift. platformIDforbigriver/subPlayer.aspxis a device tier, not a format: we probe{1, 4, 7, 10}concurrently and identify the actual format from URL path patterns (.flac16/,.alac16/,.m3u8, …). Preference order is ALAC → FLAC → MQA → AAC → HLS, with fallthrough on failure.- Browser login relies on id.nugs.net (Duende IdentityServer) trusting the
mobile client's
client_id+nugsnet://oauth2/callbackredirect pair;ASWebAuthenticationSessioncaptures the callback in-process, so the scheme is deliberately not registered inInfo.plist.
MIT for the AppleNugs source. The license covers this code only and grants no rights in nugs.net's service, content, or marks.