<feed xmlns='http://www.w3.org/2005/Atom'>
<title>rsplayer, branch main</title>
<subtitle>Daemon focused CLI music player
</subtitle>
<id>https://git.2137697.xyz/rsplayer/atom?h=main</id>
<link rel='self' href='https://git.2137697.xyz/rsplayer/atom?h=main'/>
<link rel='alternate' type='text/html' href='https://git.2137697.xyz/rsplayer/'/>
<updated>2026-11-02T12:46:43Z</updated>
<entry>
<title>ctl: show the candidates when completion is ambiguous</title>
<updated>2026-11-02T12:46:43Z</updated>
<author>
<name>salmonik</name>
<email>salmonik@tuta.io</email>
</author>
<published>2026-11-02T12:46:43Z</published>
<link rel='alternate' type='text/html' href='https://git.2137697.xyz/rsplayer/commit/?id=90114cb5a3cb9ddf67b75f5a5c3c16841a6019ad'/>
<id>urn:sha1:90114cb5a3cb9ddf67b75f5a5c3c16841a6019ad</id>
<content type='text'>
Tab now behaves like bash: it extends the word to the longest common prefix
and prints every candidate as a column list, instead of quietly cycling
through them one per press. Above completion_prompt_limit (100) it asks before
dumping the list.

Candidates are inserted with their spaces escaped, so a completed track name
stays one argument after Enter; the list itself shows the plain name.
</content>
</entry>
<entry>
<title>todo: config caching is done</title>
<updated>2026-11-02T12:36:45Z</updated>
<author>
<name>salmonik</name>
<email>salmonik@tuta.io</email>
</author>
<published>2026-11-02T12:36:45Z</published>
<link rel='alternate' type='text/html' href='https://git.2137697.xyz/rsplayer/commit/?id=a25f660e4a48123e015d75a463a1660f87d4d6b4'/>
<id>urn:sha1:a25f660e4a48123e015d75a463a1660f87d4d6b4</id>
<content type='text'>
</content>
</entry>
<entry>
<title>ctl: interactive shell with history, editing and completion</title>
<updated>2026-11-02T12:35:56Z</updated>
<author>
<name>salmonik</name>
<email>salmonik@tuta.io</email>
</author>
<published>2026-11-02T12:35:56Z</published>
<link rel='alternate' type='text/html' href='https://git.2137697.xyz/rsplayer/commit/?id=5dfb5b4ce521a44eb4e9173d20c58e665c78e979'/>
<id>urn:sha1:5dfb5b4ce521a44eb4e9173d20c58e665c78e979</id>
<content type='text'>
`rsp ctl` opens a prompt in the spirit of bluetoothctl. Lines are parsed with
the same clap definitions as the one-shot CLI, so every command and flag works
exactly as it does from a terminal, `help &lt;command&gt;` included.

Tab completes subcommands, and track/album/artist paths for `play` through
`db-complete` (run out of process, keeping its 100 ms timeout away from the
input loop). History lives in the state directory. Piped input falls back to a
plain line loop, so `rsp ctl &lt; commands` works without a terminal.
</content>
</entry>
<entry>
<title>audio: open the output at the track's rate instead of pw-alsa's 44100</title>
<updated>2026-11-02T12:28:23Z</updated>
<author>
<name>salmonik</name>
<email>salmonik@tuta.io</email>
</author>
<published>2026-11-02T12:28:23Z</published>
<link rel='alternate' type='text/html' href='https://git.2137697.xyz/rsplayer/commit/?id=6e62ee4057b8ea3144f2f4df85e5d56c37c8fd72'/>
<id>urn:sha1:6e62ee4057b8ea3144f2f4df85e5d56c37c8fd72</id>
<content type='text'>
cpal asks the ALSA "default" PCM for its default config and pipewire-alsa
answers 44.1 kHz no matter what the graph runs at, so 48 kHz material was
resampled down to 44.1 in our linear resampler and then back up to 48 by
PipeWire. The stream now opens at the track's own rate (when the device
supports it), which keeps our resampler out of the path and leaves the
conversion to PipeWire's polyphase one.

The new `sample_rate` setting forces a fixed rate instead ("auto" by
default), for anyone who would rather match the graph exactly.
</content>
</entry>
<entry>
<title>config: parse the config once and reuse it until the file changes</title>
<updated>2026-11-02T12:22:44Z</updated>
<author>
<name>salmonik</name>
<email>salmonik@tuta.io</email>
</author>
<published>2026-11-02T12:22:44Z</published>
<link rel='alternate' type='text/html' href='https://git.2137697.xyz/rsplayer/commit/?id=2c044de38f6adcfbd3cc64eb9fa55e67d61e6f94'/>
<id>urn:sha1:2c044de38f6adcfbd3cc64eb9fa55e67d61e6f94</id>
<content type='text'>
Every command, status line and notification went through Config::load, which
reads *and* rewrites config.toml, so `rsp status` alone touched the file on
each call.

The daemon now keeps a parsed copy and only stats the file to notice outside
edits, so `rsp settings set` still takes effect immediately. Config::save
refreshes the cached copy too, and Config::config_path is shared by the loader
and the cache.
</content>
</entry>
<entry>
<title>seek: jump within the current track from the CLI and over MPRIS</title>
<updated>2026-11-02T12:19:49Z</updated>
<author>
<name>salmonik</name>
<email>salmonik@tuta.io</email>
</author>
<published>2026-11-02T12:19:49Z</published>
<link rel='alternate' type='text/html' href='https://git.2137697.xyz/rsplayer/commit/?id=50cd2a17734dee4c4739112261fc40974cce409a'/>
<id>urn:sha1:50cd2a17734dee4c4739112261fc40974cce409a</id>
<content type='text'>
`rsp seek 1:30`, `rsp seek +15`, `rsp seek -30` and `rsp seek` (print the
position) drive a new AudioCommand that seeks the format reader, resets the
decoder and drops the buffered samples, so the cpal stream keeps running and
playback continues from the new spot.

MPRIS gets a working Seek/SetPosition, reports CanSeek, and the state watcher
now emits Seeked when the playhead jumps (not just on track changes), which is
what clients need to move their progress bar after an external seek.

`rsp status` also reports the position (and duration when known) and says
Paused instead of Stopped when a track is loaded but not playing.
</content>
</entry>
<entry>
<title>state: persist the queue and pick up where playback stopped</title>
<updated>2026-11-02T12:12:48Z</updated>
<author>
<name>salmonik</name>
<email>salmonik@tuta.io</email>
</author>
<published>2026-11-02T12:12:48Z</published>
<link rel='alternate' type='text/html' href='https://git.2137697.xyz/rsplayer/commit/?id=863b4017714130f042f993e7c9d0f3f8adee22bf'/>
<id>urn:sha1:863b4017714130f042f993e7c9d0f3f8adee22bf</id>
<content type='text'>
The queue, its shuffle order and whether playback was running are written to
~/.local/state/rsplayer/state.json whenever they change, and on shutdown, then
restored at daemon start. Tracks that disappeared from disk are dropped
instead of failing on play.

`rsp resume`/`rsp toggle` now start the restored track when nothing is loaded
into the engine yet, which is the state right after a restart with a queue
that was paused.
</content>
</entry>
<entry>
<title>queue: use let chains and tidy imports</title>
<updated>2026-11-02T12:02:11Z</updated>
<author>
<name>salmonik</name>
<email>salmonik@tuta.io</email>
</author>
<published>2026-11-02T12:02:11Z</published>
<link rel='alternate' type='text/html' href='https://git.2137697.xyz/rsplayer/commit/?id=7683b319b851df5e66465db4916e239b788c09fa'/>
<id>urn:sha1:7683b319b851df5e66465db4916e239b788c09fa</id>
<content type='text'>
</content>
</entry>
<entry>
<title>install.sh: only install the systemd service on linux</title>
<updated>2026-11-02T12:02:11Z</updated>
<author>
<name>salmonik</name>
<email>salmonik@tuta.io</email>
</author>
<published>2026-11-02T12:02:11Z</published>
<link rel='alternate' type='text/html' href='https://git.2137697.xyz/rsplayer/commit/?id=2fa1a8d072df9875808f07af27a9834eee3f726f'/>
<id>urn:sha1:2fa1a8d072df9875808f07af27a9834eee3f726f</id>
<content type='text'>
</content>
</entry>
<entry>
<title>mpris: report trackid as an object path, drop artUrl, track loop status</title>
<updated>2026-11-02T12:02:11Z</updated>
<author>
<name>salmonik</name>
<email>salmonik@tuta.io</email>
</author>
<published>2026-11-02T12:02:11Z</published>
<link rel='alternate' type='text/html' href='https://git.2137697.xyz/rsplayer/commit/?id=42cf7e9ba7ea36e04b970e132ebd7cf59c54dd33'/>
<id>urn:sha1:42cf7e9ba7ea36e04b970e132ebd7cf59c54dd33</id>
<content type='text'>
An empty artUrl is not a valid URI and makes some clients render a broken
image, and the repeat button snapped back to "None" because LoopStatus was
not stored.
</content>
</entry>
</feed>
