Skip to content

tsvb/applenugs

Repository files navigation

AppleNugs

CI

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.

Features

  • Sign in your way. Browser-based SSO (Apple / Google / Facebook / SiriusXM) via ASWebAuthenticationSession with 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 AVQueuePlayer while 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.

Why native

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       │
└──────────────────────────────┘

Build & run

Requires Xcode 16+ and XcodeGen (brew install xcodegen). The .xcodeproj is generated, not committed:

xcodegen generate
open AppleNugs.xcodeproj   # then ⌘R

or headless:

xcodebuild -project AppleNugs.xcodeproj -scheme AppleNugs -configuration Debug build

The 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.

iOS (personal build)

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, ⌘R

A 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 generate

or headless against the simulator:

xcodebuild -project AppleNugs.xcodeproj -scheme AppleNugs-iOS \
  -configuration Debug -destination 'generic/platform=iOS Simulator' \
  CODE_SIGNING_ALLOWED=NO build

Audio 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.

Tests

Pure-logic unit tests live in a host-free bundle (no app launch, no @testable):

xcodebuild test -project AppleNugs.xcodeproj -scheme AppleNugsTests

Keyboard shortcuts (macOS)

key action
/ Focus search
space Play / pause
n / p Next / previous track
/ Seek −10s / +10s (⇧ for ∓30s)
0 Seek to start
19 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.

Notes for hacking

  • Target layout: everything under AppleNugs/ is shared by both apps except AppleNugs/macOS/ (Sparkle glue, keyboard monitor, split-view shell, desktop faceplate) and AppleNugs/iOS/ (app entry, tab shell, now-playing screens, orientation gate). project.yml excludes each platform directory from the other target; small in-file divergences use #if os(...).
  • UI-test harness: launch with -UITEST for a stubbed logged-in state (no network/Keychain). Extra launch args: -UITestSeedQueue parks a fake queue so transport UI renders, -UITestTab artists|search|favorites|videos selects the starting tab, -UITestTheme <ThemeID> forces a theme, and -UITestShowNowPlaying opens the full-screen player (iOS).
  • Offline downloads live in Application Support/AppleNugs/Downloads/<containerID>/ with a manifest.json index, 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 (artistID vs ArtistID) and pluralization. All shape-dependent digging lives in Core/JSON.swift and Core/Catalog.swift.
  • platformID for bigriver/subPlayer.aspx is 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/callback redirect pair; ASWebAuthenticationSession captures the callback in-process, so the scheme is deliberately not registered in Info.plist.

License

MIT for the AppleNugs source. The license covers this code only and grants no rights in nugs.net's service, content, or marks.

About

No description, website, or topics provided.

Resources

License

Stars

1 star

Watchers

0 watching

Forks

Packages

 
 
 

Contributors