You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Gunicorn worker model now matches in-memory job tracking. Docker runtime now
uses --workers 1 --threads 4 to prevent cross-worker job/batch state
divergence for playlist progress polling.
Playlist confirm flow no longer depends on cookie session size. Playlist
entries are persisted in playlist_batches.entries_json (DB) instead of the
Flask session cookie, avoiding cookie overflows for large playlists.
Open redirect hardening for OAuth login callbacks.next redirect targets
are now validated to allow only safe local relative paths.
Client IP trust model tightened. Removed raw X-Forwarded-For trust in
auth/fingerprint helpers; only CF-Connecting-IP (when present) or remote_addr is used.
Notification email HTML escaping. User/content-derived values are escaped
before HTML rendering to reduce injection risk in admin notification emails.
Playlist ZIP safety and resource guardrails. ZIP entry names are sanitized
to avoid path traversal entries and playlist ZIP generation is capped via PLAYLIST_ZIP_MAX_TRACKS (default: 50) to bound RAM usage.
Playlist tracks N+1 query reductions. Added eager loading (joinedload) on
playlist/shared track list endpoints and preloaded member counts in the
playlists list API.
Added
SQLite runtime pragmas for resilience. On connect, SQLite now enables journal_mode=WAL and busy_timeout=5000 to reduce lock contention issues.
Changed
Default app version bumped to 5.1.0 in runtime/build config.
Artwork and lyrics admin endpoints now work for remote admin users.
Four admin-only endpoints (DELETE /player/api/artwork/<job_id>, PATCH /player/api/artwork/<job_id>, DELETE /player/api/lyrics/<job_id>/cache, PATCH /player/api/lyrics/<job_id>/cache) incorrectly rejected all remote
requests — they checked get_current_user_email() is not None which always
returns an email for authenticated remote users. Replaced with a proper _require_admin() helper that allows local requests and remote users with is_admin=True.
Browser audio cache (Service Worker + Cache API). A new Service Worker
(static/sw.js) intercepts /player/stream/* requests with a cache-first
strategy, storing full MP3 responses in the browser. Caching is passive
(cache-on-play) — tracks are cached automatically on first playback.
CacheManager module (static/cache-manager.js). Manages which tracks
should remain cached based on a configurable eviction policy:
Last 10 played tracks
Last 10 downloaded tracks
All favorites
250 MB max total cache size
Tracks outside the "keep set" are evicted automatically via postMessage
to the Service Worker.
Metadata cache (localStorage, 24h TTL). Track titles, artwork URLs,
and favorite state are cached in localStorage for instant offline-first
UI rendering. The player fragment now renders from cache immediately, then
refreshes from the API in the background.
HTTP cache headers on stream responses.Cache-Control: public, max-age=31536000, immutable and Accept-Ranges: bytes are now set on
all /player/stream/* responses, enabling the browser's native HTTP cache
as a backup layer.
Changed
player.js now notifies CacheManager on every playTrack() and loadTracks() call, keeping the recently-played/downloaded lists and
metadata cache in sync.
player.html fragment uses offline-first rendering: cached tracks are
shown instantly, then replaced with fresh API data when available. Favorite
toggles also update the metadata cache.
docker-compose.ymlAPP_VERSION default bumped to 4.13.0.
Local user/password authentication. The email + password login form, the POST /auth/login handler, and the "Crear usuario" form in the admin panel have
been removed. Google OAuth is now the only authentication method. The password_hash column remains in the DB but is no longer written or read. scripts/seed_test_data.py deleted.
Added
Settings / profile page (/settings). New SPA fragment accessible by clicking
the user name in the topbar. Shows profile info (avatar, name, email, provider,
member since, admin badge), read-only feature flags (lyrics, share), and an API
tokens management section.
API tokens. Users can create up to 10 personal API tokens from the settings
page for programmatic access. Tokens use the format yt2_<32 hex chars> — only
the SHA-256 hash is stored in the database. New table api_tokens with automatic
migration for existing databases. Token CRUD endpoints: GET /settings/api/tokens, POST /settings/api/tokens, DELETE /settings/api/tokens/<id>.
Bearer token authentication. API requests can authenticate via Authorization: Bearer yt2_... header. The @user_required decorator now checks
for valid API tokens before falling back to session auth. get_current_user_email()
also returns the token owner's email for token-authenticated requests.
Admin / local rate limit exemption. Added @limiter.request_filter that
exempts local (RFC-1918/loopback) IPs and admin sessions from the download
rate limiter (3/minute; 10/hour).
Changed
Topbar user name link now points to /settings instead of /player.
docker-compose.ymlAPP_VERSION default bumped to 4.12.0.
Drag-and-drop tracks to sidebar playlists broken.effectAllowed was set to 'move' but the sidebar drop handler used dropEffect = 'copy' — incompatible per
the HTML5 DnD spec, causing the browser to silently reject the drop. Changed effectAllowed to 'copyMove' so both reordering (move) and adding to playlists
(copy) work.
Collaborative playlist not syncing for other editors. Playlist data was fetched
once and cached with no refresh mechanism. Added 10-second polling
(_startCollabSync/_stopCollabSync/_pollCollabPlaylist) that re-fetches tracks
when viewing a collaborative playlist. Compares job_id fingerprints to avoid
unnecessary re-renders. Polling starts/stops automatically on view changes and SPA
navigation via _playerFragmentCleanup.
Last admin could be deleted or demoted. Backend now returns 400 if attempting to
delete the last admin or remove their is_admin flag. Frontend shows client-side
guard and server error toast.
Added
Fallback artwork initials in player bar. When no artwork URL or YouTube thumbnail
is available, the player bar shows a coloured circle with initials derived from the
track title (e.g. "Tengo ganas de verte - Valerie Luh" → T·VL). Uses deterministic
HSL hue from title hash (same algorithm as the avatar system). Includes expanded and
mobile styles. Clicking the initials opens the expanded view, same as artwork.
Feature flag confirmation dialog.toggleFeature in the admin users page now shows
a confirm() dialog before toggling any feature ("¿activar/desactivar 'Label' para
email?"). Cancelled toggles revert the checkbox. Server errors also revert and show
a toast.
Changed
Default share mode for new playlist shares changed from 'view' to 'collaborate'.
User avatars. Local users (and any user without a Google profile picture) now
display a coloured initial-letter circle. Shown in the topbar, admin user table,
and collaborative playlist "added by" labels. Google users continue to show their
profile photo. Colour is deterministic per name.
Downloads-by-user analytics chart. New "Top 10 descargas por usuario" horizontal
bar chart on the admin analytics page, showing which users download the most.
Resolves emails to display names.
Usuarios nav link in analytics page. The analytics topbar now links to the
users admin page for easier navigation.
Changed
Lyrics enabled by default. New users (without a UserFeature row) now get lyrics_enabled: true instead of false. Admin can still disable per user.
Admin users API now returns picture and provider fields per user.
Fixed
Local user name validation. Names containing @ are now rejected (backend +
frontend) to prevent confusion with email addresses.
Create-user email placeholder changed from juan@local to juan@example.com.
Collaborative playlists. Share a playlist with a "Colaborativa" toggle — anyone
with the link who clicks "Unirme a esta lista" joins as an editor. Editors can add
tracks, remove tracks, and reorder — same as the owner. Owner retains exclusive
rights: delete playlist, share/revoke.
New table playlist_members tracks playlist membership with roles (owner/editor).
Existing playlist owners are auto-migrated. New endpoint: POST /player/api/shared/<token>/join.
added_by on playlist tracks. Each track shows who added it (small "por [name]"
label in collaborative playlists). New column playlist_tracks.added_by.
Share mode toggle. Share dialog has a toggle between "Solo ver" (view-only, copy-based)
and "Colaborativa" (join-based). New column playlist_shares.mode.
Sidebar badges for collaborative playlists ("colab" tag). Editors cannot drag-delete
playlists they don't own; share button hidden for non-owners.
Player page blank on full browser reload.I18n.init() did not dispatch the i18n:change event, so the player fragment — which waits for that event when window.I18n isn't loaded yet — never called loadAll(). Added the event dispatch
to init() in i18n.js.
Delete user from admin panel. Red ✕ button on each row in /db/users with confirm() dialog. Deleting a user removes their playlists (+ shared links),
play events, and user features. Downloads are kept as anonymous (user_email set
to NULL). New endpoint: DELETE /db/api/users/<email> (@admin_or_local).