Commit Graph
40 Commits
Author SHA1 Message Date
mrleo1nidandCursor 39aaaed969 feat(web): list previous app builds on the download page
ci / api / api (push) Successful in 48s
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 58s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 1m17s
ci / ts / ts (packages + web) (push) Successful in 2m1s
ci / db / db (push) Successful in 1m4s
GET /app/clients now returns up to 8 Gitea releases per OS so users can download older installers while they still exist after prune.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-22 08:40:08 +03:00
mrleo1nidandCursor 83b604437c perf(desktop): ship a .deb instead of an 80MB AppImage
ci / api / api (push) Successful in 50s
ci / db / db (push) Successful in 50s
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Failing after 49s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 1m13s
ci / ts / ts (packages + web) (push) Canceled after 2m4s
ci / desktop / rust (desktop proxy) (push) Successful in 5m33s
release / release (версия и заметки) (push) Successful in 9s
release / android (APK в релиз) (push) Failing after 4m6s
release / desktop (Linux и Windows в релиз) (push) Successful in 17m20s
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-22 08:26:50 +03:00
mrleo1nidandCursor 73ad61e7a5 feat(genius): load song metadata, lyrics, and artwork
ci / ts / ts (packages + web) (push) Canceled after 1m0s
ci / api / api (push) Successful in 46s
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 56s
ci / db / db (push) Successful in 1m32s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 1m27s
ci / docker / docker (образы собираются — их сервер собирает сам при обновлении) (push) Successful in 1m31s
Search hits omit album covers and artist images. The worker now reads the full song and artist, stores the description, and leaves Genius image URLs unchanged on mobile.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-22 08:23:54 +03:00
mrleo1nidandCursor 597067689a feat: add a platform-aware apps download page
ci / api / api (push) Successful in 48s
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 1m10s
ci / db / db (push) Successful in 57s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 1m1s
ci / desktop / rust (desktop proxy) (push) Successful in 5m37s
ci / ts / ts (packages + web) (push) Successful in 4m10s
release / release (версия и заметки) (push) Successful in 11s
release / android (APK в релиз) (push) Successful in 4m22s
Serve desktop and Android builds from the latest Gitea release on /download, and fix the desktop compile and CI flakes that blocked v1.3.0.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-22 00:10:03 +03:00
mrleo1nidandCursor fa79394869 feat: add personal playlists and shareable track links
release / release (версия и заметки) (push) Successful in 6s
release / android (APK в релиз) (push) Successful in 4m8s
EOF

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-21 23:37:53 +03:00
mrleo1nid c34272d508 fix(auth): allow admins to change any username including their own
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 1m6s
ci / ts / ts (packages + web) (push) Successful in 2m59s
ci / api / api (push) Successful in 49s
ci / db / db (push) Successful in 1m17s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 1m25s
2026-10-21 17:33:19 +03:00
mrleo1nid 1e3b350dfc feat(upload): enhance audio upload handling and user feedback
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 1m1s
ci / db / db (push) Successful in 1m16s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 1m12s
ci / api / api (push) Successful in 45s
ci / recommender / recommender (push) Successful in 1m50s
ci / ts / ts (packages + web) (push) Failing after 6m53s
ci / docker / docker (образы собираются — их сервер собирает сам при обновлении) (push) Successful in 1m13s
- Introduced a new warning for users uploading non-FLAC or WAV formats, indicating that these formats should only be used as a last resort.
- Added functionality to highlight files needing format attention in the upload queue.
- Updated the UI to provide clearer guidance on acceptable audio formats during uploads.
- Improved documentation to reflect changes in upload requirements and user interface updates.
2026-10-21 10:07:43 +03:00
mrleo1nidandClaude Opus 5.5 74da457362 feat(auth): make upload review a separate grant instead of an admin privilege
Reviewing the upload queue is now a per-user flag (reviews_uploads), not part
of the admin role: several people can hold it, a moderator can have it, and an
admin may not. Only admins grant or revoke it; existing admins keep it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 10:07:05 +03:00
mrleo1nidandCursor 066ba24834 feat: finish the upload, playback, and test gaps from the docs
ci / api / api (push) Successful in 40s
ci / db / db (push) Successful in 1m3s
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Failing after 1m48s
ci / docker / docker (образы собираются — их сервер собирает сам при обновлении) (push) Successful in 46s
ci / ts / ts (packages + web) (push) Failing after 4m57s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 5m7s
Quality upgrades, batch covers, staging preview, and resume after reload are in the queue. Loudness and crossfade work on web and Android, and the documented checks now have suites.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-21 09:37:53 +03:00
mrleo1nidandClaude Opus 5.5 978f03b8f8 feat(admin): reset a user's recommendations from the users panel
ci / api / api (push) Successful in 40s
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 55s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 1m10s
ci / db / db (push) Successful in 2m35s
ci / ts / ts (packages + web) (push) Successful in 1m23s
- POST /admin/users/{id}/reset-recommendations clears the taste profile
  (onboarding, CF vector, bandit), play stats and wave impressions; likes,
  dislikes and history stay, wave sessions stay so a playing wave keeps going
- moderators may reset plain users, admins anyone including themselves;
  the reset is written to the audit log
- users panel gets the menu action with a confirmation; docs updated

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 08:52:31 +03:00
mrleo1nidandClaude Opus 5.5 b27b915b63 feat(web): settings to keep the search open and hide home pickers; collapsible menu sections
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 2m44s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 1m43s
ci / ts / ts (packages + web) (push) Successful in 1m16s
ci / api / api (push) Successful in 2m27s
ci / db / db (push) Successful in 2m23s
- appearance settings get "collapse search" (off keeps the field always open)
  and separate switches for the activity and mood pickers on the home page;
  stored in ui_prefs (search_collapse, home_activities, home_moods)
- Collection and Admin headers in the sidebar fold their items; the state is
  remembered in the browser, a folded Admin shows the review queue badge;
  icon-only and phone layouts always show every item

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 08:26:48 +03:00
mrleo1nidandClaude Opus 5.5 66f5749fc1 fix(deploy): take the Gitea address for Android updates from git remote
ci / api / api (push) Successful in 22s
ci / db / db (push) Successful in 2m4s
release / release (версия и заметки) (push) Successful in 5s
release / android (APK в релиз) (push) Successful in 4m11s
ci / docker / docker (образы собираются — их сервер собирает сам при обновлении) (push) Successful in 1m33s
The server kept the git.example.lan placeholder from .env.example, so the
/android page and in-app updates had no release. install.sh and update.sh now
fill GITEA_URL and GITEA_REPO from the clone's origin (https, ssh and scp
forms, credentials stripped) when they are empty or still the placeholder,
and the API treats the placeholder as not configured.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 00:23:28 +03:00
mrleo1nidandClaude Opus 5.5 860072e542 feat(web): choose where the search sits, and controls no longer select text
ci / api / api (push) Successful in 36s
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 48s
ci / ts / ts (packages + web) (push) Canceled after 12s
ci / db / db (push) Successful in 2m43s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 59s
- settings get a search position (left, center by default, right); the top bar
  is three zones and the suggestions open towards the free space
- buttons, chips, tabs, menu items and the home screen controls are not
  text-selectable, so clicks and double clicks no longer highlight labels

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 00:11:06 +03:00
mrleo1nidandClaude Opus 5.5 fda6ed48ab feat: Android download page, analysis retry and a collapsible search
ci / api / api (push) Successful in 37s
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 46s
ci / db / db (push) Failing after 14s
ci / recommender / recommender (push) Successful in 51s
ci / ts / ts (packages + web) (push) Successful in 57s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 1m2s
- /android is a public page with the latest APK, the server address to copy
  and three install steps; linked from the sign-in screen and the profile menu
- failed audio analysis can be retried from the library page, which also shows
  the most common error; the analyzer no longer burns attempts on files that
  are missing because the disk is not mounted
- the top bar search folds into a round button and slides open on click, "/"
  or Ctrl+K

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 23:55:51 +03:00
mrleo1nidandClaude Opus 5.5 3da82e902a fix(library): survive a music disk that is not mounted
ci / api / api (push) Successful in 29s
ci / db / db (push) Successful in 47s
ci / docker / docker (образы собираются — их сервер собирает сам при обновлении) (push) Successful in 52s
release / release (версия и заметки) (push) Successful in 4s
release / android (APK в релиз) (push) Successful in 3m20s
On the test server the host music folder was not mounted, so every stream and
fingerprint failed with no such file, and a scan of the empty folder would have
marked the whole library missing and purged it after 30 days.

- a scan of an empty source folder with known files aborts with a clear error
- fingerprinting skips such a source with one warning instead of one per file
- LIBRARY_HOST mounts use rslave, so a disk mounted after start shows up
- update.sh refuses to deploy with an empty LIBRARY_HOST

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 23:34:52 +03:00
mrleo1nidandClaude Opus 5.5 d91ff89c64 feat(admin): wave metrics split by model version (M6)
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 51s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 1m5s
ci / ts / ts (packages + web) (push) Successful in 1m3s
ci / api / api (push) Successful in 29s
ci / db / db (push) Failing after 12s
The library page compares listen-through, early-skip and like rates per
impression model version, so v1 and the trained ranker can be compared online.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 22:57:11 +03:00
mrleo1nidandClaude Opus 5.5 fbbe5d7528 feat(search): personalize suggestions by listening history (M6)
ci / api / api (push) Successful in 18s
ci / db / db (push) Successful in 30s
Suggest fetches three times as many candidates and reranks them in the API:
what the user plays, finishes and likes moves up a few positions and what they
dislike moves down, with Meilisearch relevance still the base order.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 22:55:07 +03:00
mrleo1nidandClaude Opus 5.5 e250837de9 fix(search): reindex on a fresh Meilisearch
ci / db / db (push) Successful in 34s
release / release (версия и заметки) (push) Successful in 5s
ci / api / api (push) Successful in 19s
release / android (APK в релиз) (push) Successful in 3m17s
Deleting a missing <uid>_next index is accepted with 202 and fails inside the
task with index_not_found. That error was not treated as not-found, so the first
reindex aborted and search stayed unavailable until indexes existed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 22:44:59 +03:00
mrleo1nidandClaude Opus 5.5 58bd14ef38 feat(recommender): LightGBM lambdarank ranker with impression features (M6)
ci / db / db (push) Failing after 1m15s
ci / api / api (push) Successful in 17s
ci / recommender / recommender (push) Successful in 32s
ci / docker / docker (образы собираются — их сервер собирает сам при обновлении) (push) Successful in 1m41s
The wave logs each served candidate's feature vector to rec_impression_features.
Nightly training joins them with impression outcomes, validates on the last week
and activates the model only when it beats the v1 order and the active model.
The service loads the active ranker and uses its order in place of the v1 score.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 22:41:26 +03:00
mrleo1nidandClaude Opus 5.5 fb07f962eb feat: taste onboarding for new listeners (M6)
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 1m2s
ci / db / db (push) Failing after 1m29s
ci / ts / ts (packages + web) (push) Successful in 49s
ci / api / api (push) Successful in 1m25s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 48s
ci / recommender / recommender (push) Successful in 21s
- GET /onboarding offers the library's top artists (with album covers)
  and genres and says whether to show it (not done and < 10 plays);
  PUT /me/onboarding stores the choice or a skip (migration 00009).
- The recommender seeds the taste from it while the user has fewer than
  30 scored tracks: chosen artists' tracks +1, chosen genres +0.5.
- Web: onboarding replaces the wave on the home page until done or
  skipped; artist grid with covers, genre chips, at least 3 artists.
- e2e journey and the recommender DB test cover it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 22:35:43 +03:00
mrleo1nidandClaude Opus 5.5 85fceaa686 fix(storage-agent): write nothing but the music files to the mounted storage
ci / api / api (push) Successful in 50s
ci / docker / docker (образы собираются — их сервер собирает сам при обновлении) (push) Failing after 10s
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Failing after 31m43s
ci / db / db (push) Failing after 19s
- Imported tracks are created directly under their final name with
  O_EXCL (never overwriting; " (2)" on conflict), fsync'ed and checked
  against SHA-256 — no hidden .pipu-tmp file next to them any more.
- On any failure the agent removes its own partial file and the empty
  directories it created for it.
- The write check uses access(W_OK) and the read-only mount flag instead
  of creating and deleting .pipu-write-test in the storage root.
- Tests assert the storage tree holds only audio files after successful
  and failed writes and after the write check.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 19:22:49 +03:00
mrleo1nidandClaude Opus 5.5 4cca427b82 test(e2e): log service warnings and give search indexing more time
ci / api / api (push) Successful in 33s
ci / db / db (push) Failing after 1m34s
release / release (версия и заметки) (push) Successful in 11s
release / android (APK в релиз) (push) Canceled after 29m27s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 18:29:17 +03:00
mrleo1nidandClaude Opus 5.5 2285107d04 test: end-to-end user journey in CI; fix ALS vectors for small libraries
ci / api / api (push) Successful in 30s
ci / recommender / recommender (push) Successful in 30s
ci / db / db (push) Failing after 1m8s
- newApp (extracted from main) is the exact production wiring; the
  `ci / db` job now also runs it on PostgreSQL, Redis and Meilisearch
  and walks the main user flows over HTTP: first admin sign-in and
  credential change, local source and scan, search, album / artist /
  top tracks, signed stream, events and history, likes, fallback wave,
  playback state, upload queue up to review.
- ALS on a small library produced fewer than 64 factors and the insert
  into vector(64) failed; vectors are now zero-padded to the column size.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 18:26:36 +03:00
mrleo1nidandClaude Opus 5.5 f5c276092f ci: integration tests on PostgreSQL + pgvector for migrations and store SQL
ci / db / db (push) Failing after 53s
ci / recommender / recommender (push) Successful in 36s
ci / api / api (push) Successful in 34s
ci / docker / docker (образы собираются — их сервер собирает сам при обновлении) (push) Successful in 1m9s
New `ci / db` job with a pgvector service: Go applies all migrations,
rolls them back to zero and applies them again, then runs the scanner,
fingerprint, write-template, top-tracks and upload-queue store queries;
Python runs the analysis worker, catalog / profile loading and a full
ALS training run against the same schema. Until now this SQL ran
nowhere before reaching a real server.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 18:18:44 +03:00
mrleo1nidandClaude Opus 5.5 1c4e209e86 feat(web): align the web UI with the design prototype
ci / api / api (push) Successful in 43s
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 52s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 1m36s
ci / ts / ts (packages + web) (push) Successful in 1m39s
- Ambient background in the app shell: three drifting blurred blobs,
  the third one tinted with the current track's cover; can be turned off
  in settings and stays still with reduced motion.
- Island player variant (floating glass panel) next to the bar, chosen
  in settings; player source line ("Album · …", "My Wave"), reactions
  around the transport controls, queue drawer.
- Sidebar: like / dislike counters, pending uploads badge on Queue,
  admin Settings page holding the upload queue limits.
- Collection: header tile with kicker, underline tabs, time / number
  lead column with an equalizer for the current track, album column,
  dimmed dislikes with undo, Play all / Shuffle pills.
- Album: cover-tinted header and Play / Shuffle; artist: artist wave,
  Play and popular tracks (new GET /artists/{id}/top-tracks).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 18:13:24 +03:00
mrleo1nidandClaude Opus 5.5 ca106e8eb1 feat(api): upload queue with quota, tus intake, processing and import (M5.5)
ci / api / api (push) Successful in 46s
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 1m3s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 1m22s
ci / ts / ts (packages + web) (push) Successful in 1m38s
- Migration 00008: upload_batches, upload_files, upload_quota.
- Quota reserved under a row lock when a batch is created; every status
  change goes through one transition that frees space in the same
  transaction. SHA-256 duplicates are rejected before any upload.
- tus 1.0.0 core (HEAD/PATCH) for the author only; offset is the real
  staging file size, data beyond the declared size is refused (ADR-015).
- Processing worker: ffprobe checks, SHA-256 verification, exact and
  Chromaprint duplicates against library and queue, metadata from tags
  or the dropped folder path, possible_duplicate on metadata match, and
  the target path from write_template reusing existing folder spelling.
- Import worker: edited tags written with ffmpeg stream copy, file
  written by storage-agent, point scan of the new file, imported status;
  idempotent and resumed after restart. Stale uploads expire after 24 h,
  orphan staging files are removed at startup.
- API: quota, precheck, batches, my uploads, withdraw, admin list,
  metadata edit, bulk review, retry and queue settings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 17:23:05 +03:00
mrleo1nidandClaude Opus 5.5 f66be2ba0a feat: storage writes via storage-agent, accepts_uploads and write_template (M5.5)
ci / api / api (push) Successful in 33s
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 43s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 1m11s
ci / ts / ts (packages + web) (push) Successful in 1m10s
ci / docker / docker (образы собираются — их сервер собирает сам при обновлении) (push) Successful in 51s
- storage-agent: POST /internal/files copies a staged file into a source
  (temp file, fsync, SHA-256 check, hard link so nothing is overwritten,
  " (2)" suffix on conflict, owner PIPU_FILE_UID/GID) and
  POST /internal/files/test. NFS sources that accept uploads are mounted
  rw in an agent-only /library-rw and bind-mounted read-only for others;
  local sources are written through /library-host-rw.
- meta: write templates ({track:02}, {disc:1-}, [optional]) with name
  sanitising and length limits, suggestion from the source's regexes
  and a reversibility check against 50 catalogue tracks.
- API and admin UI: accepts_uploads / write_template on sources,
  write-template suggest and check endpoints, write test result.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 13:46:56 +03:00
mrleo1nidandClaude Opus 5.5 9b50038bb4 feat(api): SHA-256 and Chromaprint fingerprints for the library (M5.5)
ci / api / api (push) Successful in 27s
ci / docker / docker (образы собираются — их сервер собирает сам при обновлении) (push) Successful in 58s
A background Fingerprinter runs after every scan and at startup, one file
at a time, filling media_files.sha256 and track_fingerprints (fpcalc raw,
first 120 s). Without fpcalc only SHA-256 is computed and files stay
queued for Chromaprint. Changing a file's content resets its prints.
Similarity() compares raw prints with a ±80-point alignment search; it is
the basis for duplicate detection in the upload queue.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 13:37:05 +03:00
mrleo1nidandClaude Opus 5.5 6a67c27f50 feat(api): Android self-update endpoints proxied from Gitea releases (M5)
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 43s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 1m12s
ci / api / api (push) Successful in 35s
ci / ts / ts (packages + web) (push) Successful in 1m20s
GET /api/v1/app/android/latest reads the latest Gitea release with the
server token (cached for 10 minutes); the download endpoint caches the
APK under /updates, verifies its SHA-256 and serves it with Range.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 12:52:41 +03:00
mrleo1nidandClaude Opus 5.5 eaedd1fc62 feat(web): My Wave button, mood picker and endless wave playback (M4)
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 41s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 1m13s
ci / ts / ts (packages + web) (push) Successful in 1m14s
ci / api / api (push) Successful in 36s
- WaveButton: TypeScript port of the prototype's canvas animation
  (orb and liquid), breathing at rest, pulsing to the track's BPM and
  energy in the cover palette, following the cursor, pressing with a
  spring; reduced motion and hidden tabs slow or stop it
- home: the wave in the centre with activity and mood chips; changing
  them while playing rebuilds the upcoming tracks without stopping
- player wave mode: asks for the next batch when two tracks are left,
  sends complete / skip / early skip / like / dislike as feedback and
  refreshes the queue right after a reaction or an early skip
- tracks expose bpm, energy and loudness once analysed; library page
  shows analysis progress and 7-day wave metrics; wave style setting

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 12:29:38 +03:00
mrleo1nidandClaude Opus 5.5 3908814cda feat(api): My Wave sessions with a Go fallback and wave metrics (M4)
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 40s
ci / mobile / mobile (Metro-бандл Android) (push) Failing after 9s
ci / api / api (push) Successful in 35s
ci / ts / ts (packages + web) (push) Successful in 51s
- /wave/options, POST /wave/sessions (activity, mood, seed),
  /next with reactions, PATCH to switch the context on the fly, DELETE
- the recommender is called with an 800 ms budget; on failure the api
  serves a fallback wave of likes, favourite artists and random
  available tracks with the same dislike / recent / served filters
- every served track is an impression with its source and model; wave
  events (context_type wave) set its outcome, reactions win
- library status shows the analysis queue and 7-day wave metrics:
  listen-through, early skip and like rate

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 12:23:17 +03:00
mrleo1nidandClaude Opus 5.5 ef29b85320 feat(rec): audio analyzer and My Wave v1 engine (M4)
ci / api / api (push) Successful in 27s
ci / recommender / recommender (push) Successful in 1m0s
ci / docker / docker (образы собираются — их сервер собирает сам при обновлении) (push) Successful in 1m26s
- migration 00006: track features and embeddings (pgvector, HNSW),
  analysis queue filled by a trigger on new tracks, wave sessions and
  impressions, recommender profiles, model versions
- analyzer basic-1 (ADR-014): ffmpeg decode, three 30 s excerpts,
  256-d timbre/rhythm embedding, tempo by onset autocorrelation, key,
  loudness, energy, danceability, acousticness, intro length; worker
  with SKIP LOCKED claims, retries and re-analysis on version change
- wave engine v1: candidates (session/taste similarity, fresh,
  liked artists, rediscover, popular, exploration, seed), filters
  (dislikes, played in 3 h, already served), heuristic score, activity
  and mood fit from contexts.yaml with relaxing thresholds, MMR with
  one track per artist, exploration share, session vector updated by
  reactions and a change of course after three skips
- POST /internal/wave/batch with the catalog index cached in memory
  and the session kept in Redis

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 12:20:55 +03:00
mrleo1nidandClaude Opus 5.5 c05e523cbe feat(api): streaming, transcoding, events, history and likes (M3)
ci / api / api (push) Successful in 29s
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 35s
ci / ts / ts (packages + web) (push) Failing after 49s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 1m1s
- tracks carry a stream_url signed for the user for 12 h and the
  user's reaction; /stream checks the signature, expiry and that the
  user is not blocked, then hands the file to nginx by X-Accel-Redirect
  (or serves it with Range support when X_ACCEL=false)
- original by default; formats browsers can't play and explicit
  high/medium/low go through Opus transcoding, never upwards: a lossy
  source under the target bitrate stays original; transcodes are
  cached per file and size, deduplicated in flight, limited by
  TRANSCODE_CONCURRENCY and trimmed hourly by LRU to the size limit
- /events takes up to 500 events with client UUIDs, drops repeats and
  events older than 30 days, counts plays (≥ half or 4 min, or
  finished), early skips and history entries (≥ 30 s)
- history with clearing, likes and dislikes with cursors and totals,
  track reactions, playback state with last-write-wins, tracks batch

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 07:25:15 +03:00
mrleo1nidandClaude Opus 5.5 8b76aa3964 feat: album covers with palettes and signed links (M2)
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 55s
ci / ts / ts (packages + web) (push) Successful in 1m0s
ci / docker / docker (образы собираются — их сервер собирает сам при обновлении) (push) Successful in 34s
ci / api / api (push) Successful in 26s
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 34s
- covers are taken from the embedded picture of the first track or a
  cover/folder/front image in the album folder, converted by ffmpeg to
  WebP 64/300/1200 and named by content hash, so albums sharing a cover
  share the files; a full scan looks again for albums without one
- a 3-colour palette (dominant, second, most saturated accent) from a
  deterministic k-means feeds placeholders, page tints and the wave
- /covers/{id} is public but HMAC-signed and immutable; nginx serves
  the file via X-Accel-Redirect, X_ACCEL=false serves it from api
- the web client picks the size by display size and pixel density
- install.sh and update.sh generate STREAM_SIGNING_KEY

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 07:06:15 +03:00
mrleo1nidandClaude Opus 5.5 c1160cccd3 feat(api): search with Meilisearch (M2)
ci / api / api (push) Successful in 27s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 1m1s
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 34s
ci / ts / ts (packages + web) (push) Successful in 1m1s
ci / docker / docker (образы собираются — их сервер собирает сам при обновлении) (push) Successful in 25s
- artists, albums and tracks indexes are rebuilt from PostgreSQL next
  to the live ones and swapped in, so search keeps working; a rebuild
  runs on start, after every scan and when a source is removed
- transliteration fields (Кино ↔ kino) and a retry in the other
  keyboard layout (ghbdtn → привет) reported as corrected_query
- /search/suggest (3 artists, 3 albums, 5 tracks), /search by type
  with cursor pages, /admin/search/reindex; Meilisearch is part of
  /readyz
- found ids are hydrated from the catalog, so a lagging index never
  returns deleted entries

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 06:51:50 +03:00
mrleo1nidandClaude Opus 5.5 c583991d14 feat(api): library sources, scanner and catalog (M2)
ci / api / api (push) Successful in 26s
ci / mobile / mobile (Metro-бандл Android) (push) Failing after 22s
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 37s
ci / ts / ts (packages + web) (push) Successful in 51s
ci / docker / docker (образы собираются — их сервер собирает сам при обновлении) (push) Successful in 53s
- schema for sources, path patterns, media files, artists, albums,
  tracks, genres (migration 00003)
- path patterns (RE2 with named groups, skip), merge with tags by
  source priority, fallbacks from the file name and folder, name
  normalization and multi-artist split
- scanner: walk, skip unchanged files by size and mtime, ffprobe in 8
  workers with a single catalog writer, renames by content hash keep
  the track, duplicates fold into one track with the best file as
  primary, missing files are kept 30 days
- storage-agent mounts NFS with validated server, export and an option
  whitelist, no shell; test mount counts audio files
- admin API: sources CRUD, connection test, patterns with preview,
  scan, library status, file errors, host folder picker; scheduled
  scans and health checks; mounts are restored on start
- catalog API: artist, artist albums, album with tracks, track
- api image is alpine with ffmpeg for ffprobe

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 06:48:37 +03:00
mrleo1nidandClaude Opus 5.5 f075863f15 feat(api): user management and audit log for admins (M1)
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 47s
ci / ts / ts (packages + web) (push) Successful in 38s
ci / api / api (push) Successful in 18s
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 35s
- /admin/users: list with search, role filter and cursor pages; create
  with a one-time temporary password; rename, change role, block;
  reset password; delete
- every change is checked against the role policy, a forbidden field
  rejects the whole patch; the last active admin is locked in the
  transaction and cannot be demoted, blocked or deleted
- role change and blocking revoke access tokens at once; blocking and
  password reset also end all sessions
- /admin/audit: newest-first journal with actor names
- path and query parameter errors are problem+json now

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 06:18:02 +03:00
mrleo1nidandClaude Opus 5.5 ed4a492dba feat(api): sign-in, refresh rotation and bootstrap admin (M1)
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 1m15s
ci / api / api (push) Successful in 1m31s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 47s
ci / ts / ts (packages + web) (push) Successful in 39s
ci / docker / docker (образы собираются — их сервер собирает сам при обновлении) (push) Successful in 26s
- users, refresh_tokens and audit_log schema (migration 00002)
- argon2id passwords with rehash on login, password policy, lockout
  with exponential back-off and a per-IP rate limit in Redis
- Ed25519 JWT access tokens with kid-based key rotation; rotating
  refresh tokens with reuse detection (cookie for web, body for Android)
- bootstrap admin on first start; must_change_credentials and
  must_change_password gate every other endpoint
- /auth/login, /auth/refresh, /auth/logout(-all), /me, /me/password,
  /me/bootstrap-credentials, /me/sessions; server-info reports
  bootstrap_required
- role policy from docs/auth-and-roles.md with table tests
- problem+json errors with field errors; ru/en translations of the codes
- install.sh and update.sh generate JWT_PRIVATE_KEY

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 06:12:25 +03:00
mrleo1nidandClaude Opus 5.5 4fa1add80d fix: address project review findings
ci / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 48s
ci / api (push) Successful in 51s
ci / recommender (push) Canceled after 2s
ci / ts (packages + web) (push) Canceled after 0s
ci / docker (образы собираются — их сервер собирает сам при обновлении) (apps/web/Dockerfile, web, ) (push) Canceled after 0s
ci / docker (образы собираются — их сервер собирает сам при обновлении) (services/api/Dockerfile, api, api) (push) Canceled after 0s
ci / docker (образы собираются — их сервер собирает сам при обновлении) (services/api/Dockerfile, storage-agent, storage-agent) (push) Canceled after 0s
ci / docker (образы собираются — их сервер собирает сам при обновлении) (services/recommender/Dockerfile, recommender, ) (push) Canceled after 0s
ci / mobile (Metro-бандл Android) (push) Canceled after 0s
Security and robustness:
- storage-agent rejects an empty INTERNAL_TOKEN (config notEmpty + middleware)
- api takes the client IP only from X-Real-IP (chi RealIP trusted True-Client-IP)
- /readyz reports ok/fail and logs the cause; checks run in parallel with a timeout
- storage-agent shuts down gracefully; shared httpx.NewServer/Serve with IdleTimeout
- goose migrations take a Postgres advisory lock
- recommender readyz: per-check timeout, redis socket timeouts

Clients:
- Android release allows cleartext HTTP for LAN servers (expo-build-properties)
- checkServer times out after 10s; connect button is disabled while checking

Deploy and CI:
- install.sh hands cache/uploads/updates/models to the non-root service users
- update.sh refuses downgrades unless PIPU_ALLOW_DOWNGRADE=1
- recommender/analyzer get only the env they need; required secrets fail fast
- nginx security headers and CSP; strip True-Client-IP
- dev compose binds ports to 127.0.0.1; postgres/redis images pinned
- version.mjs fails on a malformed explicit tag instead of releasing 0.0.0-dev
- contract check catches untracked generated files
- final releases fail without a release keystore; changelog skips rc tags

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 00:46:46 +03:00
mrleo1nidandClaude Opus 5.5 ebf296542c feat: M0 monorepo skeleton with services, clients, deploy and CI
ci / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 3m55s
ci / api (push) Successful in 51s
ci / mobile (Metro-бандл Android) (push) Canceled after 0s
release / images (services/api/Dockerfile, storage-agent, storage-agent) (push) Canceled after 0s
release / images (services/recommender/Dockerfile, recommender, ) (push) Canceled after 0s
ci / recommender (push) Successful in 1m11s
ci / ts (packages + web) (push) Canceled after 3s
release / verify (push) Successful in 1m16s
release / images (apps/web/Dockerfile, web, ) (push) Failing after 2m40s
release / android + release (push) Skipped
release / images (services/api/Dockerfile, api, api) (push) Canceled after 1s
- api/openapi.yaml: contract with /healthz, /readyz, /api/v1/server-info
- services/api (Go): api with oapi-codegen strict server, goose migrations,
  readiness checks for Postgres/Redis; storage-agent stub with internal token
- services/recommender (Python, uv): FastAPI health/readiness/internal status,
  `pipu-rec serve` and `pipu-rec analyze` entrypoints
- packages: generated api-client, core (server URL + server check),
  i18n (ru/en + dictionary check), design-tokens (CSS vars + TS from tokens.json)
- apps/web: Vite + React shell showing server status, language switch
- apps/mobile: Expo (SDK 53) Android app with server connect screen
- deploy: Dockerfiles, compose (prod + dev), nginx template, install/update scripts
- .gitea/workflows: ci.yml and tag-based release.yml (images, signed APK, release)
- scripts/version.mjs: version and Android versionCode from git tag
- tests/fixtures/music/generate.sh: synthetic test library via ffmpeg
- docs: versionCode formula, OpenAPI 3.0.3, dev instructions in README

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 23:55:11 +03:00