Native macOS app that embeds YouTube Music and publishes what you're listening to as Discord Rich Presence — track title, artist, artwork, and playback progress bar.
No Electron. No backend. No third-party dependencies. Just Swift and a local Discord IPC socket.
- Embedded YouTube Music powered by
WKWebView— it's just the website, running natively - Discord Rich Presence over the local Discord IPC socket (no internet, no server)
- Track title, artist, album artwork, playback progress, and a "Listen on YouTube Music" button
- Menu bar extra with playback controls (prev / play-pause / next / stop / reconnect)
- Launches at login, stays out of the way
- Zero analytics, zero backend, zero telemetry
| macOS | 14 Sonoma or newer |
| Discord | Desktop app running locally |
| Build | Swift 6 toolchain (for building from source) |
Grab the latest .zip from Releases, unzip, and move the app to /Applications.
Security note: The app is ad-hoc signed (no Apple Developer account required). On first launch, right-click → Open to bypass Gatekeeper.
git clone https://github.com/Swift22/youtube-music-rpc.git
cd youtube-music-rpc
./script/build_and_run.shThat's it — no Xcode project, no CocoaPods, no SPM fetch. The build script assembles the .app bundle from scratch.
- Go to the Discord Developer Portal and create a new application
- Copy the Application ID (Client ID)
- Launch YouTube Music RPC
- Open Settings and paste the Client ID
- Start playing something
./script/check.sh # runs swift test + swift build (same as CI)
swift test # tests only
swift test --filter DiscordIPC # single suiteThe project has two SwiftPM targets:
YTMusicRPCCore— pure library: Discord IPC, bridge model, parsers. No SwiftUI.YouTubeMusicRPCApp— the app: UI, WebView, lifecycle. Depends on Core.
Tests use Swift Testing (import Testing, @Test, #expect()), not XCTest.
flowchart LR
A["SwiftUI app shell"] --> B["WKWebView\nYouTube Music"]
B --> C["Injected JS bridge"]
C --> D["TrackStateParser"]
D --> E["PresenceController"]
E --> F["Discord IPC socket"]
G["AppSettings"] --> E
Full write-up: docs/architecture.md
Track detection reads navigator.mediaSession.metadata plus DOM fallbacks in YouTube Music's player bar. If YouTube changes their markup, detection can break. The maintenance checklist is in docs/webkit-bridge.md.
- Stores only your Discord Client ID in
UserDefaults - YouTube session data stays in the system
WKWebViewdata store (same as Safari) - Track metadata goes only to the local Discord IPC socket — never leaves your machine
- No analytics, no crash reporting, no remote anything
./script/package_release.shFor Developer ID signing and notarization: docs/release.md
PRs welcome. See CONTRIBUTING.md.