diff --git a/.markdownlint.yml b/.markdownlint.yml new file mode 100644 index 0000000..ff7d7cc --- /dev/null +++ b/.markdownlint.yml @@ -0,0 +1 @@ +MD013: false diff --git a/README.md b/README.md index f9d6f23..1b6d868 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # STMPS (Subsonic Terminal Music Player S) -A terminal client for *sonic music servers. Inspired by ncmpcpp and musickube. +*Stamps* is a terminal client for *sonic music servers, inspired by ncmpcpp and musickube. Main Branch: [![Build macOS arm64](https://github.com/spezifisch/stmps/actions/workflows/build-macos-arm.yml/badge.svg)](https://github.com/spezifisch/stmps/actions/workflows/build-macos-arm.yml) @@ -12,34 +12,35 @@ Dev Branch: ## Features -* browse by folder -* queue songs and albums -* create and play playlists -* favorites -* volume control -* server-side scrobbling (e.g. on Navidrome, gonic) -* [MPRIS2](https://mpris2.readthedocs.io/en/latest/) control +- Browse by folder +- Queue songs and albums +- Create and play playlists +- Mark favorites +- Volume control +- Server-side scrobbling (e.g., on Navidrome, gonic) +- [MPRIS2](https://mpris2.readthedocs.io/en/latest/) control and metadata ## Screenshots -These are using [Navidrome's demo server](https://demo.navidrome.org/) ([config file](./stmp-navidromedemo.toml)). +These screenshots use [Navidrome's demo server](https://demo.navidrome.org/) ([config file](./stmp-navidromedemo.toml)). -Queue: +### Queue ![Queue View](./docs/screenshots/queue.png) -Browser: +### Browser ![Browser View](./docs/screenshots/browser.png) ## Dependencies -[mpv](https://mpv.io): +### Required Software -* Linux (Debian/Ubuntu): `apt install pkg-config libmpv libmpv-dev` -* MacOS (Homebrew): `brew install pkg-config mpv` (not the cask) +- [mpv](https://mpv.io) + - Linux (Debian/Ubuntu): `apt install pkg-config libmpv libmpv-dev` + - MacOS (Homebrew): `brew install pkg-config mpv` (not the cask) -Go build dependencies) +### Go Build Dependencies * Go 1.19+ * [tview](https://github.com/rivo/tview) @@ -47,71 +48,106 @@ Go build dependencies) ## Compiling -stmp should compile normally with `go build`. Cgo is needed for linking with libmpv. +Compile STMPS with `go build`. Cgo is needed for interfacing with libmpv. ## Configuration -stmp looks for a config file called `stmp.toml` in either `$HOME/.config/stmp` -or the directory in which the executable is placed. +STMPS looks for a configuration file named `stmp.toml` in either `$HOME/.config/stmp` or the directory containing the executable. -### Example configuration +### Example Configuration ```toml [auth] username = 'admin' password = 'password' -plaintext = true # Use 'legacy' unsalted password auth. (default: false) +plaintext = true # Use 'legacy' unsalted password authentication (default: false) [server] host = 'https://your-subsonic-host.tld' -scrobble = true # Use Subsonic scrobbling for last.fm/ListenBrainz (default: false) +scrobble = true # Use Subsonic scrobbling for last.fm/ListenBrainz (default: false) + +[client] +random-songs = 50 ``` ## Usage -* Q - quit -* 1 - folder view -* 2 - queue view -* 3 - playlist view -* 4 - log (errors, etc) view -* Escape/Return - close modal if open +### General Navigation -### Playback +- `Q`: Quit +- `1`: Folder view +- `2`: Queue view +- `3`: Playlist view +- `4`: Log (errors, etc.) view +- `Escape`/`Return`: Close modal if open -These are accessible in every view. +### Playback Controls -* p - play/pause -* P - stop -* > - next song -* -/= volume down/volume up -* ,/. seek -10/+10 seconds -* r - add 50 random songs to the queue +These controls are accessible from any view: -### Browser +- `p`: Play/pause +- `P`: Stop +- `>`: Next song +- `-`/`=`: Volume down/volume up +- `,`/`.`: Seek -10/+10 seconds +- `r`: Add 50 random songs to the queue -* Enter - play song (clears current queue) -* a - add album or song to queue -* y - toggle star on song/album -* A - add song to playlist -* R - refresh the list (if in artist directory, only refreshes that artist) -* / - Search artists -* n - Continue search forward -* N - Continue search backwards +### Browser Controls -### Queue +- `Enter`: Play song (clears current queue) +- `a`: Add album or song to queue +- `y`: Toggle star on song/album +- `A`: Add song to playlist +- `R`: Refresh the list (if in artist directory, only refreshes that artist) +- `/`: Search artists +- `n`: Continue search forward +- `N`: Continue search backward + +### Queue Controls -* d/Delete - remove currently selected song from the queue -* D - remove all songs from queue -* y - toggle star on song +- `d`/`Delete`: Remove currently selected song from the queue +- `D`: Remove all songs from queue +- `y`: Toggle star on song -### Playlist +### Playlist Controls -* n - new playlist -* d - delete playlist -* a - add playlist or song to queue +- `n`: New playlist +- `d`: Delete playlist +- `a`: Add playlist or song to queue ## Credits -* This is a fork of [STMP](https://github.com/wildeyedskies/stmp), see -[AUTHORS](./AUTHORS). I decided to rename my fork as its codebase has diverged -quite a bit. +This is a fork of [STMP](https://github.com/wildeyedskies/stmp). See [AUTHORS](./AUTHORS) for more information. The codebase has diverged significantly from the original. + +## Advanced Configuration and Features + +### MPRIS2 Integration + +To enable MPRIS2 support (Linux only), run STMPS with the `-mpris` flag. Ensure you have D-Bus set up correctly on your system. + +### MacOS Media Control + +On MacOS, STMPS integrates with the native MediaPlayer framework to handle system media controls. This is automatically enabled if running on MacOS. *Note:* This is work in progress. + +### Profiling + +To profile the application, use the following flags: + +- `-cpuprofile=`: Write CPU profile to `file` +- `-memprofile=`: Write memory profile to `file` + +These flags are useful for performance debugging and analysis. + +### Debugging and Logs + +View logs and error messages in the log view by pressing `4`. This can help diagnose issues with server connections, playback, or other functionalities. + +## Contributing + +Contributions are welcome! Feel free to open issues or submit pull requests on GitHub. For major changes, please discuss first to ensure alignment with the project goals. + +## Licensing + +STMPS is licensed under the GNU General Public License v3.0 (GPL-3.0-only). This license allows you to freely use, modify, and distribute the software, provided that any distributed versions of the software, or derivative works, are also licensed under the GPL-3.0-only. + +For more details, refer to the [LICENSE](./LICENSE) file in the repository. diff --git a/remote/mpris2.go b/remote/mpris2.go index f8a697f..b9c66c1 100644 --- a/remote/mpris2.go +++ b/remote/mpris2.go @@ -225,9 +225,9 @@ func (m *MprisPlayer) Previous() *dbus.Error { return nil } -func (m *MprisPlayer) Seek(offset int64, _ int) *dbus.Error { +func (m *MprisPlayer) Seek(offset int64, _ int) (int64, error) { // TODO not implemented - return nil + return 0, nil } func (m *MprisPlayer) SetPosition(trackId dbus.ObjectPath, position int64) *dbus.Error { diff --git a/stmps.go b/stmps.go index 93b47f7..0d5532c 100644 --- a/stmps.go +++ b/stmps.go @@ -105,6 +105,7 @@ func main() { connection.Host = viper.GetString("server.host") connection.PlaintextAuth = viper.GetBool("auth.plaintext") connection.Scrobble = viper.GetBool("server.scrobble") + connection.RandomSongNumber = viper.GetUint("client.random-songs") indexResponse, err := connection.GetIndexes() if err != nil { diff --git a/subsonic/api.go b/subsonic/api.go index 7da6aff..3bcf67d 100644 --- a/subsonic/api.go +++ b/subsonic/api.go @@ -15,17 +15,18 @@ import ( ) type SubsonicConnection struct { - Username string - Password string - Host string - PlaintextAuth bool - Scrobble bool + Username string + Password string + Host string + PlaintextAuth bool + Scrobble bool + RandomSongNumber uint - clientName string - clientVersion string + clientName string + clientVersion string - logger logger.LoggerInterface - directoryCache map[string]SubsonicResponse + logger logger.LoggerInterface + directoryCache map[string]SubsonicResponse } func Init(logger logger.LoggerInterface) *SubsonicConnection { @@ -241,8 +242,12 @@ func (connection *SubsonicConnection) GetMusicDirectory(id string) (*SubsonicRes func (connection *SubsonicConnection) GetRandomSongs() (*SubsonicResponse, error) { query := defaultQuery(connection) - // Let's get 50 random songs, default is 10 - query.Set("size", "50") + // Try loading the number of random songs from the config file (and clamp it to 500) if not, default to 50 + if connection.RandomSongNumber > 0 && connection.RandomSongNumber < 500 { + query.Set("size", strconv.FormatInt(int64(connection.RandomSongNumber), 10)) + } else { + query.Set("size", "50") + } requestUrl := connection.Host + "/rest/getRandomSongs" + "?" + query.Encode() resp, err := connection.getResponse("GetRandomSongs", requestUrl) if err != nil {