diff --git a/README.md b/README.md index ef2f630..91d0d0b 100644 --- a/README.md +++ b/README.md @@ -1,52 +1,63 @@ # BearWave for macOS -Native macOS 26 port of the Linux/KDE app BearWave. +Native macOS port of BearWave, focused on fast internet radio playback. -This repository is the current SwiftUI + AVFoundation version built as a -SwiftPM app. The product scope is focused internet radio on macOS. +BearWave for macOS is a SwiftUI + AVFoundation desktop app built around the +same product idea as the Linux version: quick station discovery, lightweight +playback, favorites, recents, and native desktop integration without turning +the app into a general-purpose audio suite. -## Current Scope +## Screenshots -- Radio Browser integration for Top, Germany, Netherlands, World, search, favorites, recents, and manual stations -- Native SwiftUI macOS UI with sidebar, list, detail panel, player bar, settings, and menu bar extra -- Station folders for organizing favorites and saved stations -- Stream playback through `AVPlayer` -- Playback states: connecting, buffering, playing, paused, stopped, failed -- Stream metadata display via `AVPlayerItemMetadataOutput` -- Station favicons/artwork with local disk cache -- Now Playing artwork resolution with fallback chain: metadata lookup, station favicon, app default artwork +![Main station browser](screens/screen1.png) + +![Favorites and station folders](screens/screen2.png) + +![Active playback state](screens/screen3.png) + +## Features + +- Native macOS 26 app built with SwiftUI +- Radio Browser integration +- Top, Germany, Netherlands, World, and Search Results pages +- Favorites, recents, and manual stations +- Station folders for organizing saved stations +- Menu bar controls - Mini Player with `Cmd+M` +- Explicit playback states: + - connecting + - buffering + - playing + - paused + - stopped + - failed +- Stream metadata support through `AVPlayerItemMetadataOutput` +- Station artwork/favicon caching +- Resume last station on launch +- English and German localization -## Project Layout +## Product Direction -- `Package.swift` - SwiftPM package with: - - `BearWaveCore` shared app logic and resources - - `BearWave` app executable - - `BearWaveChecks` lightweight verification executable -- `Sources/BearWaveApp` - App entrypoint and scene setup -- `Sources/BearWaveCore` - App logic split into: - - `App/` high-level app coordinator (`AppModel.swift`) - - `Models/` domain models (`RadioStation`, `StationGroup`, `PlaybackStatus`, `PlaybackError`, `StationPage`) - - `Services/` Radio Browser client, playback controller, artwork loading, Now Playing artwork resolution - - `Stores/` persistence, settings, station library - - `Views/` SwiftUI UI (sidebar, list, detail, player bar, settings, menu bar, mini player) - - `Support/` resource helpers - - `Resources/` localized strings, assets, entitlements placeholder -- `Tests/BearWaveChecks` - Executable smoke checks for decoding, search normalization, filtering, and library behavior -- `script/build_and_run.sh` - Local build, bundle, and launch entrypoint +This macOS version is intentionally **radio-first**. -## Build and Run +That means: +- internet radio is the core product +- native macOS behavior is preferred over KDE/Qt parity +- podcast features are intentionally out of scope + +## Build + +```bash +swift build --disable-index-store +``` + +Run the app bundle through the local staging script: ```bash ./script/build_and_run.sh ``` -Useful modes: +Useful script modes: ```bash ./script/build_and_run.sh --verify @@ -55,66 +66,66 @@ Useful modes: ./script/build_and_run.sh --telemetry ``` -## Development Notes +Run the lightweight checks: -- Target platform is `macOS 26` -- Preferred workspace is: - `/Users/sebastian/Development/radioappmac` -- Build with: - `swift build --disable-index-store` -- Lightweight verification: - `CLANG_MODULE_CACHE_PATH=/private/tmp/bearwave-clang-cache swift run --disable-index-store BearWaveChecks` -- The generated app bundle lives in: - `./dist/BearWave.app` -- The run script stages SwiftPM resource bundles and localization directories manually into the app bundle +```bash +CLANG_MODULE_CACHE_PATH=/private/tmp/bearwave-clang-cache swift run --disable-index-store BearWaveChecks +``` -## Playback and Metadata Notes +## Project Layout -- Audio playback uses `AVPlayer` -- Stream metadata is read through `AVPlayerItemMetadataOutput` -- `PlaybackController` exposes explicit playback states instead of a simple play/pause boolean -- Some radio streams provide current-song metadata; many do not -- `MediaPlayer` is imported with `@preconcurrency` because the app updates `MPNowPlayingInfoCenter` -- `MPMediaItemArtwork` is created from a stable `CGImage` request handler -- ATS is explicitly opened for media and `http://` streams in the generated app `Info.plist`, otherwise many radio streams do not play +- `Package.swift` + SwiftPM package definition +- `Sources/BearWaveApp` + app entrypoint and scene wiring +- `Sources/BearWaveCore` + shared app logic, models, stores, services, views, and resources +- `Tests/BearWaveChecks` + lightweight verification executable +- `script/build_and_run.sh` + local build, bundle, and launch script +- `screens/` + README screenshots ## Persistence -Data is stored under native macOS locations: +BearWave stores data under native macOS locations: - `Application Support/BearWave/` - `favorites.json` - `manual-stations.json` - `groups.json` - `state.json` - - `Caches/BearWave/` - `api_cache/` - artwork cache files -The app also tries to import old Linux-style files on first run if present: +On first run it can also import older Linux-style BearWave files when present: - `~/.config/bearwave/favorites.json` - `~/.config/bearwave/state.json` -## Current UX Behavior +## Current Status -- Search is global against Radio Browser and independent from the current browse page -- Search results appear on a dedicated `Search Results` page -- Search ranking intentionally filters naive mid-word matches such as `slam` matching `Islamabad` -- A right-side detail panel shows station metadata and current stream metadata when available -- German localization is active across the app +Implemented and working: +- radio browsing and global search +- station playback +- playback status reporting +- metadata display +- favorites, recents, manual stations +- station folders +- menu bar controls +- mini player -## Known Technical Edges +Still pragmatic rather than release-ready: +- SwiftPM-staged `.app` bundle +- debug-oriented signing and packaging +- custom app-bundle staging script -- The app is currently a SwiftPM-staged `.app`, not an Xcode-managed release app -- Debug signing and sandbox behavior are intentionally pragmatic, not release-ready -- Resource handling is functional but still tied to the custom staging script +## Roadmap -## Suggested Next Work - -- Improve packaging, signing, and notarization -- Add more targeted playback diagnostics per stream failure mode -- Expand tests around playback-state transitions and search ranking -- Add cache eviction for memory and disk caches -- Keep station folders lightweight and radio-focused +- improve release packaging, signing, and notarization +- expand playback diagnostics +- strengthen tests around playback state and search ranking +- refine cache management +- keep station folders lightweight and clearly radio-focused diff --git a/screens/screen1.png b/screens/screen1.png new file mode 100644 index 0000000..82a9e04 Binary files /dev/null and b/screens/screen1.png differ diff --git a/screens/screen2.png b/screens/screen2.png new file mode 100644 index 0000000..a7338ed Binary files /dev/null and b/screens/screen2.png differ diff --git a/screens/screen3.png b/screens/screen3.png new file mode 100644 index 0000000..5ed4d2d Binary files /dev/null and b/screens/screen3.png differ