Survival LLM
A free AI assistant and survival library (1.5 million offline articles) that runs on your own computer, with no internet. Shows the source of every answer. Mac and Windows.
Your music is everywhere... now you don't have to be.
OtherKotlin · SwiftGitHub @ParachordOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
Parachord for Android and iOS: one queue across Spotify, Apple Music, SoundCloud, Bandcamp and local files
Taken from the repository README
Loading the full README.
Read the full README ↗A unified music player that brings Spotify, Apple Music, SoundCloud, Bandcamp, and your local files into one queue. Parachord resolves every track across your available sources and plays from the best match based on a configurable resolver priority.
This repo holds both mobile companions to the Parachord desktop app: a native Android app (Jetpack Compose) and a native iOS app (SwiftUI). They share one Kotlin Multiplatform core. Both apps use the same resolver pipeline, plugin system and playlist sync as desktop, and the Android app is very close to feature parity.
These are from the Android app; the iOS app follows the same layout with native SwiftUI styling.
One queue across all sources. Tap any track and Parachord figures out where to play it from.
| Source | Android | iOS |
|---|---|---|
| Spotify (Premium) | Spotify Connect via the Web API: controls Spotify on your phone or any Connect device | Same: Spotify Connect via the Web API |
| Apple Music (subscription) | MusicKit JS in a hidden WebView | Native MusicKit |
| SoundCloud | Native ExoPlayer streaming | AVPlayer |
| Local files | Device media library, with artwork extraction and online enrichment | Device music library |
| Bandcamp | Resolves tracks and opens them in the browser (as on desktop) | Same |
| Direct HTTP streams | ExoPlayer | AVPlayer |
Sources are scored priority first, confidence second. A Spotify result at 70% confidence beats SoundCloud at 95% when Spotify is ranked higher. Matches below 60% confidence are dropped, so a wrong song never plays. An exact ISRC or MusicBrainz recording match scores 100%, even when the names differ.
Default order: spotify > applemusic > bandcamp > soundcloud > localfiles > youtube. You can change it in settings.
.xspf URL, such as a radio station's "recently played" feed. Parachord polls it every 5 minutes and keeps the local copy and its mirrors in step.Apple Music's API can't delete or rename playlists or remove tracks from them, so those changes have to be made in the Apple Music app.
.axe plugin on desktop but is disabled on mobile, because it needs a local server.recording_mbid, artist_mbids and release_mbid.go.parachord.com, falling back to a parachord.com/go deeplink if the smart-link service is unreachable.Both apps run the same plugins as desktop. They're bundled as .axe files and updated at runtime from the parachord-plugins marketplace, checked at most once a day. Android hosts them in a headless WebView and iOS in JavaScriptCore, and both run desktop's resolver-loader.js unchanged.
Plugin types: content resolvers, AI providers, meta-services (Last.fm, MusicBrainz, Discogs, Wikipedia), scrobblers and concert services. YouTube and Ollama are filtered out on mobile (capabilities.mobile: false). Native resolvers (Spotify, Apple Music, plus SoundCloud and local files on Android) take priority over their .axe equivalents for speed.
External playback (Apple Music, Spotify Connect) survives screen-off, Doze mode and Android's foreground-service killer. A silent 1-second WAV loops on ExoPlayer, which keeps MediaSessionService happy while the DRM audio flows through MusicKit or Spotify. See CLAUDE.md, "External Playback Background Survival", for the rationale.
Parachord is a player for people who already subscribe to the services it plays from, plus their own files and servers. It brings those services together in one app. It is not a way to get music without paying for it, or to keep copies of it.
If you work for a music service and have a concern about how Parachord plays your catalog, please open an issue.
| Android | iOS | |
|---|---|---|
| OS | Android 8.0+ (API 26) | iOS 17.0+ (iPhone and iPad) |
| Spotify playback | Spotify Premium (the free tier can browse but can't be controlled through the Web API) | Same |
| Apple Music playback | Apple Music subscription | Same |
| AI features | Your own ChatGPT / Claude / Gemini key, set in Settings | Same |
Prerequisites: Android Studio Ladybug (2024.2) or newer, JDK 17, Android SDK 35.
Copy the example config and fill in your keys:
cp local.properties.example local.properties
| Key | Required | Get it from |
|---|---|---|
LASTFM_API_KEY | Yes | https://www.last.fm/api/account/create |
LASTFM_SHARED_SECRET | Yes | Same as above |
SPOTIFY_CLIENT_ID | Yes | https://developer.spotify.com/dashboard |
SOUNDCLOUD_CLIENT_ID | Optional | https://soundcloud.com/you/apps |
SOUNDCLOUD_CLIENT_SECRET | Optional | Same as above |
APPLE_MUSIC_AUTHKEY_P8 | Optional | Path to your MusicKit AuthKey. The build mints the developer token and rotates it before it expires. |
APPLE_MUSIC_DEVELOPER_TOKEN | Optional | Generated from the AuthKey above, or set by hand (scripts/apple-music-token.py) |
ACHORDION_BEARER_TOKEN | Optional | Pre-warms Achordion's track-links cache on share and scrobble. Blank turns the submit off. |
AI provider keys (ChatGPT / Claude / Gemini) are entered per user in Settings, not at build time, which keeps them out of the open-source build config. Concert-service keys are also entered in Settings; optionally, TICKETMASTER_API_KEY and SEATGEEK_CLIENT_ID in local.properties supply fallback keys for users who haven't added their own.
# Build + install debug APK on connected device
./gradlew installDebug
# Force-stop the old process so Android picks up the new code
adb shell am force-stop com.parachord.android.debug
# Run unit tests (shared module + app)
./gradlew :shared:testDebugUnitTest :app:testDebugUnitTest
Release builds require a keystore; the CI workflow handles signing and publishing. See docs/play-store-publishing.md.
Prerequisites: Xcode 26 with the iOS 26 SDK, JDK 17 (Gradle builds the shared framework).
Copy the example secrets and fill in your keys:
cp iosApp/Parachord/Secrets.xcconfig.example iosApp/Parachord/Secrets.xcconfig
Secrets.xcconfig holds LASTFM_API_KEY, LASTFM_SHARED_SECRET, APPLE_MUSIC_DEVELOPER_TOKEN and ACHORDION_BEARER_TOKEN. It's gitignored, and the project still builds without it; Last.fm features are then inert until you add it. Spotify is bring-your-own: users enter their Spotify client ID in Settings.
Open iosApp/Parachord.xcodeproj and run the Parachord scheme. A build phase runs :shared:embedAndSignAppleFrameworkForXcode on every build, so changes to shared/ are picked up automatically. To build headless:
xcodebuild build -project iosApp/Parachord.xcodeproj -scheme Parachord \
-destination 'platform=iOS Simulator,name=iPhone 17 Pro'
Run the shared tests on the iOS simulator with ./gradlew :shared:iosSimulatorArm64Test. Kotlin/Native catches bugs the JVM suite can't, such as unsupported regex classes. iOS-specific notes are in iosApp/AGENTS.md.
Android is tag-driven:
./scripts/release-version.sh 0.4.0-beta.3
git push && git push origin v0.4.0-beta.3
The v* push triggers the release workflow. It signs the APK and AAB, attaches both to a GitHub Release, and uploads the AAB to the Play Console internal track. Prerelease version names (a hyphen in the semver) always go to internal, whatever the track flag says.
iOS builds are archived with iosApp/scripts/testflight.sh (--upload sends them to App Store Connect), then added to the TestFlight groups. See iosApp/README.md.
Both apps mirror the desktop Parachord app's architecture, adapted to each platform's idioms. Detailed design notes live in CLAUDE.md (Android + shared) and iosApp/AGENTS.md (iOS).
| Layer | Shared (:shared, KMP) | Android (:app) | iOS (iosApp/) |
|---|---|---|---|
| Language | Kotlin Multiplatform | Kotlin | Swift |
| UI | — | Jetpack Compose + Material 3 | SwiftUI |
| Playback | Queue, routing contracts | Media3 / ExoPlayer, Spotify Web API (Connect), MusicKit JS (WebView) | AVPlayer, Spotify Web API (Connect), MusicKit |
| Database | SQLDelight | SQLDelight Android driver | SQLDelight native driver |
| Networking | Ktor (all API clients) | OkHttp engine | Darwin engine |
| DI | Koin sharedModule | Koin | IosContainer |
| Preferences | KvStore + SecureTokenStore | SharedPreferences, EncryptedSharedPreferences (AES-256-GCM, Android Keystore) | NSUserDefaults, Keychain |
| Plugins | PluginManager, JsRuntime | WebView (JsBridge) | JavaScriptCore |
| Images | — | Coil | SwiftUI image cache |
shared/src/
├── commonMain/kotlin/com/parachord/shared/
│ ├── ai/ # DJ chat + recommendations: providers, tool loop, prompts
│ ├── api/ # Ktor clients (Spotify, Apple Music, Last.fm, MusicBrainz, ListenBrainz, Achordion, …)
│ ├── db/ # SQLDelight schema, queries, DAOs
│ ├── metadata/ # Metadata cascade, MBID + image enrichment
│ ├── model/ # Track, Album, Artist, Playlist, …
│ ├── playback/ # Queue, scrobbling
│ ├── plugin/ # PluginManager, JsRuntime interface, marketplace sync
│ ├── repository/ # Library, history, friends, charts, concerts, recommendations, …
│ ├── resolver/ # Resolver coordinator + scoring
│ ├── settings/ # SettingsStore
│ └── sync/ # Sync engine + Spotify / Apple Music / ListenBrainz providers
├── androidMain/ # Android actuals
└── iosMain/ # iOS actuals, IosContainer, IosResolverRuntime
app/src/main/java/com/parachord/android/
├── auth/ # OAuth flows (Spotify, Last.fm, SoundCloud)
├── bridge/ # WebView JS runtime for .axe plugins
├── data/ # Typealias shims to shared, local media scanner
├── playback/ # PlaybackController, PlaybackService, Spotify / Apple Music / SoundCloud handlers
├── resolver/ # ResolverManager, AndroidResolverRuntime, TrackResolverCache
├── ui/ # Compose screens, components, navigation, theme
└── widget/ # Home screen mini player widget
iosApp/
├── Parachord/ # SwiftUI app: screens, PlaybackRouter, MusicKit, background sync
└── ShareExtension/ # Share-to-Parachord extension
:shared. Each app supplies UI, playback engines and platform glue. Android files that moved to shared became typealiases in their original packages, so imports keep working.For the full list of "lessons learned the hard way", see CLAUDE.md.
A full security review was completed in April 2026; see SECURITY.md. Highlights:
CI_KEYSTORE_PATH is unset; there's no fallback to the debug keystore.EncryptedSharedPreferences or the iOS Keychain, never in plain preferences..claude/, play-service-account.json, local.properties and Secrets.xcconfig are gitignored.Plugin sandboxing (#107), meaning the .axe fetch allowlist and storage namespacing, is deferred until a broader plugin SDK revision shared with desktop.
GitHub Actions workflows live in .github/workflows/:
build.yml: on pushes to main, PRs and v* tags.
v*): does everything above, then publishes a GitHub Release with the APK and AAB attached, and uploads the AAB to the Play Console internal testing track (when the PLAY_SERVICE_ACCOUNT_JSON secret is present).android-tests.yml: the JVM unit test suites.ios-tests.yml: the shared tests on the iOS simulator, for changes to shared/ or the build config.sync-plugins.yml: opens a PR when the marketplace has newer versions of the bundled .axe plugins.apple-music-token-expiry.yml: a weekly check that files an issue 30 days before the CI Apple Music developer token expires.Required repository secrets:
LASTFM_API_KEY, LASTFM_SHARED_SECRET, SPOTIFY_CLIENT_ID, SOUNDCLOUD_CLIENT_ID, SOUNDCLOUD_CLIENT_SECRET, APPLE_MUSIC_DEVELOPER_TOKENCI_KEYSTORE_BASE64, CI_KEYSTORE_PASSWORDPLAY_SERVICE_ACCOUNT_JSONMIT, the same license as the desktop app.