These 48 commits are when the Protocol Buffers files have changed:
| Commit: | 05c31a5 | |
|---|---|---|
| Author: | Tsiry Sandratraina | |
| Committer: | Tsiry Sandratraina | |
feat(analytics): listening history across every source, in DuckDB `play_history` records every listen, but a library that has only ever been played here goes back as far as the install and no further. A new DuckDB engine imports the history you can export from elsewhere — Spotify Extended Streaming History, Last.fm CSV/JSON, and Rocksky scrobbles over the public AppView — and keeps it alongside what the player records. DuckDB reads the player's SQLite and never writes it; the analytics file can be deleted and rebuilt from its sources at any time. Sources are deduplicated against each other by measurement rather than assumption. The same listen frequently arrives from two exports at once and not at the same instant: measured against a real library, 11k Last.fm scrobbles sat exactly three hours after their Spotify twin, because the scrobbler submitted local time as UTC. Unioned naively that inflates a nine-year history by a third and double-counts exactly the tracks played most. The engine takes the modal clock offset between each ordered pair of sources, trusts it only when enough pairs agree, and folds duplicates within four minutes of it, keeping the copy from the source that knows most. Hard-coding an offset would have been right for one timezone. Matching is on the primary artist credit and a Unicode-folded title, because Spotify records the album artist alone where Last.fm records the full credit, and a title written in Japanese or Cyrillic has no ASCII letters to fold on. `music-player analytics` covers import, rocksky, sync, enrich, overview, top, clock, sessions, skips, drift, transitions, rotation, on-this-day, origins, and query for anything the rest does not. The same figures are on the daemon as AnalyticsService and as listening_* MCP tools, and the desktop Statistics tab grows a Listening History panel: headline figures, a weekday x hour heatmap, listens over time, and all-time artist and track rankings with artwork. A switch restricts any of it to music the local library has. Building now needs ./scripts/fetch-duckdb.sh once before cargo. It fetches the prebuilt static DuckDB from upstream's release into vendor/, so nothing compiles the DuckDB C++ sources and the binary stays self-contained. It cannot be a build script: libduckdb-sys resolves the library when its own crate compiles, and Cargo offers no way to order another crate's build script ahead of that.
The documentation is generated from this commit.
| Commit: | 7807ef2 | |
|---|---|---|
| Author: | Tsiry Sandratraina | |
| Committer: | Tsiry Sandratraina | |
feat(playback): stream a real 16-band spectrum with the output levels The full player's equalizer bars were synthesised from two bass-band scalars — shaped noise, not a spectrum, which is why they neither respected the frequency content nor moved convincingly. The engine (rockbox-playback 0.7.0) now measures 16 log-spaced bands per output buffer via a one-pole low-pass ladder over the signed mix — the difference of two neighbouring stages is a band-pass — cheap enough for the audio callback and spectrally verified upstream (a 60 Hz tone lands in the bottom bands, 8 kHz near the top). `Levels` carries the bands through tracklist and the gRPC stream (`repeated float bands`), zero for older engines, so every consumer keeps working. TEMPORARY: rockbox-playback 0.7.0 is not on crates.io yet, so the workspace carries a [patch.crates-io] pointing it (and its path-dependency siblings rockbox-codecs / rockbox-dsp / rockbox-metadata, which must resolve to the same package identity or their `links =` native libraries appear twice) at the sibling ../rockbox-zig checkout. Publish 0.7.0 and delete the patch.
| Commit: | 8d78750 | |
|---|---|---|
| Author: | Tsiry Sandratraina | |
| Committer: | Tsiry Sandratraina | |
feat: key and tempo on every track, read from the file before being guessed Two new columns on `track`, shown in every track list on the desktop and the web client, with the key drawn as a colour on the row's left edge the way a DJ tool does — finding what mixes is spotting neighbouring colours, which is much faster than reading two-character labels down a column. Keys are in **Camelot** (`8A`), not "A minor". The notation exists because adjacent numbers mix, which is the entire reason to see a key while choosing what plays next; "A minor" says the same thing and hides it. The colour follows position on the circle of fifths for the same reason — neighbours there are the keys that go together, unlike neighbours in the alphabet. Relative major and minor share a hue because they share a key signature, with the minor darker so the two stay apart. **The file is asked before anything is computed.** A track tagged `Fm` came back as F major: correlation-based detection picks the tonic well and the mode badly, because the two profiles for one tonic look alike. That is not a small error — F major is 7B and F minor is 4A, opposite ends of the wheel. Mixxx, Rekordbox and Traktor all read the tag first, which is why a tagged library agreed with itself everywhere except here. The tag arrives namespaced in mp4 — `com.apple.iTunes:initialkey` — so the vendor prefix is stripped before matching. Without that the tag every DJ tool writes into an m4a is invisible, which is exactly how a track marked `4A` was reported as its parallel major. Camelot, `Fm`, `F minor` and a bare `F` are all understood; anything else is left alone, since a wrong key is worse than none because it gets acted on. **A scan used to leave every key null**, for two independent reasons: the pass hung off `refresh_music_library` while the CLI calls `scan_music_library`, and it was detached, so a command that exits when it returns killed it before it decoded anything. It is awaitable now, and the two callers choose — `scan` awaits it and logs each track as it goes, the daemon detaches it. A library function no longer spawns work as a side effect, which is also what had the pass running twice. The columns appear only when the library can answer for them — the daemon's own or another music-player — rather than sitting empty against Subsonic or Jellyfin, where an empty column reads as broken rather than absent. A null key shows nothing at all; a null tempo shows a dash, because a blank in a numeric column reads as a fault. Also here, from the full-screen player: the equalizer is full-bleed along the bottom edge, 96 flat bars with floating peak caps, and `v` toggles it. The scrim over the artwork is tinted with the *window* colour rather than a fixed near-black — every control on that canvas is themed, and a dark scrim under a light skin left all of them dark on dark. The artwork now matches the waveform's width instead of being sized off the container height, which had it both smaller than the waveform and, since a width inside a `VerticalLayout` does not centre, sitting to one side of it. Verified against the track that started this: 4A, 162 BPM — what Mixxx shows.
| Commit: | e5b7f30 | |
|---|---|---|
| Author: | Tsiry Sandratraina | |
| Committer: | Tsiry Sandratraina | |
feat: hear the music — tempo, mood, loudness, waveforms, and a DJ that uses them A track can now be decoded once and measured: a **waveform**, its **loudness** (EBU R128), its **tempo**, and its **mood** as a point in valence/arousal space. One decode pass produces all four — decoding is nearly the whole cost, and doing it per feature would decode each track three times. That is enough to answer "what should play after this?" with no genres, no tags and nobody else's listening history. **Auto-DJ** keeps the queue five deep, each track chosen to follow the last. Built as a chain rather than a ranking: every pick is measured against the one before it, so an hour travels a long way while each transition stays close. Ranking against a single seed gives a set that never leaves the first track's neighbourhood and then falls off a cliff. Half and double time count as one tempo. 140 and 70 are the same tempo counted differently, and without that they look further apart than anything else in the library and never follow each other. A target steers rather than filters. Weighted so it can overcome about one step of chain distance and no more: weaker and "make it more energetic" does nothing, stronger and every pick jumps to the most extreme match. It never interrupts what is playing — that is the whole difference between it and shuffle. **The waveform is under the artwork in both full-screen players**, and doubles as a seek bar: you can aim at the quiet part you remember rather than at a percentage. Bars are normalised to percentiles, not to the maximum, so one stray click cannot flatten the whole track — with a test stating exactly that, as "the drawing is unchanged with and without it". **Live equalizer bars** in both, driven by the daemon's `levels` feed, so they move with audio actually leaving the output — including on a cast device the client never decodes. The daemon measures four numbers, not a spectrum, so the response is shaped rather than an FFT: bars lean on the channel nearest them so the display is stereo, and low bars are steadier than high ones because bass is sustained and treble is transient. Same shaping in both clients — one instrument should look like the same instrument. Results are cached in SQLite, keyed by library as well as track: ids are only unique within a provider, and without that a Navidrome track and a local one could share a row and draw the wrong waveform. Artist and title ride along denormalised, so ranking a whole library is not a request per track. Four MCP tools expose it, and the skill teaches steering over micromanaging. **Also fixed: the desktop never showed the signed-in account.** gRPC and http start separately, and it asked the moment gRPC answered — before anything was listening. One attempt, and a failure wrote an empty handle, which reads exactly like "nobody is signed in". It now retries, and a failure leaves the row alone rather than lying about it. The web client was never affected because it cannot load at all until http is up. Verified end to end against an isolated daemon: two generated tracks at 76 and 120 BPM were detected as 76.0 and 120.0, and auto-DJ queued them unprompted.
| Commit: | d06dbc8 | |
|---|---|---|
| Author: | Tsiry Sandratraina | |
| Committer: | Tsiry Sandratraina | |
fix: likes past 500, a real web VU meter, and persisted playback modes **Likes were truncated at 500.** `getStarred2` is not a paged endpoint — it returns every star in one response — but the result ran through `normalize_limit`, which exists to respect the 500-row ceiling Subsonic's *paged* endpoints have. Past 500 likes the rest simply did not exist as far as any client could tell, which is why some hearts were right and some were not. It pages the result rather than the request now, and a limit of zero means all of it, which is what the clients ask for. A track also carries its own `liked` state where the source reports one — Subsonic on every song, Jellyfin's `IsFavorite` — and the heart prefers it. A snapshot of "everything liked" can always be incomplete; a per-track answer cannot. The flag survives the tracklist round trip, which is where it was being dropped. **The web client's VU meter was two constants.** `vuLeft={playing ? 0.55 : 0}` — it had never animated. It subscribes to the `levels` subscription now (which the daemon already served) and applies the same auto-gain and ballistics as the desktop, so both clients move the same way on the same track. **Shuffle and repeat did not survive a restart**, twice over: the queue snapshot did not record them, and no client could read them back — each kept its own copy that began at "off" however the session had ended. They are saved with the queue, held on the tracklist the API layers share, and reported with the now-playing response.
| Commit: | 7697977 | |
|---|---|---|
| Author: | Tsiry Sandratraina | |
| Committer: | Tsiry Sandratraina | |
build: depend on the published rockbox-playback 0.6.2 Drops the `[patch.crates-io]` block that pointed at a sibling checkout. It was always meant to be temporary, but a path outside the repository cannot resolve on a clean runner, so it broke CI — the patch should have been a git rev, or the crate published first. `playback/Cargo.toml` already asked for 0.6.2; it now comes from crates.io with a checksum like every other dependency. Only `rockbox-playback` needed publishing: the patch also listed `rockbox-metadata` and `rockbox-dsp` solely because a *path* dependency drags in a second copy of a `links` crate, which a registry dependency does not.
| Commit: | d7ad7b5 | |
|---|---|---|
| Author: | Tsiry Sandratraina | |
| Committer: | Tsiry Sandratraina | |
feat: VU meters that follow the audio, and federated search on the desktop
| Commit: | 31e56e9 | |
|---|---|---|
| Author: | Tsiry Sandratraina | |
| Committer: | Tsiry Sandratraina | |
fix: playlists load fully and fast, and albums stop refetching **Playlists showed a handful of tracks**, the same intersection bug the album detail had: the rpc layer sent only track *ids* to the UI, which then looked each one up in the cached library and dropped the misses. Against a remote server the cache is one page, so most of a playlist vanished. It now passes the tracks the daemon already returned. **Playlist rows always said "0 tracks".** A listing reports a count without sending entries, so counting `tracks` gave zero. `Playlist` carries a `track_count` now, populated from Subsonic's `songCount`, Jellyfin's `ChildCount` and Plex's `leafCount`, and `len()` falls back to counting what is held. The first column shows the playlist position rather than the track's old index in the library listing, which was arbitrary. **Opening an album was slow, and that was my doing.** The previous commit fixed the dropped-tracks bug by fetching each missing track — a dozen sequential round trips per album. The real problem was that the proto `Song` carried no uri, so a client holding an album listing could not play from it. It does now, along with album and artist, so an album open is **one** request: `open_album` also called `GetAlbumDetails` twice, once for the header and once inside the track fetch. **Both details are cached** by id, so navigating back does not re-ask. Dropped on a library reload, since a server switch makes every id belong to something else, and on the mutations that would make a cached copy wrong. Two more, both reported: the server switcher re-reads which server is current when it opens, instead of lighting up "this machine" whenever this side's copy was empty; and `listCastDevices` returns one row per device rather than per advertised service, which is why a peer daemon appeared three times in Play to.
| Commit: | 13b2ab3 | |
|---|---|---|
| Author: | Tsiry Sandratraina | |
| Committer: | Tsiry Sandratraina | |
fix: album detail and Liked were empty against a remote server Both had the same root cause: the desktop treated its cached track list as the whole library. That holds for local files and does not for a provider, whose cache is one page of something much larger. **Album detail showed a handful of tracks.** `fetch_album_tracks` intersected the album's songs with the cache and silently dropped the rest — a song in an album listing carries no uri, so the cache was doing real work, but a miss meant the row vanished. Misses are now fetched from the daemon, which routes `GetTrackDetails` through the provider. **Liked was always empty.** The desktop filtered its cache by a *local* like set — a file plus the ids matched in the local database — so Subsonic's ids never matched anything. A new `GetLikedTracks` RPC asks the daemon, which returns the provider's own stars (Subsonic) or favourites (Jellyfin), and falls back to the local set when nothing is connected. The TUI gets the same route for free. Neither client should be deriving from a partial cache what the daemon can answer exactly; these were the two places still doing it.
| Commit: | a01e759 | |
|---|---|---|
| Author: | Tsiry Sandratraina | |
| Committer: | Tsiry Sandratraina | |
feat: a Rocksky provider, and no duplicate renderers **Rocksky** is the hosted Navidrome at `navidrome.rocksky.app`. It is a Subsonic server, so it reuses that backend wholesale; what it adds is that there is nothing to configure but the login. That needed one new thing on the registry: `fixed_url`. A backend that always talks to one address says so, both APIs report it, and the clients drop the url field rather than asking for something they would ignore. The daemon substitutes the factory's url when saving, so a stale row or a client that sends one anyway cannot point Rocksky somewhere else. **Renderer discovery listed duplicates.** Chromecast discovery pushed every announcement unconditionally, and mDNS re-announces periodically — so the Play to list filled with copies of one speaker. All three discovery paths now share one `insert_unique`, keyed on id *and* service because a music-player peer legitimately advertises two records under one id. Only genuinely new devices are broadcast, so clients are not told about a device twice either.
| Commit: | 0749ac8 | |
|---|---|---|
| Author: | Tsiry Sandratraina | |
| Committer: | Tsiry Sandratraina | |
feat: a server switcher in the TUI, on C `C` opens an fzf-style list of saved servers over the library; picking one re-points every screen at it. "This machine" is always the first row, so there is always somewhere to read from, and choosing it goes back to the daemon's own files. Ctrl-n adds a server without leaving the overlay, Ctrl-d forgets the highlighted one, and typing filters by name *or* url — a server is as often known by its address as by its name. Reaching it needed a gRPC surface. `ServersService` mirrors the GraphQL resolvers over the same `ProviderState`, so the TUI, the Slint desktop and the web client cannot disagree about which server is connected. The password is accepted but never returned, and the form does not echo it, for the same reason. Connecting drops the library cache and re-reads tracks and playlists, because everything on screen came from the old server. It does not touch playback: a provider is where the library is *read*, and nothing on this path can reach the player. The switcher follows the fuzzy finder's shape — a flag on `App` that swallows keys while it is up — rather than introducing a second modal mechanism, and the connected row carries the same green dot the other two clients use.
| Commit: | 8a9e4ce | |
|---|---|---|
| Author: | Tsiry Sandratraina | |
| Committer: | Tsiry Sandratraina | |
feat: RSQL filters, smart playlists, and WebAssembly extensions Three subsystems, plus the UI work to reach them. RSQL (rsql/) A filter language that survives being typed by a human: `genre==rock;year>2000`, `(liked==true,playcount>10);lastplayed<30d`. Hand-written lexer and parser compiling to parameterized SQLite. Field names resolve through a Schema to fixed column expressions written here; every literal is bound, so no user text reaches SQL. Negations emit an explicit `IS NULL OR` arm — without it SQL's three-valued logic silently drops every untagged row from `genre!=rock`. Smart playlists A smart playlist stores a query, not a track list. It extends the existing `playlist` table rather than adding a parallel one, so every surface that already lists, opens, plays or deletes a playlist handles it unchanged — only the refill is new. Regenerating rewrites playlist_tracks in one transaction, and a library scan re-runs every stored filter. Filters are validated before anything is written, at all three entry points: an empty playlist left behind by a typo is worse than an error. New `track_stats` feeds `playcount`/`skipcount`/`lastplayed`. It shares the scrobbler's threshold but needs no account — "played more than five times" has to work on a laptop that has never signed in to anything. `track.created_at` is stamped on insert only; the scanner leaves it NotSet on update, or every rescan would reset "recently added". Forms in all three UIs — Slint, TUI and web — with a live match count and an RSQL editor that highlights and completes as you type. The colouring and completion come from one place (`rsql::editor`), so all three agree. Extensions (extensions/) WebAssembly extensions via Extism, with five capabilities: events, metadata, commands, smart-playlist predicates, and media sources. The host/guest contract is an XTP schema, so `xtp plugin init --schema-file` generates typed bindings for seven languages. Capabilities are declared in the manifest and are what gates which exports the host calls — every generated module exports every schema function, so the manifest is the only thing stopping a metadata provider being asked to handle events. Permissions (allowedHosts, libraryRead, config) are enforced host-side, never trusted from the guest. Network access is a permission rather than a capability because any capability can legitimately need it. Five worked examples, one per capability, each built and loaded by the host's integration tests. Plus `music-player extension init/list/install/ uninstall`, with the schema compiled into the binary so scaffolding works outside a checkout. Also Slint lost keyboard shortcuts intermittently: closing an overlay destroyed the focused TextInput without handing focus back, and only 14 of 45 dismissal sites restored it. Now the `show-*` flags are watched instead, so every path is covered — buttons, backdrops, Escape, the Rust side, and any added later. Verified by driving the running app. webui moved from create-react-app to Vite 8 (build 60s+ → 1.5s, dev server ready in 174ms), TypeScript 4.9 → 7.0.2, and Storybook 6/webpack5 → Storybook 10 on the Vite builder, with the 14 stories converted CSF2 → CSF3. `vite.config.ts` pins outDir to `build/` and port 3000 because rust-embed and Tauri both depend on them.
| Commit: | edd2a41 | |
|---|---|---|
| Author: | Tsiry Sandratraina | |
| Committer: | Tsiry Sandratraina | |
feat: heart clicks sync to Rocksky from the webui and the Slint desktop New music_player_storage::rocksky module (mirrors rockboxd's crates/rocksky — the SDK only reads loved songs, so the /likes REST endpoints are used directly): POST /likes with the track metadata, DELETE /likes/<sha256(title - artist - album)> to unlike, silent no-op without a `rocksky login` token, always fire-and-forget so UI handlers never block. Reached from both API layers: - gRPC: LibraryService.LikeTrack(id, like) — the Slint app calls it from its LikeTrack/LikeAlbum commands on top of the local liked store - GraphQL: likeTrack(id, like) mutation — the webui heart in the track row is now clickable, backed by a localStorage liked set (useLikes hook) and filled pink when liked
| Commit: | 4bcc436 | |
|---|---|---|
| Author: | Tsiry Sandratraina | |
| Committer: | Tsiry Sandratraina | |
feat: crossfade, media controls, artist pictures and a fluorescent TUI Playback/daemon: - Real crossfade/gapless: the engine now holds the current track plus one lookahead so transitions happen in-engine; the tracklist syncs when the engine advances and the lookahead is re-synced on queue mutations (repeat-one deliberately skips it) - Queue persistence is armed only by PlayerCommand::RestoreQueue (sent by daemon boots), so tests and `music-player open` never touch the saved queue - MPRIS media controls on Linux via souvlaki (playerctl, desktop widgets, media keys); macOS Now Playing center integration in the Slint app - Artist pictures batch-filled from the Rocksky API after every scan (new artist.picture column + migration), shown in the desktop Artists list - bitrate + sample_rate added to the Track proto Desktop: - VFD shows queue position, codec, bitrate and sample rate - `q` toggles the queue drawer, `b` the sidebar; VU meters animate at 60ms - reqwest rustls-tls for https artist pictures TUI/CLI: - Fluorescent neon theme, overridable via a [tui_theme] table in settings.toml (#hex or named colors) - Status line moved to the bottom with a SpaceVim-style charcoal bar; progress time readout in the default foreground - `u` toggles the play-queue view - clap 3 -> 4 with styled help (pocketenv palette); subcommand dispatch refactored into small handlers - Silence the bogus "A Tokio 1.x context ... shutdown" print on TUI exit (websocket reader noise during runtime shutdown) CI: - Discard test PCM via fifo:/dev/null instead of spraying stdout - release-slint-desktop builds the triggering ref, not the input tag (old tags predate the desktop crate)
| Commit: | d2db2be | |
|---|---|---|
| Author: | Tsiry Sandratraina | |
feat: add a new slint based desktop app version
| Commit: | 3235bda | |
|---|---|---|
| Author: | Tsiry Sandratraina | |
| Committer: | Tsiry Sandratraina | |
Modernize the entire stack: Rockbox engine, FTS5 search, ratatui TUI, Tauri 2 Playback - Replace symphonia/rodio/librespot pipeline with rockbox-playback 0.6 (40+ formats, EQ/crossfade/ReplayGain, native HTTP streaming); delete the audio crate entirely - Implement seek and volume end-to-end (engine -> gRPC/GraphQL -> clients); both were todo!() panics before - Player loop is now an async task (tokio::select over commands + status tick) on a dedicated current-thread runtime - audio_output setting: cpal (default), stdout, fifo, unix or tcp socket - Replace lofty with rockbox-metadata in the scanner and types Search - Replace tantivy with SQLite FTS5 virtual tables kept in sync by triggers; backfill migration included - Real search over CLI, TUI, gRPC (proto extended) and GraphQL Library - refresh semantics: prune tracks whose files are gone (plus orphaned albums/artists), then index new files - Parallel metadata parsing (spawn_blocking per file, bounded by CPU count) + single-transaction save: ~3000 tracks in about a second - Periodic background rescan via library_refresh_interval (minutes, 0=off) - Scan log reports track count and duration TUI (finished, ratatui 0.30) - fzf-style fuzzy finder on / (nucleo, match highlighting, scope tabs) - neovim-inspired status line + context-sensitive keybinding hint bar - ? help overlay; working play/pause/next/prev/seek/volume keys - Paged loading for large libraries; playlist sidebar wired; all todo!()/panic paths removed; render smoke tests Web UI - Apollo -> TanStack Query v5 + Jotai on React 18; TauriLink replaced by a shared fetcher/subscription layer - Seekable progress bar wired to the seek mutation with optimistic updates; live position via subscription instead of 500ms polling - Infinite loading on Tracks/Albums/Artists (pages of 100, virtualized) Desktop - Migrate Tauri 1.2 -> 2.11: v2 config schema, capabilities file, Emitter/Listener APIs, core.invoke globals Integrations - Subsonic/Navidrome and Jellyfin addons (browse + stream via authenticated URLs) configured from settings - Rocksky scrobbling (~/.rocksky/token.json, half-track/4-minute rule, scrobble=false to disable) - Remove unfinished addons: kodi, airplay, deezer, datpiff, genius, myvazo, tononkira, lastfm, musicbrainz Infrastructure - tonic 0.8 -> 0.14 (tonic-prost-build, GrpcWebLayer, http:// URIs) - Playlist gRPC client methods implemented (were todo!()) - Mixer service implemented (volume/mute) - tracing everywhere instead of println; RUST_LOG filter, sqlx quieted - Nix flake modernized (current crane API, lock regenerated) + manual nix-build workflow (ubuntu x64/arm64, macOS) - CI pinned to Rust 1.98.0 (workflows + fluentci) - npm installer package (npm i -g music-player / npx music-player) downloading verified binaries from GitHub releases, with MUSIC_PLAYER_VERSION override - Remove flakestry publish workflow - Tests self-bootstrap their database; 149 passing across the workspace - Version bumped to 0.2.0 across all crates; README rewritten with ToC
| Commit: | 472876d | |
|---|---|---|
| Author: | Tsiry Sandratraina | |
| Committer: | Tsiry Sandratraina | |
feat(api): add filter query parameter revert changes
| Commit: | bebed44 | |
|---|---|---|
| Author: | Tsiry Sandratraina | |
| Committer: | Tsiry Sandratraina | |
fix(player): enable remove_track_at and play_track_at for remote player fix(client): solve error with remote_track fix(client): solve issue with remove_track
| Commit: | ae47e07 | |
|---|---|---|
| Author: | Tsiry Sandratraina | |
feat: add start_index parameter
| Commit: | 19e0779 | |
|---|---|---|
| Author: | Tsiry Sandratraina | |
feat: connect to remote player
| Commit: | 6b5a92c | |
|---|---|---|
| Author: | Tsiry Sandratraina | |
| Committer: | Tsiry Sandratraina | |
fix(server): re-generate proto fix(server): re-generate proto
| Commit: | afebede | |
|---|---|---|
| Author: | Tsiry Sandratraina | |
| Committer: | Tsiry Sandratraina | |
feat: improve remote connect
| Commit: | 623f2e6 | |
|---|---|---|
| Author: | Tsiry Sandratraina | |
| Committer: | Tsiry Sandratraina | |
feat(api): add pagination
| Commit: | d1b08a3 | |
|---|---|---|
| Author: | Tsiry Sandratraina | |
| Committer: | Tsiry Sandratraina | |
feat(graphql): implement playlist queries and mutations work in progress work in progress
| Commit: | a7f3050 | |
|---|---|---|
| Author: | Tsiry Sandratraina | |
fix(proto): remove optional label
| Commit: | a35bca1 | |
|---|---|---|
| Author: | Tsiry Sandratraina | |
feat(scanner): read AlbumArtist information
| Commit: | 34a5043 | |
|---|---|---|
| Author: | Tsiry Sandratraina | |
refactoring object types
| Commit: | 35b1395 | |
|---|---|---|
| Author: | Tsiry Sandratraina | |
feat: display song progressbar
| Commit: | ad92c00 | |
|---|---|---|
| Author: | Tsiry Sandratraina | |
feat: highlight current track
| Commit: | d368036 | |
|---|---|---|
| Author: | Tsiry Sandratraina | |
| Committer: | Tsiry Sandratraina | |
feat: [tracklist] implement play_track_at(position) fix tracklist manager feat: [tracklist] implement play_track_at(index)
| Commit: | a8e149f | |
|---|---|---|
| Author: | Tsiry Sandratraina | |
| Committer: | Tsiry Sandratraina | |
feat: browse tracks by artist
| Commit: | b7b9e9b | |
|---|---|---|
| Author: | Tsiry Sandratraina | |
feat: browse tracks by artist
| Commit: | fa5a4e3 | |
|---|---|---|
| Author: | Tsiry Sandratraina | |
| Committer: | Tsiry Sandratraina | |
add play/pause from the terminal ui
| Commit: | 1d25a63 | |
|---|---|---|
| Author: | Tsiry Sandratraina | |
| Committer: | Tsiry Sandratraina | |
[album] show album tracks in the terminal ui
| Commit: | ec8c641 | |
|---|---|---|
| Author: | Tsiry Sandratraina | |
| Committer: | Tsiry Sandratraina | |
show tracks, artists and albums in the terminal ui
| Commit: | d9ba976 | |
|---|---|---|
| Author: | Tsiry Sandratraina | |
fix: sort album track by track_number
| Commit: | af82efd | |
|---|---|---|
| Author: | Tsiry Sandratraina | |
| Committer: | Tsiry Sandratraina | |
feat: control `music-player` from command-line
| Commit: | be87de0 | |
|---|---|---|
| Author: | Tsiry Sandratraina | |
feat: automatically scan music library
| Commit: | 4ff83ee | |
|---|---|---|
| Author: | Tsiry Sandratraina | |
| Committer: | Tsiry Sandratraina | |
feat: implement Create/Update/Delete Playlists bump version code
| Commit: | b6f86de | |
|---|---|---|
| Author: | Tsiry Sandratraina | |
| Committer: | Tsiry Sandratraina | |
api: implement GetTracklistTracks readme: update usage instructions update README.md
| Commit: | 43f457d | |
|---|---|---|
| Author: | Tsiry Sandratraina | |
| Committer: | Tsiry Sandratraina | |
api: implement GetTracklistTrack readme: update usage instructions update README.md
| Commit: | 2bc023a | |
|---|---|---|
| Author: | Tsiry Sandratraina | |
| Committer: | Tsiry Sandratraina | |
feat: implement Next/Previous track and tracklist loading bump version code update Cargo.toml
| Commit: | 64a28d8 | |
|---|---|---|
| Author: | Tsiry Sandratraina | |
| Committer: | Tsiry Sandratraina | |
feat: implement Next/Previous track and tracklist loading bump version code
| Commit: | acb96ff | |
|---|---|---|
| Author: | Tsiry Sandratraina | |
[api] implement get_artists, get_albums and get_tracks
| Commit: | 02e015d | |
|---|---|---|
| Author: | Tsiry Sandratraina | |
feat: scan music directory
| Commit: | cdf5957 | |
|---|---|---|
| Author: | Tsiry Sandratraina | |
| Committer: | Tsiry Sandratraina | |
feat: implemented AddTrack, Play, Pause and Stop RPC methods
| Commit: | 600682c | |
|---|---|---|
| Author: | Tsiry Sandratraina | |
proto: configure directory structure
| Commit: | 7fb8eb2 | |
|---|---|---|
| Author: | Tsiry Sandratraina | |
added simple grpc server