When credit exhaustion occurred, the retry logic reused the same DiskShare object which could become invalid during the retry delay, causing "DiskShare has already been closed" errors. Now the share is re-acquired on each retry attempt. Added withShareRetry() functions that manage share lifecycle within the retry loop, and updated all SMB operations to use them. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> |
||
|---|---|---|
| app | ||
| build-ffmpeg | ||
| dependencies | ||
| gradle/wrapper | ||
| scripts | ||
| .gitignore | ||
| .gitmodules | ||
| build.gradle.kts | ||
| CLAUDE.md | ||
| gradle.properties | ||
| gradlew | ||
| gradlew.bat | ||
| plan.txt | ||
| README.md | ||
| settings.gradle.kts | ||
| shell.nix | ||
| TIDAL_API_REFERENCE.md | ||
| TIDAL_EVENT_API_REFERENCE.md | ||
PlaceboPlayer
A minimalist Android music player focused on audio quality for USB DACs.
Key Features
- Bit-perfect audio output - Direct USB DAC support with no resampling or mixing
- Gapless playback - Seamless transitions between tracks
- Folder-based navigation - No library, just browse your files
- SMB network storage - Access music on network shares (NAS, Windows shares, Samba)
- Privacy-first - No tracking, no analytics (network only used for SMB)
- AOSP compatible - Works on pure Android without Google services
- Tidal streaming - In-app streaming through Tidal
Technical Highlights
- Min SDK: API 34 (Android 14.0+) for advanced AudioTrack API
- Architecture: MVVM with Jetpack Compose
- Playback: Media3 fork with BitPerfectAudioOutputProvider for USB DACs
- Media Session: Lock screen controls and notification integration
- Data Architecture: Dual database design (persistent settings + regenerable cache)
- Persistent Database (userdata): Settings, playlists, playback history
- Cache Database (cache storage): Metadata, cover art (cleared by Android when storage low)
Known issues
- Tidal "recently played" is not populated. The "Enable playback event reporting" option was supposed to fix that but it doesn't work.
Planned features
- Smarter buffering implementation (for now we're only using ExoPlayer's built in player cache)
Quick Start
# Build debug APK
./gradlew assembleDebug
# Build release APK (requires keystore.properties)
./scripts/build_release.sh
# Start emulator with GUI, install the built apk and run it
./scripts/emulator.sh --gui
Release Builds
Create keystore.properties in the project root:
storeFile=/path/to/your/keystore.p12
storePassword=your_password
keyAlias=your_alias
keyPassword=your_password
Available Scripts
build_release.sh- Build signed release APKemulator.sh- Install APK and run app in emulator, starting the emulator if not already running.--gui- Enable graphical interface--install-apk <path>- Install specified APK instead of the default
app_restart.sh- Kill, reinstall and restart the app on emulatorapp_logcat.sh- Stream logs from the app (use with timeout)app_play_intent.sh- Trigger playback via intent (for automated testing)
Unit Tests
./gradlew test # Run JVM unit tests
./gradlew connectedAndroidTest # Run instrumented tests (device required)
Unit tests cover metadata extraction converters, storage location parsing, and in-flight deduplication. Instrumented tests verify FFmpeg extraction with real audio files.
Automated Testing & Debugging
The app supports intent-based playback triggering for automated testing:
# Trigger playback with test files
./scripts/app_play_intent.sh
# View logs (use timeout to limit output)
timeout 10 ./scripts/app_logcat.sh
# Reinstall and restart app
./scripts/app_restart.sh
The app_play_intent.sh script demonstrates how to:
- Send a
PLAY_AUDIOintent with a content:// URI - Provide an optional queue of tracks
- Trigger playback programmatically without UI interaction
See CLAUDE.md for technical details.
Documentation
- CLAUDE.md - Technical documentation for developers and AI agents
License
GPLv2+
Waveform extraction and display code is originally based on Andrii Serbeniuk's Amplituda and compose-audiowaveform code, which is licensed under the Apache 2.0 license.
The Tidal client functionality is originally based on https://github.com/0xf4b1/tidal-kt with heavy modifications. That code is licensed under GPL3.
For ffmpeg and androidx-media, see the LICENSE files in the respective submodule.